// Copyright 2001 freehep package org.freehep.graphics2d; import java.awt.Shape; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.PathIterator; import java.awt.geom.AffineTransform; /** * 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.
* * @author Simon Fischer * @version $Id: SymbolShape.java,v 1.5 2003/04/25 23:12:45 duns Exp $ */ 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 int numberOfPoints; private ArrayPathIterator pathIterator; private Rectangle2D.Double bounds; public SymbolShape() { ensureNumberOfPoints(10); type[0] = PathIterator.SEG_MOVETO; bounds = new Rectangle2D.Double(0,0,0,0); 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 bounds.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 bounds; } public Rectangle getBounds() { Rectangle2D bounds = getBounds2D(); return new Rectangle((int)Math.floor(bounds.getX()), (int)Math.floor(bounds.getY()), (int)Math.ceil(bounds.getWidth()), (int)Math.ceil(bounds.getHeight())); } 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 = numberOfPoints = n; pathIterator.done(); } /** Type must be one of the symbols defined in VectorGraphicsConstants * except TYPE_CIRCLE. * @see org.freehep.graphics2d.VectorGraphicsConstants */ public void create(int type, double x, double y, double size) { switch (type) { case VectorGraphicsConstants.SYMBOL_VLINE: createHLine(x, y, size); break; case VectorGraphicsConstants.SYMBOL_HLINE: createVLine(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; } setBounds(x, y, size); } public 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; } public void createVLine(double x, double y, double size) { ensureNumberOfPoints(2); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createPlus(double x, double y, double size) { ensureNumberOfPoints(4); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createCross(double x, double y, double size) { ensureNumberOfPoints(4); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createStar(double x, double y, double size) { ensureNumberOfPoints(8); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createUpTriangle(double x, double y, double size) { ensureNumberOfPoints(4); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createDownTriangle(double x, double y, double size) { ensureNumberOfPoints(4); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createDiamond(double x, double y, double size) { ensureNumberOfPoints(5); pathIterator.numberOfPoints = numberOfPoints = 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; } public void createBox(double x, double y, double size) { ensureNumberOfPoints(5); pathIterator.numberOfPoints = numberOfPoints = 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; points[3] = y-side; type[2] = PathIterator.SEG_LINETO; points[4] = x+side; points[5] = y+side; type[3] = PathIterator.SEG_LINETO; points[6] = x-side; points[7] = y+side; type[4] = PathIterator.SEG_CLOSE; } private void setBounds(double x, double y, double size) { bounds.x = x - size/2; bounds.y = y - size/2; bounds.width = bounds.height = size; } private void ensureNumberOfPoints(int n) { if ((type == null) || (type.length < n)) { this.points = new double[n*2]; this.type = new int[n]; } } }