Added DEFAULT_GRAIN to grain types. When possible these should all be the same grain.
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / grain / ShapeUtil.java
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
7 \r
8 import javax.measure.quantity.Area;\r
9 import javax.measure.quantity.Length;\r
10 import javax.measure.unit.SI;\r
11 \r
12 import org.jscience.physics.amount.Amount;\r
13 public class ShapeUtil {\r
14         private ShapeUtil(){}\r
15 \r
16         /*\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
24          */\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
29                 \r
30                 \r
31                 double x = 0, y = 0, sx = 0, sy = 0;\r
32                 double nx, ny;\r
33                 double area = 0;\r
34                 while (!i.isDone()) {\r
35                         double coords[] = new double[6];\r
36                         int type = i.currentSegment(coords);\r
37                         switch( type ){\r
38                         case PathIterator.SEG_CLOSE:\r
39                                 //Go back to the start\r
40                                 nx = sx;\r
41                                 ny = sy;\r
42                                 area += x * ny;\r
43                                 area -= y * nx;\r
44                                 break;\r
45                         case PathIterator.SEG_LINETO:\r
46                                 nx = coords[0];\r
47                                 ny = coords[1];\r
48                                 area += x * ny;\r
49                                 area -= y * nx;\r
50         \r
51                                 //Remember the last points\r
52                                 x = nx;\r
53                                 y = ny;\r
54                                 \r
55                                 break;\r
56                         case PathIterator.SEG_MOVETO:\r
57                                 //Remember the starting point\r
58                                 x = sx = coords[0];\r
59                                 y = sy = coords[1];\r
60                                 break;\r
61                         default:\r
62                                 throw new Error("Bad segment type from Flattening Path Iterator");\r
63                         }\r
64                         i.next();\r
65                 }\r
66                 \r
67                 area = area / 2.0; // Result so far is double the signed area\r
68                 \r
69                 if ( area < 0 ){ //Depending on winding it could be negative\r
70                         area = area * -1.0;\r
71                 }\r
72                 \r
73                 \r
74                 return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT);\r
75         }\r
76 \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
81                 double len = 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
91                                 x = nx;\r
92                                 y = ny;\r
93                         } else if (type == PathIterator.SEG_MOVETO) {\r
94                                 // System.out.println("Move");\r
95                                 x = coords[0];\r
96                                 y = coords[1];\r
97                         } else {\r
98                                 // System.err.println("Got " + type);\r
99                         }\r
100                         i.next();\r
101                 }\r
102                 return Amount.valueOf(len, SI.MILLIMETER);\r
103         }\r
104 \r
105         /*\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
109          */\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
114         \r
115                 while (!i.isDone()) {\r
116                         double coords[] = new double[6];\r
117                         int type = i.currentSegment(coords);\r
118                         switch (type) {\r
119                         case PathIterator.SEG_CLOSE:\r
120                                 cur.closePath();\r
121                                 if (cur != null ){\r
122                                         java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
123                                         if ( !a.isEmpty() )\r
124                                                 res.add(area);\r
125                                 }\r
126                                 cur = new GeneralPath(i.getWindingRule());\r
127                                 break;\r
128                         case PathIterator.SEG_MOVETO:\r
129                                 if (cur != null ){\r
130                                         java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
131                                         if ( !a.isEmpty() )\r
132                                                 res.add(area);\r
133                                 }\r
134                                 cur = new GeneralPath(i.getWindingRule());\r
135                                 cur.moveTo(coords[0], coords[1]);\r
136                                 break;\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
140                                 break;\r
141                         case PathIterator.SEG_LINETO:\r
142                                 cur.lineTo(coords[0], coords[1]);\r
143                                 break;\r
144                         case PathIterator.SEG_QUADTO:\r
145                                 cur.quadTo(coords[0], coords[1], coords[2], coords[3]);\r
146                                 break;\r
147         \r
148                         }\r
149                         i.next();\r
150                 }\r
151         \r
152                 return res;\r
153         }\r
154         \r
155         \r
156 }\r