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