/* * $Id$ * * Copyright (c) 2004-2012 by Rodney Kinney, Brent Easton * * 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. */ package VASSAL.build.module.map.boardPicker.board; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.w3c.dom.Element; import VASSAL.build.AbstractConfigurable; import VASSAL.build.Buildable; import VASSAL.build.module.Map; import VASSAL.build.module.documentation.HelpFile; import VASSAL.build.module.map.boardPicker.Board; import VASSAL.build.module.map.boardPicker.board.mapgrid.GridContainer; import VASSAL.build.module.map.boardPicker.board.mapgrid.GridNumbering; import VASSAL.build.module.map.boardPicker.board.mapgrid.Zone; import VASSAL.build.module.map.boardPicker.board.mapgrid.ZoneHighlight; import VASSAL.build.module.map.boardPicker.board.mapgrid.ZonedGridHighlighter; import VASSAL.configure.Configurer; import VASSAL.i18n.Resources; /** * Map Grid that contains any number of {@link VASSAL.build.module.map.boardPicker.board.mapgrid.Zone}s against a background {@link MapGrid} */ public class ZonedGrid extends AbstractConfigurable implements GeometricGrid, GridContainer { protected List<Zone> zones = new ArrayList<Zone>(); protected MapGrid background; protected GridContainer container; protected ZonedGridHighlighter zoneHighlighters; public String[] getAttributeDescriptions() { return new String[0]; } public Class<?>[] getAttributeTypes() { return new Class<?>[0]; } public String[] getAttributeNames() { return new String[0]; } public String getAttributeValueString(String key) { return null; } public void setAttribute(String key, Object value) { } public Configurer getConfigurer() { return null; } public void addTo(Buildable parent) { container = (GridContainer) parent; container.setGrid(this); } public GridContainer getContainer() { return container; } public Dimension getSize() { return container.getSize(); } public boolean contains(Point p) { return container.contains(p); } public void removeGrid(MapGrid grid) { if (background == grid) { background = null; } } public Board getBoard() { return container != null ? container.getBoard() : null; } public Map getMap() { return getBoard() == null ? null : getBoard().getMap(); } public void setGrid(MapGrid grid) { background = grid; } public Class<?>[] getAllowableConfigureComponents() { return background == null ? new Class<?>[]{Zone.class, HexGrid.class, SquareGrid.class, RegionGrid.class} : new Class<?>[]{Zone.class}; } public static String getConfigureTypeName() { return Resources.getString("Editor.MultiZoneGrid.component_type"); //$NON-NLS-1$ } public HelpFile getHelpFile() { return HelpFile.getReferenceManualPage("ZonedGrid.htm"); //$NON-NLS-1$ } public void removeFrom(Buildable parent) { ((GridContainer) parent).removeGrid(this); } /* * Zones that do not use the background grid must clip out the portion of * the background grid that they cover. Cache as much of the work as * possible to prevent bogging down with large numbers of zones. */ protected Area scaledZones = null; protected Area translatedZones = null; protected AffineTransform scaleTransform; protected AffineTransform translateTransform; protected double lastScale = 0.0; protected int lastX = -1; protected int lastY = -1; public void draw(Graphics g, Rectangle bounds, Rectangle visibleRect, double scale, boolean reversed) { /* * Skip clipping if there is no background grid, or it isn't visible */ if (background != null && background.isVisible()) { /* * Calculate and cache scaled shape consisting of all zones that do not * use the parent grid. (There may be none!) */ if (lastScale != scale || scaleTransform == null) { scaleTransform = AffineTransform.getScaleInstance(scale, scale); scaledZones = null; for (Zone zone : zones) { if (!zone.isUseParentGrid()) { if (scaledZones == null) { scaledZones = new Area( scaleTransform.createTransformedShape(zone.getShape())); } else { scaledZones.add(new Area( scaleTransform.createTransformedShape(zone.getShape()))); } } } lastScale = scale; translateTransform = null; // Force translatedZones to be regenerated } /* * Translate and cache the combined zone shape */ if (lastX != bounds.x || lastY != bounds.y || translateTransform == null) { translateTransform = AffineTransform.getTranslateInstance(bounds.x, bounds.y); translatedZones = null; if (scaledZones != null) { translatedZones = new Area(translateTransform.createTransformedShape(scaledZones)); } lastX = bounds.x; lastY = bounds.y; } /* * Clip out the area covered by the Zones not using the background grid and draw it. */ Graphics2D g2d = (Graphics2D) g; Shape oldClip = g2d.getClip(); if (translatedZones != null && oldClip != null) { Area clipArea = new Area(oldClip); clipArea.subtract(translatedZones); g2d.setClip(clipArea); } background.draw(g, bounds, visibleRect, scale, reversed); g2d.setClip(oldClip); } /* * Draw each Zone */ for (Zone zone : zones) { zone.draw(g, bounds, visibleRect, scale, reversed); } } public GridNumbering getGridNumbering() { return background != null ? background.getGridNumbering() : null; } public Point getLocation(String location) throws BadCoords { for (Zone zone : zones) { try { Point p = zone.getLocation(location); if (p != null && zone.contains(p)) { return p; } } catch (BadCoords bc) { } } if (background != null) return background.getLocation(location); else throw new BadCoords(); } public boolean isVisible() { return true; } public String locationName(Point p) { String name = null; for (Zone zone : zones) { if (zone.contains(p)) { name = zone.locationName(p); break; } } if (name == null && background != null) { name = background.locationName(p); } return name; } public String localizedLocationName(Point p) { String name = null; for (Zone zone : zones) { if (zone.contains(p)) { name = zone.localizedLocationName(p); break; } } if (name == null && background != null) { name = background.localizedLocationName(p); } return name; } public int range(Point p1, Point p2) { MapGrid grid = background; Zone z1 = findZone(p1); Zone z2 = findZone(p2); if (z1 == z2 && z1 != null && z1.getGrid() != null) { grid = z1.getGrid(); } return grid != null ? grid.range(p1, p2) : (int)Math.round(p1.distance(p2)); } public Area getGridShape(Point center, int range) { Area a = null; Zone z = findZone(center); if (z != null && z.getGrid() instanceof GeometricGrid) { a = ((GeometricGrid)z.getGrid()).getGridShape(center,range); } if (a == null && background instanceof GeometricGrid) { a = ((GeometricGrid)background).getGridShape(center,range); } if (a == null) { a = new Area(new Ellipse2D.Double(center.x-range, center.y-range, range * 2, range * 2)); } return a; } public Zone findZone(Point p) { for (Zone zone : zones) { if (zone.contains(p)) { return zone; } } return null; } public Zone findZone(String name) { for (Zone zone : zones) { if (zone.getName().equals(name)) { return zone; } } return null; } public Point snapTo(Point p) { Point snap = null; Zone z = findZone(p); if (z != null) { snap = z.snapTo(p); } if (snap == null) { snap = background != null ? background.snapTo(p) : p; } return snap; } public boolean isLocationRestricted(Point p) { for (Zone zone : zones) { if (zone.contains(p)) { return zone.getGrid() != null && zone.getGrid().isLocationRestricted(p); } } return background != null && background.isLocationRestricted(p); } public void addZone(Zone z) { zones.add(z); } public void removeZone(Zone z) { zones.remove(z); } public Iterator<Zone> getZones() { return zones.iterator(); } public MapGrid getBackgroundGrid() { return background; } public void setBackgroundGrid(MapGrid background) { this.background = background; } public void build(Element e) { super.build(e); if (getComponentsOf(ZonedGridHighlighter.class).isEmpty()) { addChild(new ZonedGridHighlighter()); } } private void addChild(Buildable b) { add(b); b.addTo(this); } public void setZoneHighlighter(ZonedGridHighlighter zh) { zoneHighlighters = zh; } public ZoneHighlight getZoneHighlight(String name) { if (zoneHighlighters != null) { return zoneHighlighters.getZoneHighlightByName(name); } return null; } }