1 package com.billkuker.rocketry.motorsim.grain;
\r
2 import java.awt.Shape;
\r
3 import java.awt.geom.AffineTransform;
\r
4 import java.awt.geom.GeneralPath;
\r
5 import java.awt.geom.PathIterator;
\r
6 import java.util.HashSet;
\r
7 import java.util.Set;
\r
9 import javax.measure.quantity.Area;
\r
10 import javax.measure.quantity.Length;
\r
11 import javax.measure.unit.SI;
\r
13 import org.jscience.physics.amount.Amount;
\r
14 public class ShapeUtil {
\r
15 private ShapeUtil(){}
\r
18 * Return the Area of a singular polygon (NO HOLES OR DISJOINT PARTS).
\r
19 * Coordinates assumed to be in MM.
\r
20 * http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html
\r
21 * http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon
\r
22 * http://www.wikihow.com/Calculate-the-Area-of-a-Polygon
\r
23 * According to http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u33.html
\r
24 * this algorithm works OK with holes, and it seems to (see test)
\r
26 public static Amount<Area> area(Shape a) {
\r
27 //if ( !a.isSingular() )
\r
28 //throw new IllegalArgumentException("Can not calculate area of non-singular shape!");
\r
29 PathIterator i = a.getPathIterator(new AffineTransform(), .001);
\r
32 double x = 0, y = 0, sx = 0, sy = 0;
\r
35 while (!i.isDone()) {
\r
36 double coords[] = new double[6];
\r
37 int type = i.currentSegment(coords);
\r
39 case PathIterator.SEG_CLOSE:
\r
40 //Go back to the start
\r
46 case PathIterator.SEG_LINETO:
\r
52 //Remember the last points
\r
57 case PathIterator.SEG_MOVETO:
\r
58 //Remember the starting point
\r
63 throw new Error("Bad segment type from Flattening Path Iterator");
\r
68 area = area / 2.0; // Result so far is double the signed area
\r
70 if ( area < 0 ){ //Depending on winding it could be negative
\r
75 return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT);
\r
78 public static Amount<Length> perimeter(Shape a) {
\r
79 //TODO: I think I need to handle seg_close!!
\r
80 PathIterator i = a.getPathIterator(new AffineTransform(), .001);
\r
81 double x = 0, y = 0;
\r
83 while (!i.isDone()) {
\r
84 double coords[] = new double[6];
\r
85 int type = i.currentSegment(coords);
\r
86 if (type == PathIterator.SEG_LINETO) {
\r
87 // System.out.println("Line");
\r
88 double nx = coords[0];
\r
89 double ny = coords[1];
\r
90 // System.out.println(x+","+y+ " to " + nx+"," + ny);
\r
91 len += Math.sqrt(Math.pow(x - nx, 2) + Math.pow(y - ny, 2));
\r
94 } else if (type == PathIterator.SEG_MOVETO) {
\r
95 // System.out.println("Move");
\r
99 // System.err.println("Got " + type);
\r
103 return Amount.valueOf(len, SI.MILLIMETER);
\r
107 * Separate an area into multiple distinct area.
\r
108 * Area CAN NOT HAVE HOLES. HOLES WILL BE RETURNED AS AREAS,
\r
109 * SO A DONUT WILL TURN INTO TWO CIRCLES.
\r
111 public static Set<java.awt.geom.Area> separate(java.awt.geom.Area a) {
\r
112 Set<java.awt.geom.Area> res = new HashSet<java.awt.geom.Area>();
\r
113 PathIterator i = a.getPathIterator(new AffineTransform());
\r
114 GeneralPath cur = null;
\r
116 while (!i.isDone()) {
\r
117 double coords[] = new double[6];
\r
118 int type = i.currentSegment(coords);
\r
120 case PathIterator.SEG_CLOSE:
\r
123 java.awt.geom.Area area = new java.awt.geom.Area(cur);
\r
124 if ( !a.isEmpty() )
\r
127 cur = new GeneralPath(i.getWindingRule());
\r
129 case PathIterator.SEG_MOVETO:
\r
131 java.awt.geom.Area area = new java.awt.geom.Area(cur);
\r
132 if ( !a.isEmpty() )
\r
135 cur = new GeneralPath(i.getWindingRule());
\r
136 cur.moveTo(coords[0], coords[1]);
\r
138 case PathIterator.SEG_CUBICTO:
\r
139 cur.curveTo(coords[0], coords[1], coords[2], coords[3],
\r
140 coords[4], coords[5]);
\r
142 case PathIterator.SEG_LINETO:
\r
143 cur.lineTo(coords[0], coords[1]);
\r
145 case PathIterator.SEG_QUADTO:
\r
146 cur.quadTo(coords[0], coords[1], coords[2], coords[3]);
\r