package nl.tudelft.simulation.dsol.animation.D2; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.text.NumberFormat; import javax.swing.JPanel; /** * The GridPanel introduces the gridPanel
* (c) copyright 2002-2005 Delft University of Technology , the * Netherlands.
* See for project information www.simulation.tudelft.nl
* License of use: Lesser General Public License (LGPL) , no * warranty. * @version $Revision: 1.2 $ $Date: 2010/08/10 11:37:49 $ * @author Niels Lang , Peter Jacobs */ public class GridPanel extends JPanel { /** */ private static final long serialVersionUID = 1L; /** the UP directions for moving/zooming. */ public static final int UP = 1; /** the DOWN directions for moving/zooming. */ public static final int DOWN = 2; /** the LEFT directions for moving/zooming. */ public static final int LEFT = 3; /** the RIGHT directions for moving/zooming. */ public static final int RIGHT = 4; /** the ZOOM factor. */ public static final double ZOOMFACTOR = 1.2; /** gridColor. */ protected static final Color GRIDCOLOR = Color.BLACK; /** the extent of this panel. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected Rectangle2D extent = null; /** the extent of this panel. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected Rectangle2D homeExtent = null; /** show the grid. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected boolean showGrid = true; /** the gridSize in world Units. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected double gridSize = 100.0; /** the formatter to use. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected NumberFormat formatter = NumberFormat.getInstance(); /** the last computed Dimension. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected Dimension lastDimension = null; /** the last known world coordinate of the mouse. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected Point2D worldCoordinate = new Point2D.Double(); /** whether to show a tooltip with the coordinates or not. */ @SuppressWarnings("checkstyle:visibilitymodifier") protected boolean showToolTip = true; /** * constructs a new GridPanel. * @param extent the extent to show. */ public GridPanel(final Rectangle2D extent) { this(extent, new Dimension(600, 600)); } /** * constructs a new GridPanel. * @param extent the initial extent. * @param size the size of the panel in pixels. */ public GridPanel(final Rectangle2D extent, final Dimension size) { super(); this.extent = extent; this.homeExtent = (Rectangle2D) extent.clone(); this.setBackground(Color.WHITE); this.setPreferredSize(size); this.lastDimension = this.getSize(); } /** * returns the extent of this panel. * @return Rectangle2D */ public final Rectangle2D getExtent() { return this.extent; } /** {@inheritDoc} */ @Override public void paintComponent(final Graphics g) { super.paintComponent(g); if (!this.getSize().equals(this.lastDimension)) { this.lastDimension = this.getSize(); this.extent = Renderable2DInterface.Util.computeVisibleExtent(this.extent, this.getSize()); } if (this.showGrid) { this.drawGrid(g); } } /** * show the grid? * @param bool true/false */ public final synchronized void showGrid(final boolean bool) { this.showGrid = bool; this.repaint(); } /** * Set the world coordinates based on a mouse move. * @param point the x,y world coordinates */ public final synchronized void setWorldCoordinate(final Point2D point) { this.worldCoordinate = point; } /** * @return worldCoordinate */ public final synchronized Point2D getWorldCoordinate() { return this.worldCoordinate; } /** * Display a tooltip with the last known world coordinates of the mouse, in case the tooltip should be displayed. */ public final synchronized void displayWorldCoordinateToolTip() { if (this.showToolTip) { String worldPoint = "(x=" + this.formatter.format(this.worldCoordinate.getX()) + " ; y=" + this.formatter.format(this.worldCoordinate.getY()) + ")"; setToolTipText(worldPoint); } } /** * @return showToolTip */ public final synchronized boolean isShowToolTip() { return this.showToolTip; } /** * @param showToolTip set showToolTip */ public final synchronized void setShowToolTip(final boolean showToolTip) { this.showToolTip = showToolTip; } /** * pans the panel in a specified direction. * @param direction the direction * @param percentage the percentage */ public final synchronized void pan(final int direction, final double percentage) { if (percentage <= 0 || percentage > 1.0) { throw new IllegalArgumentException("percentage<=0 || >1.0"); } switch (direction) { case LEFT: this.extent.setRect(this.extent.getMinX() - percentage * this.extent.getWidth(), this.extent.getMinY(), this.extent.getWidth(), this.extent.getHeight()); break; case RIGHT: this.extent.setRect(this.extent.getMinX() + percentage * this.extent.getWidth(), this.extent.getMinY(), this.extent.getWidth(), this.extent.getHeight()); break; case UP: this.extent.setRect(this.extent.getMinX(), this.extent.getMinY() + percentage * this.extent.getHeight(), this.extent.getWidth(), this.extent.getHeight()); break; case DOWN: this.extent.setRect(this.extent.getMinX(), this.extent.getMinY() - percentage * this.extent.getHeight(), this.extent.getWidth(), this.extent.getHeight()); break; default: throw new IllegalArgumentException("direction unkown"); } this.repaint(); } /** * resets the panel to its original extent. */ public final synchronized void home() { this.extent = Renderable2DInterface.Util.computeVisibleExtent(this.homeExtent, this.getSize()); this.repaint(); } /** * @return Returns the showGrid. */ public final boolean isShowGrid() { return this.showGrid; } /** * @param showGrid The showGrid to set. */ public final void setShowGrid(final boolean showGrid) { this.showGrid = showGrid; } /** * zooms in/out. * @param factor The zoom factor */ public final synchronized void zoom(final double factor) { zoom(factor, (int) (this.getWidth() / 2.0), (int) (this.getHeight() / 2.0)); } /** * zooms in/out. * @param factor The zoom factor * @param mouseX x-position of the mouse around which we zoom * @param mouseY y-position of the mouse around which we zoom */ public final synchronized void zoom(final double factor, final int mouseX, final int mouseY) { Point2D mwc = Renderable2DInterface.Util.getWorldCoordinates(new Point2D.Double(mouseX, mouseY), this.extent, this.getSize()); double minX = mwc.getX() - (mwc.getX() - this.extent.getMinX()) * factor; double minY = mwc.getY() - (mwc.getY() - this.extent.getMinY()) * factor; double w = this.extent.getWidth() * factor; double h = this.extent.getHeight() * factor; this.extent.setRect(minX, minY, w, h); this.repaint(); } /** * Added to make sure the recursive render-call calls THIS render method instead of a potential super-class defined * 'paintComponent' render method. * @param g the graphics object */ @SuppressWarnings("checkstyle:designforextension") protected synchronized void drawGrid(final Graphics g) { // we prepare the graphics object for the grid g.setFont(g.getFont().deriveFont(11.0f)); g.setColor(GRIDCOLOR); double scale = Renderable2DInterface.Util.getScale(this.extent, this.getSize()); int gridSizePixels = (int) Math.round(this.gridSize / scale); if (gridSizePixels < 40) { this.gridSize = 10 * this.gridSize; int maximumNumberOfDigits = (int) Math.max(0, 1 + Math.ceil(Math.log(1 / this.gridSize) / Math.log(10))); this.formatter.setMaximumFractionDigits(maximumNumberOfDigits); this.drawGrid(g); return; } if (gridSizePixels > 10 * 40) { int maximumNumberOfDigits = (int) Math.max(0, 2 + Math.ceil(Math.log(1 / this.gridSize) / Math.log(10))); this.formatter.setMaximumFractionDigits(maximumNumberOfDigits); this.gridSize = this.gridSize / 10; this.drawGrid(g); return; } // Let's draw the vertical lines double mod = this.extent.getMinX() % this.gridSize; int x = (int) -Math.round(mod / scale); while (x < this.getWidth()) { Point2D point = Renderable2DInterface.Util.getWorldCoordinates(new Point2D.Double(x, 0), this.extent, this.getSize()); if (point != null) { String label = this.formatter.format(Math.round(point.getX() / this.gridSize) * this.gridSize); double labelWidth = this.getFontMetrics(this.getFont()).getStringBounds(label, g).getWidth(); if (x > labelWidth + 4) { g.drawLine(x, 15, x, this.getHeight()); g.drawString(label, (int) Math.round(x - 0.5 * labelWidth), 11); } } x = x + gridSizePixels; } // Let's draw the horizontal lines mod = Math.abs(this.extent.getMinY()) % this.gridSize; int y = (int) Math.round(this.getSize().getHeight() - (mod / scale)); while (y > 15) { Point2D point = Renderable2DInterface.Util.getWorldCoordinates(new Point2D.Double(0, y), this.extent, this.getSize()); if (point != null) { String label = this.formatter.format(Math.round(point.getY() / this.gridSize) * this.gridSize); RectangularShape labelBounds = this.getFontMetrics(this.getFont()).getStringBounds(label, g); g.drawLine((int) Math.round(labelBounds.getWidth() + 4), y, this.getWidth(), y); g.drawString(label, 2, (int) Math.round(y + labelBounds.getHeight() * 0.3)); } y = y - gridSizePixels; } } }