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