// Copyright 2001-2004, FreeHEP.
package 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
* @version $Id: freehep-graphics2d/src/main/java/org/freehep/graphics2d/SymbolShape.java 7aee336a8992 2005/11/25 23:19:05 duns $
*/
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.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;
}
}
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];
}
}
}