/* * $Id$ * * Copyright (c) 2000-2003 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ /* * Created by IntelliJ IDEA. * User: rkinney * Date: Jul 25, 2002 * Time: 11:46:35 PM * To change template for new class use * Code Style | Class Templates options (Tools | IDE Options). */ package VASSAL.build.module.map.boardPicker.board.mapgrid; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import VASSAL.build.Buildable; import VASSAL.build.module.map.boardPicker.board.HexGrid; import VASSAL.counters.Labeler; import VASSAL.tools.ArrayUtils; import VASSAL.tools.ScrollPane; public class HexGridNumbering extends RegularGridNumbering { private static final Logger logger = LoggerFactory.getLogger(HexGridNumbering.class); private HexGrid grid; private boolean stagger = true; public void addTo(Buildable parent) { grid = (HexGrid) parent; grid.setGridNumbering(this); } public static final String STAGGER = "stagger"; public HexGrid getGrid() { return grid; } public String[] getAttributeDescriptions() { return ArrayUtils.append( super.getAttributeDescriptions(), "Odd-numbered rows numbered higher?" ); } public String[] getAttributeNames() { return ArrayUtils.append( super.getAttributeNames(), STAGGER ); } public Class<?>[] getAttributeTypes() { return ArrayUtils.append( super.getAttributeTypes(), Boolean.class ); } public void setAttribute(String key, Object value) { if (STAGGER.equals(key)) { if (value instanceof String) { value = Boolean.valueOf((String) value); } stagger = ((Boolean) value).booleanValue(); } else { super.setAttribute(key, value); } } public String getAttributeValueString(String key) { if (STAGGER.equals(key)) { return String.valueOf(stagger); } else { return super.getAttributeValueString(key); } } /** Draw the numbering if visible */ public void draw(Graphics g, Rectangle bounds, Rectangle visibleRect, double scale, boolean reversed) { if (visible) { forceDraw(g, bounds, visibleRect, scale, reversed); } } /** Draw the numbering, even if not visible */ public void forceDraw(Graphics g, Rectangle bounds, Rectangle visibleRect, double scale, boolean reversed) { int size = (int) (scale * fontSize + 0.5); if (size < 5) { return; } Graphics2D g2d = (Graphics2D) g; AffineTransform oldT = g2d.getTransform(); if (reversed) { AffineTransform t = AffineTransform.getRotateInstance(Math.PI, bounds.x + .5 * bounds.width, bounds.y + .5 * bounds.height); g2d.transform(t); visibleRect = t.createTransformedShape(visibleRect).getBounds(); } if (!bounds.intersects(visibleRect)) { return; } Rectangle region = bounds.intersection(visibleRect); Shape oldClip = g.getClip(); if (oldClip != null) { Area clipArea = new Area(oldClip); clipArea.intersect(new Area(region)); g.setClip(clipArea); } double deltaX = scale * grid.getHexWidth(); double deltaY = scale * grid.getHexSize(); if (grid.isSideways()) { bounds = new Rectangle(bounds.y, bounds.x, bounds.height, bounds.width); region = new Rectangle(region.y, region.x, region.height, region.width); } int minCol = 2 * (int) Math.floor((region.x - bounds.x - scale * grid.getOrigin().x) / (2 * deltaX)); double xmin = bounds.x + scale * grid.getOrigin().x + deltaX * minCol; double xmax = region.x + region.width + deltaX; int minRow = (int) Math.floor((region.y - bounds.y - scale * grid.getOrigin().y) / deltaY); double ymin = bounds.y + scale * grid.getOrigin().y + deltaY * minRow; double ymax = region.y + region.height + deltaY; Font f = new Font("Dialog", Font.PLAIN, size); Point p = new Point(); int alignment = Labeler.TOP; int offset = -(int) Math.round(deltaY / 2); if (grid.isSideways() || rotateTextDegrees != 0) { alignment = Labeler.CENTER; offset = 0; } Point gridp = new Point(); Point centerPoint = null; double radians = 0; if (rotateTextDegrees != 0) { radians = Math.toRadians(rotateTextDegrees); g2d.rotate(radians); } for (double x = xmin; x < xmax; x += 2 * deltaX) { for (double y = ymin; y < ymax; y += deltaY) { p.setLocation((int) Math.round(x), (int) Math.round(y) + offset); gridp = new Point(p.x, p.y - offset); grid.rotateIfSideways(p); // Convert from map co-ordinates to board co-ordinates gridp.translate(-bounds.x, -bounds.y); grid.rotateIfSideways(gridp); gridp.x = (int) Math.round(gridp.x / scale); gridp.y = (int) Math.round(gridp.y / scale); centerPoint = offsetLabelCenter(p, scale); Labeler.drawLabel(g2d, getName(getRow(gridp), getColumn(gridp)), centerPoint.x, centerPoint.y, f, Labeler.CENTER, alignment, color, null, null); p.setLocation((int) Math.round(x + deltaX), (int) Math.round(y + deltaY / 2) + offset); gridp = new Point(p.x, p.y - offset); grid.rotateIfSideways(p); // Convert from map co-ordinates to board co-ordinates gridp.translate(-bounds.x, -bounds.y); grid.rotateIfSideways(gridp); gridp.x = (int) Math.round(gridp.x / scale); gridp.y = (int) Math.round(gridp.y / scale); centerPoint = offsetLabelCenter(p, scale); Labeler.drawLabel(g2d, getName(getRow(gridp), getColumn(gridp)), centerPoint.x, centerPoint.y, f, Labeler.CENTER, alignment, color, null, null); } } if (rotateTextDegrees != 0) { g2d.rotate(-radians); } g.setClip(oldClip); g2d.setTransform(oldT); } @Override public Point getCenterPoint(int col, int row) { if (stagger) { if (grid.isSideways()) { if (col % 2 != 0) { if (hDescending) row++; else row--; } } else { if (col % 2 != 0) { if (vDescending) row++; else row--; } } } if (grid.isSideways()) { if (vDescending) col = getMaxRows() - col; if (hDescending) row = getMaxColumns() - row; } else { if (hDescending) col = getMaxColumns() - col; if (vDescending) row = getMaxRows() - row; } Point p = new Point(); p.x = (int) (col * grid.getHexWidth()); p.x += grid.getOrigin().x; if (col % 2 == 0) p.y = (int) (row * grid.getHexSize()); else p.y = (int) (row * grid.getHexSize() + grid.getHexSize()/2); p.y += grid.getOrigin().y; grid.rotateIfSideways(p); return p; } public int getColumn(Point p) { int x = getRawColumn(p); if (vDescending && grid.isSideways()) { x = (getMaxRows() - x); } if (hDescending && !grid.isSideways()) { x = (getMaxColumns() - x); } return x; } public int getRawColumn(Point p) { p = new Point(p); grid.rotateIfSideways(p); int x = p.x - grid.getOrigin().x; x = (int) Math.floor(x / grid.getHexWidth() + 0.5); return x; } protected JComponent getGridVisualizer() { if (visualizer == null) { visualizer = new JPanel() { private static final long serialVersionUID = 1L; public void paint(Graphics g) { g.clearRect(0, 0, getWidth(), getHeight()); Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight()); grid.forceDraw(g, bounds, bounds, 1.0, false); forceDraw(g, bounds, bounds, 1.0, false); } public Dimension getPreferredSize() { return new Dimension(4 * (int) grid.getHexSize(), 4 * (int) grid.getHexWidth()); } }; } return visualizer; } public int getRow(Point p) { int ny = getRawRow(p); if (vDescending && !grid.isSideways()) { ny = (getMaxRows() - ny); } if (hDescending && grid.isSideways()) { ny = (getMaxColumns() - ny); } if (stagger) { if (grid.isSideways()) { if (getRawColumn(p) % 2 != 0) { if (hDescending) { ny--; } else { ny++; } } } else { if (getRawColumn(p) % 2 != 0) { if (vDescending) { ny--; } else { ny++; } } } } return ny; } protected int getRawRow(Point p) { p = new Point(p); grid.rotateIfSideways(p); Point origin = grid.getOrigin(); double dx = grid.getHexWidth(); double dy = grid.getHexSize(); int nx = (int) Math.round((p.x - origin.x) / dx); int ny; if (nx % 2 == 0) { ny = (int) Math.round((p.y - origin.y) / dy); } else { ny = (int) Math.round((p.y - origin.y - dy / 2) / dy); } return ny; } public void removeFrom(Buildable parent) { grid.setGridNumbering(null); } protected int getMaxRows() { return (int) Math.floor(grid.getContainer().getSize().height / grid.getHexWidth() + 0.5); } protected int getMaxColumns() { return (int) Math.floor(grid.getContainer().getSize().width / grid.getHexSize() + 0.5); } public static void main(String[] args) { class TestPanel extends JPanel { private static final long serialVersionUID = 1L; private boolean reversed; private double scale = 1.0; private HexGrid grid; private HexGridNumbering numbering; private TestPanel() { setLayout(new BorderLayout()); Box b = Box.createHorizontalBox(); final JTextField tf = new JTextField("1.0"); b.add(tf); tf.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { try { scale = Double.parseDouble(tf.getText()); repaint(); } // FIXME: review error message catch (NumberFormatException e1) { logger.error("", e1); } } }); final JCheckBox reverseBox = new JCheckBox("Reversed"); reverseBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { reversed = reverseBox.isSelected(); repaint(); } }); b.add(reverseBox); final JCheckBox sidewaysBox = new JCheckBox("Sideways"); sidewaysBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { grid.setAttribute(HexGrid.SIDEWAYS, sidewaysBox.isSelected() ? Boolean.TRUE : Boolean.FALSE); repaint(); } }); b.add(sidewaysBox); add(BorderLayout.NORTH, b); grid = new HexGrid(); grid.setAttribute(HexGrid.COLOR, Color.black); numbering = new HexGridNumbering(); numbering.setAttribute(HexGridNumbering.COLOR, Color.black); numbering.addTo(grid); JPanel p = new JPanel() { private static final long serialVersionUID = 1L; public void paint(Graphics g) { Rectangle r = new Rectangle(0, 0, getWidth(), getHeight()); g.clearRect(r.x, r.y, r.width, r.height); grid.forceDraw(g, r, getVisibleRect(), scale, reversed); numbering.forceDraw(g, getBounds(), getVisibleRect(), scale, reversed); } }; Dimension d = new Dimension(4000, 4000); p.setPreferredSize(d); add(BorderLayout.CENTER, new ScrollPane(p)); } } JFrame f = new JFrame(); f.add(new TestPanel()); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); screenSize.height -= 100; screenSize.width -= 100; f.setSize(screenSize); f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } }