/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package kcl.waterloo.marker;
import java.awt.FontMetrics;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.Iterator;
import kcl.waterloo.graphics.GJGraphInterface;
import kcl.waterloo.graphics.data.GJXYSeries;
import kcl.waterloo.math.ArrayMath;
import kcl.waterloo.math.ArrayUtils;
/**
*
* @author ML
*/
public class ShapeUtils {
private ShapeUtils() {
}
public static ArrayList getSegments(Shape p) {
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = p.getPathIterator(null); !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
return segments;
}
public static GJXYSeries getXY(Shape s) {
ArrayList x = new ArrayList();
ArrayList y = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = s.getPathIterator(null); !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (Iterator it = segments.iterator(); it.hasNext();) {
GJPathSegmentInfo segment = it.next();
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
// break;
case PathIterator.SEG_LINETO:
x.add(segment.getData()[0]);
y.add(segment.getData()[1]);
break;
case PathIterator.SEG_QUADTO:
x.add(segment.getData()[0]);
x.add(segment.getData()[2]);
y.add(segment.getData()[1]);
y.add(segment.getData()[3]);
break;
case PathIterator.SEG_CUBICTO:
x.add(segment.getData()[0]);
x.add(segment.getData()[2]);
x.add(segment.getData()[4]);
y.add(segment.getData()[1]);
y.add(segment.getData()[3]);
y.add(segment.getData()[5]);
break;
case PathIterator.SEG_CLOSE:
}
}
return new GJXYSeries(x, y);
}
public static double[] getX(Shape s) {
ArrayList arr = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = s.getPathIterator(null); !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (Iterator it = segments.iterator(); it.hasNext();) {
GJPathSegmentInfo segment = it.next();
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
// break;
case PathIterator.SEG_LINETO:
arr.add(segment.getData()[0]);
break;
case PathIterator.SEG_QUADTO:
arr.add(segment.getData()[0]);
arr.add(segment.getData()[2]);
break;
case PathIterator.SEG_CUBICTO:
arr.add(segment.getData()[0]);
arr.add(segment.getData()[2]);
arr.add(segment.getData()[4]);
break;
case PathIterator.SEG_CLOSE:
}
}
return ArrayUtils.asDouble(arr);
}
public static GJXYSeries getFlattenedXY(Shape s) {
return getFlattenedXY(s, Math.min(s.getBounds2D().getWidth() / 1000d, s.getBounds2D().getHeight() / 1000d));
}
public static GJXYSeries getFlattenedXY(Shape s, double flatness) {
FlatteningPathIterator f = new FlatteningPathIterator(s.getPathIterator(null), flatness);
ArrayList x = new ArrayList();
ArrayList y = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = f; !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (GJPathSegmentInfo segment : segments) {
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
x.add(segment.getData()[0]);
y.add(segment.getData()[1]);
break;
case PathIterator.SEG_CLOSE:
break;
default:
}
}
return new GJXYSeries(x, y);
}
public static double[] getFlattenedX(Shape s) {
return getFlattenedX(s, Math.min(s.getBounds2D().getWidth() / 1000d, s.getBounds2D().getHeight() / 1000d));
}
public static double[] getFlattenedX(Shape s, double flatness) {
FlatteningPathIterator f = new FlatteningPathIterator(s.getPathIterator(null), flatness);
ArrayList arr = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = f; !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (GJPathSegmentInfo segment : segments) {
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
arr.add(segment.getData()[0]);
break;
case PathIterator.SEG_CLOSE:
break;
default:
}
}
return ArrayUtils.asDouble(arr);
}
public static double[] getY(Shape s) {
ArrayList arr = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = s.getPathIterator(null); !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (GJPathSegmentInfo segment : segments) {
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
arr.add(segment.getData()[1]);
break;
case PathIterator.SEG_QUADTO:
arr.add(segment.getData()[1]);
arr.add(segment.getData()[3]);
break;
case PathIterator.SEG_CUBICTO:
arr.add(segment.getData()[1]);
arr.add(segment.getData()[3]);
arr.add(segment.getData()[5]);
break;
case PathIterator.SEG_CLOSE:
}
}
return ArrayUtils.asDouble(arr);
}
public static double[] getFlattenedY(Shape s) {
return getFlattenedY(s, Math.min(s.getBounds2D().getWidth() / 1000d, s.getBounds2D().getHeight() / 1000d));
}
public static double[] getFlattenedY(Shape s, double flatness) {
FlatteningPathIterator f = new FlatteningPathIterator(s.getPathIterator(null), flatness);
ArrayList arr = new ArrayList();
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = f; !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
for (GJPathSegmentInfo segment : segments) {
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
arr.add(segment.getData()[1]);
break;
case PathIterator.SEG_CLOSE:
break;
default:
}
}
return ArrayUtils.asDouble(arr);
}
public static Path2D getFromZeroX(GJGraphInterface gr, Shape p) {
return getFromX(gr, p, 0d);
}
public static Path2D getFromX(GJGraphInterface gr, Shape p, double y) {
Path2D path = new Path2D.Double();
double[] d = new double[6];
PathIterator it = p.getPathIterator(null);
it.currentSegment(d);
path.moveTo(d[0], gr.yPositionToPixel(y));
path.append(p, true);
while (!it.isDone()) {
it.currentSegment(d);
it.next();
}
path.lineTo(d[0], gr.yPositionToPixel(y));
return path;
}
public static Path2D getFromZeroY(GJGraphInterface gr, Shape p, double x) {
return getFromY(gr, p, x);
}
public static Path2D getFromY(GJGraphInterface gr, Shape p, double x) {
Path2D path = new Path2D.Double();
double[] d = new double[6];
PathIterator it = p.getPathIterator(null);
it.currentSegment(d);
path.moveTo(gr.xPositionToPixel(x), d[1]);
path.append(p, true);
while (!it.isDone()) {
it.currentSegment(d);
it.next();
}
path.lineTo(gr.xPositionToPixel(x), d[1]);
return path;
}
/**
* Returns a numerical estimate of the area of a shape.
*
* @param s a Shape
* @return the area.
*/
public static double getArea(Shape s) {
Area area = new Area(s);
if (area.isEmpty()) {
return 0d;
}
double[] dX = ArrayMath.diff(ShapeUtils.getFlattenedX(area));
double[] y1 = ShapeUtils.getFlattenedY(area);
double[] y2 = new double[y1.length - 1];
double[] y3 = new double[y1.length - 1];
System.arraycopy(y1, 0, y2, 0, y1.length - 1);
System.arraycopy(y1, 1, y3, 0, y1.length - 1);
ArrayMath.addi(y2, y3);
ArrayMath.divi(y2, 2);
ArrayMath.muli(y2, dX);
return Math.abs(ArrayMath.sum(y2));
}
/**
* Returns a numerical estimate of the centroid (center of mass) of the
* specified Shape.
*
* @param s a Shape object
* @return the estimate as a Point2d.Double instance.
*/
public static Point2D.Double getCentroid(Shape s) {
double[] x = ShapeUtils.getFlattenedX(s);
double[] y = ShapeUtils.getFlattenedY(s);
int n;
if (isClosed(s)) {
n = x.length - 2;
} else {
n = x.length - 1;
}
double sx = ArrayMath.sum(x, 0, n);
double sy = ArrayMath.sum(y, 0, n);
return new Point2D.Double(sx / (double) n, sy / (double) n);
}
/**
* Tests whether shape s2 lays within shape s1.
*
* @param s1 a Shape
* @param s2 a Shape
* @return true if s2 lies within s1.
*/
public static boolean contains(Shape s1, Shape s2) {
Area area1 = new Area(s1);
Area area2 = new Area(s2);
area2.subtract(area1);
return area2.isEmpty();
}
/**
* Tests whether shape s2 lays within shape s1.
*
* @param s1 a Shape
* @param s2 a Shape
* @return true if s2 lies within s1.
*/
public static boolean intersects(Shape s1, Shape s2) {
Area area1 = new Area(s1);
Area area2 = new Area(s2);
area2.intersect(area1);
return !area2.isEmpty();
}
public static double sumdX(Shape s) {
double[] x = getX(s);
return ArrayMath.sum(ArrayMath.diff(x));
}
public static boolean isClosed(Shape s) {
return sumdX(s) == 0;
}
public static String toString(Shape s) {
StringBuilder str = new StringBuilder();
Formatter f = new Formatter(str);
ArrayList segments = new ArrayList();
double[] d = new double[6];
int type;
for (PathIterator it = s.getPathIterator(null); !it.isDone(); it.next()) {
type = it.currentSegment(d);
segments.add(new GJPathSegmentInfo(type, d));
}
f.format("%s [%x]\n", s.getClass().toString(), s.hashCode());
for (GJPathSegmentInfo segment : segments) {
switch (segment.getType()) {
case PathIterator.SEG_MOVETO:
f.format("moveTo:\t%10.4f\t%10.4f\n", segment.getData()[0], segment.getData()[1]);
break;
case PathIterator.SEG_LINETO:
f.format("lineTo:\t%10.4f\t%10.4f\n", segment.getData()[0], segment.getData()[1]);
break;
case PathIterator.SEG_QUADTO:
f.format("quadTo:\t%10.4f\t%10.4f\t%10.4f\t%10.4f\n",
segment.getData()[0], segment.getData()[1],
segment.getData()[2], segment.getData()[3]);
break;
case PathIterator.SEG_CUBICTO:
f.format("curveTo:\t%10.4f\t%10.4f\t%10.4f\t%10.4f\t%10.4f\t%10.4f\n",
segment.getData()[0], segment.getData()[1],
segment.getData()[2], segment.getData()[3],
segment.getData()[4], segment.getData()[5]);
break;
case PathIterator.SEG_CLOSE:
f.format("closePath\n");
}
}
return f.toString();
}
/**
* Given an arc, arc0 and an angle theta, getXYOnArc returns the XY
* coordinates for the intersection of theta on the second arc, arc1.
*
* @param arc0
* @param arc1
* @param theta
* @return
*/
public static double[] getXYOnArc(Ellipse2D arc0, Ellipse2D arc1, double theta) {
double x1 = arc0.getCenterX();
double y1 = arc0.getCenterY();
double x2 = x1 + (Math.cos(theta) * arc1.getHeight());
double y2 = y1 + (Math.sin(theta) * arc1.getWidth());
double horzAngle = (Math.atan2(y2 - y1, x2 - x1) + Math.PI * 2) % (Math.PI * 2);
double xc = arc1.getCenterX();
double yc = arc1.getCenterY();
double r1 = arc1.getWidth() / 2d - arc1.getWidth() / 30d;
double r2 = arc1.getHeight() / 2d - arc1.getHeight() / 30d;
double cot = Math.cos(0);
double sit = Math.sin(0);
double xl = xc + r1 * Math.cos(horzAngle) * cot - r2 * Math.sin(horzAngle) * sit;
double yl = yc + r1 * Math.cos(horzAngle) * sit + r2 * Math.sin(horzAngle) * cot;
return new double[]{xl, yl};
}
public static double[] getStringLocationToArc(double[] xy, double theta, String s, FontMetrics metrics) {
if (theta == 0) {
xy[1] += metrics.getAscent() / 2d;
} else if (Math.abs(theta) == Math.PI / 2d) {
xy[0] -= metrics.stringWidth(s) / 2d;
} else if (Math.abs(theta) == Math.PI) {
xy[1] += metrics.getAscent() / 2d;
} else if (Math.abs(theta) == 1.5d * Math.PI) {
xy[0] -= metrics.stringWidth(s) / 2d;
xy[1] += metrics.getAscent();
} else if (Math.abs(theta) < Math.PI / 2d) {
//Do nothing
} else if (Math.abs(theta) < Math.PI) {
xy[0] -= metrics.stringWidth(s);
} else if (Math.abs(theta) < 1.5 * Math.PI) {
xy[0] -= metrics.stringWidth(s);
xy[1] += metrics.getAscent() / 2d;
} else if (Math.abs(theta) > 1.5 * Math.PI) {
xy[1] += metrics.getAscent() / 2d;
}
return xy;
}
}
Ads help maintain this website.