// Copyright 2001-2004, FreeHEP. package org.xmind.org.freehep.graphics2d; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** * This class can be used to create and render simple shapes quickly and without * memory allocation. A common point array is used for all created shapes. The * factory methods don't return a new shape, but set the object to the selected * shape. Hence, the class is not thread-safe and only one PathIterator can be * used at the same time.<br> * * @author Simon Fischer * @author Jason Wong */ public class SymbolShape implements Shape { private static final double SQRT_2 = Math.sqrt(2.); private static final double SQRT_3 = Math.sqrt(3.); private class ArrayPathIterator implements PathIterator { private int currentPoint = 0; private double[] points; private int[] type; private int numberOfPoints; private ArrayPathIterator(double[] points, int[] type) { this.points = points; this.type = type; } public boolean isDone() { return currentPoint >= numberOfPoints; } public void next() { currentPoint++; } public int currentSegment(double[] coords) { coords[0] = points[2 * currentPoint]; coords[1] = points[2 * currentPoint + 1]; return type[currentPoint]; } public int currentSegment(float[] coords) { coords[0] = (float) points[2 * currentPoint]; coords[1] = (float) points[2 * currentPoint + 1]; return type[currentPoint]; } public int getWindingRule() { return PathIterator.WIND_NON_ZERO; } private void reset() { currentPoint = 0; } private void done() { currentPoint = numberOfPoints; } } private double points[]; private int type[]; private ArrayPathIterator pathIterator; private double x, y; private double size; private int symbol; public SymbolShape() { ensureNumberOfPoints(10); type[0] = PathIterator.SEG_MOVETO; for (int i = 1; i < type.length; i++) { type[i] = PathIterator.SEG_LINETO; } this.pathIterator = new ArrayPathIterator(points, type); } public boolean contains(double x, double y) { return getBounds2D().contains(x, y); } public boolean contains(double x, double y, double w, double h) { return contains(x, y) && contains(x + w, y) && contains(x, y + h) && contains(x + w, y + h); } public boolean contains(Point2D p) { return contains(p.getX(), p.getY()); } public boolean contains(Rectangle2D r) { return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } /** Returns true, if at least one of the points is contained by the shape. */ public boolean intersects(double x, double y, double w, double h) { return contains(x, y) || contains(x + w, y) || contains(x, y + h) || contains(x + w, y + h); } public boolean intersects(Rectangle2D r) { return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } public PathIterator getPathIterator(AffineTransform at, double flatness) { return getPathIterator(at); } public Rectangle2D getBounds2D() { return new Rectangle2D.Double(x - size / 2, y - size / 2, size, size); } public Rectangle getBounds() { return getBounds2D().getBounds(); } public PathIterator getPathIterator(AffineTransform t) { if (t != null) { t.transform(points, 0, pathIterator.points, 0, points.length / 2); } // if (!pathIterator.isDone()) { // System.err.println("SymbolShape: concurrent PathIterator // requested!"); // } pathIterator.reset(); return pathIterator; } // -------------------- factory methods -------------------- private void createNew(int n) { // if (!pathIterator.isDone()) { // System.err.println("SymbolShape: concurrent modification!"); // } ensureNumberOfPoints(n); pathIterator.numberOfPoints = n; pathIterator.done(); } /** * Type must be one of the symbols defined in VectorGraphicsConstants except * TYPE_CIRCLE. * * @see org.xmind.org.freehep.graphics2d.VectorGraphicsConstants */ public void create(int symbol, double x, double y, double size) { this.symbol = symbol; this.x = x; this.y = y; this.size = size; switch (symbol) { case VectorGraphicsConstants.SYMBOL_VLINE: createVLine(x, y, size); break; case VectorGraphicsConstants.SYMBOL_HLINE: createHLine(x, y, size); break; case VectorGraphicsConstants.SYMBOL_PLUS: createPlus(x, y, size); break; case VectorGraphicsConstants.SYMBOL_CROSS: createCross(x, y, size); break; case VectorGraphicsConstants.SYMBOL_STAR: createStar(x, y, size); break; case VectorGraphicsConstants.SYMBOL_BOX: createBox(x, y, size); break; case VectorGraphicsConstants.SYMBOL_UP_TRIANGLE: createUpTriangle(x, y, size); break; case VectorGraphicsConstants.SYMBOL_DN_TRIANGLE: createDownTriangle(x, y, size); break; case VectorGraphicsConstants.SYMBOL_DIAMOND: createDiamond(x, y, size); break; } } @SuppressWarnings("nls") public String toString() { return getClass() + ": " + symbol + " (" + x + ", " + y + ") size: " + size; } private void createHLine(double x, double y, double size) { createNew(2); type[0] = PathIterator.SEG_MOVETO; points[0] = x - size / 2; points[1] = y; type[1] = PathIterator.SEG_LINETO; points[2] = x + size / 2; points[3] = y; } private void createVLine(double x, double y, double size) { createNew(2); type[0] = PathIterator.SEG_MOVETO; points[0] = x; points[1] = y - size / 2; type[1] = PathIterator.SEG_LINETO; points[2] = x; points[3] = y + size / 2; } private void createPlus(double x, double y, double size) { createNew(4); double length = size / 2.; type[0] = PathIterator.SEG_MOVETO; points[0] = x + length; points[1] = y; type[1] = PathIterator.SEG_LINETO; points[2] = x - length; points[3] = y; type[2] = PathIterator.SEG_MOVETO; points[4] = x; points[5] = y + length; type[3] = PathIterator.SEG_LINETO; points[6] = x; points[7] = y - length; } private void createCross(double x, double y, double size) { createNew(4); double side = size / 2. / SQRT_2; type[0] = PathIterator.SEG_MOVETO; points[0] = x - side; points[1] = y - side; type[1] = PathIterator.SEG_LINETO; points[2] = x + side; points[3] = y + side; type[2] = PathIterator.SEG_MOVETO; points[4] = x + side; points[5] = y - side; type[3] = PathIterator.SEG_LINETO; points[6] = x - side; points[7] = y + side; } private void createStar(double x, double y, double size) { createNew(8); double delta = size / 2.; type[0] = PathIterator.SEG_MOVETO; points[0] = x; points[1] = y - delta; type[1] = PathIterator.SEG_LINETO; points[2] = x; points[3] = y + delta; type[2] = PathIterator.SEG_MOVETO; points[4] = x - delta; points[5] = y; type[3] = PathIterator.SEG_LINETO; points[6] = x + delta; points[7] = y; delta = size / 2. / SQRT_2; type[4] = PathIterator.SEG_MOVETO; points[8] = x - delta; points[9] = y - delta; type[5] = PathIterator.SEG_LINETO; points[10] = x + delta; points[11] = y + delta; type[6] = PathIterator.SEG_MOVETO; points[12] = x + delta; points[13] = y - delta; type[7] = PathIterator.SEG_LINETO; points[14] = x - delta; points[15] = y + delta; } private void createUpTriangle(double x, double y, double size) { createNew(4); type[0] = PathIterator.SEG_MOVETO; points[0] = x; points[1] = y - size / SQRT_3; type[1] = PathIterator.SEG_LINETO; points[2] = x - size / 2.; points[3] = y + (-size / SQRT_3 + size * SQRT_3 / 2.); type[2] = PathIterator.SEG_LINETO; points[4] = x + size / 2.; points[5] = y + (-size / SQRT_3 + size * SQRT_3 / 2.); type[3] = PathIterator.SEG_CLOSE; } private void createDownTriangle(double x, double y, double size) { createNew(4); type[0] = PathIterator.SEG_MOVETO; points[0] = x; points[1] = y + size / SQRT_3; type[1] = PathIterator.SEG_LINETO; points[2] = x - size / 2.; points[3] = y - (-size / SQRT_3 + size * SQRT_3 / 2.); type[2] = PathIterator.SEG_LINETO; points[4] = x + size / 2.; points[5] = y - (-size / SQRT_3 + size * SQRT_3 / 2.); type[3] = PathIterator.SEG_CLOSE; } private void createDiamond(double x, double y, double size) { createNew(5); double length = size / 2.; type[0] = PathIterator.SEG_MOVETO; points[0] = x + length; points[1] = y; type[1] = PathIterator.SEG_LINETO; points[2] = x; points[3] = y + length; type[2] = PathIterator.SEG_LINETO; points[4] = x - length; points[5] = y; type[3] = PathIterator.SEG_LINETO; points[6] = x; points[7] = y - length; type[4] = PathIterator.SEG_CLOSE; } private void createBox(double x, double y, double size) { createNew(5); double side = size / SQRT_2 / 2; type[0] = PathIterator.SEG_MOVETO; points[0] = x - side; points[1] = y - side; type[1] = PathIterator.SEG_LINETO; points[2] = x + side + 1; points[3] = y - side; type[2] = PathIterator.SEG_LINETO; points[4] = x + side + 1; points[5] = y + side + 1; type[3] = PathIterator.SEG_LINETO; points[6] = x - side; points[7] = y + side + 1; type[4] = PathIterator.SEG_CLOSE; } private void ensureNumberOfPoints(int n) { if ((type == null) || (type.length < n)) { this.points = new double[n * 2]; this.type = new int[n]; } } }