Added EndBurner with Punt
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / grain / util / RotatedShapeGrain.java
1 package com.billkuker.rocketry.motorsim.grain.util;\r
2 \r
3 import java.awt.Rectangle;\r
4 import java.awt.Shape;\r
5 import java.awt.geom.AffineTransform;\r
6 import java.awt.geom.Area;\r
7 import java.awt.geom.Ellipse2D;\r
8 import java.awt.geom.GeneralPath;\r
9 import java.awt.geom.PathIterator;\r
10 import java.awt.geom.Rectangle2D;\r
11 \r
12 import javax.measure.quantity.Length;\r
13 import javax.measure.quantity.Volume;\r
14 import javax.measure.unit.SI;\r
15 \r
16 import org.apache.log4j.Logger;\r
17 import org.jscience.physics.amount.Amount;\r
18 \r
19 import com.billkuker.rocketry.motorsim.Grain;\r
20 import com.billkuker.rocketry.motorsim.visual.Editor;\r
21 import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
22 \r
23 public abstract class RotatedShapeGrain implements Grain {\r
24         \r
25         private static Logger log = Logger.getLogger(RotatedShapeGrain.class);\r
26         \r
27         public static RotatedShapeGrain DEFAULT_GRAIN = new RotatedShapeGrain(){\r
28                 {\r
29                         try{\r
30                                 Shape outside = new Rectangle2D.Double(0,0,15,70);\r
31                                 shape.add( outside );\r
32                                 shape.inhibit( outside );\r
33                                 shape.subtract( new Rectangle2D.Double(0,0,5,70));\r
34                                 shape.subtract(new Rectangle2D.Double(0, -10, 15, 10));\r
35                                 shape.subtract(new Rectangle2D.Double(0, 70, 15, 10));\r
36                         } catch ( Exception e ){\r
37                                 throw new Error(e);\r
38                         }\r
39                 }\r
40         };\r
41         \r
42 \r
43         \r
44         public enum Quality {\r
45                 High()\r
46                         {{\r
47                                  surfaceAreaStep = .001;\r
48                                  squareFlatteningError = 0.001;\r
49                                  squareSubdivide = .01;\r
50                                  areaFlatteningError = .001;\r
51                         }},\r
52                 Low()                   {{\r
53                          surfaceAreaStep = .001;\r
54                          squareFlatteningError = .1;\r
55                          squareSubdivide = .1;\r
56                          areaFlatteningError = .1;\r
57                 }};\r
58                 \r
59                 double surfaceAreaStep = .001;\r
60                 double squareFlatteningError = 0.001;\r
61                 double squareSubdivide = .01;\r
62                 double areaFlatteningError = .001;\r
63         }\r
64         \r
65         protected Quality quality = Quality.Low;\r
66         \r
67         protected BurningShape shape = new BurningShape();\r
68         \r
69         protected Amount<Length> web = null;\r
70         \r
71         public Area getCrossSection(Amount<Length> regression) {\r
72                 Area ret = new Area();\r
73                 for( Area a : ShapeUtil.separate(shape.getShape(regression))){\r
74                         Rectangle2D b = a.getBounds2D();\r
75                         Ellipse2D inner = new Ellipse2D.Double(-b.getMinX(), -b.getMinX(), b.getMinX()*2, b.getMinX()*2);\r
76                         Ellipse2D outer = new Ellipse2D.Double(-b.getMaxX(), -b.getMaxX(), b.getMaxX()*2, b.getMaxX()*2);\r
77                         Area aa = new Area(outer);\r
78                         aa.subtract(new Area(inner));\r
79                         ret.add(aa);\r
80                 }\r
81                 return ret;\r
82         }\r
83 \r
84         public Area getSideView(Amount<Length> regression) {\r
85                 Area a = new Area();\r
86                 Area reg = shape.getShape(regression);\r
87                 a.add(reg);\r
88                 a.transform(AffineTransform.getScaleInstance(-1, 1));\r
89                 a.add(reg);\r
90                 return a;\r
91         }\r
92 \r
93         public Amount<javax.measure.quantity.Area> surfaceArea(\r
94                         Amount<Length> regression) {\r
95                 Amount<javax.measure.quantity.Area> zero = Amount.valueOf(0, javax.measure.quantity.Area.UNIT);\r
96                 \r
97                 if (regression.isGreaterThan(webThickness()))\r
98                         return zero;\r
99 \r
100                 java.awt.geom.Area burn = shape.getShape(regression);\r
101                 \r
102                 if (burn.isEmpty())\r
103                         return zero;\r
104                 \r
105                 burn.subtract(shape.getShape(regression.plus(Amount.valueOf(quality.surfaceAreaStep,\r
106                                 SI.MILLIMETER))));\r
107         \r
108                 double sqmm = yRotatedSurfaceArea(burn);\r
109 \r
110                 \r
111                 return Amount.valueOf(sqmm, SI.MILLIMETER.pow(2).asType(javax.measure.quantity.Area.class)).divide(2);\r
112 \r
113         }\r
114 \r
115         public Amount<Volume> volume(Amount<Length> regression) {\r
116                 Shape squared = square(shape.getShape(regression));\r
117                 Amount<javax.measure.quantity.Area> sum = Amount.valueOf(0, SI.SQUARE_METRE);\r
118                 //for( Area a: ShapeUtil.separate(squared) ){\r
119                 //      sum = sum.plus( ShapeUtil.area(a) );\r
120                 //}\r
121                 sum = ShapeUtil.area(squared);\r
122                 Amount<Volume> v = sum.times(Amount.valueOf(Math.PI, SI.MILLIMETER)).to(Volume.UNIT);\r
123                 return v;\r
124         }\r
125 \r
126         public Amount<Length> webThickness() {\r
127                 if (web != null)\r
128                         return web;\r
129 \r
130                 java.awt.geom.Area a = shape.getShape(Amount.valueOf(0, SI.MILLIMETER));\r
131                 Rectangle r = a.getBounds();\r
132                 double max = r.getWidth() < r.getHeight() ? r.getHeight() : r\r
133                                 .getWidth(); // The max size\r
134                 double min = 0;\r
135                 double guess;\r
136                 while (true) {\r
137                         guess = min + (max - min) / 2; // Guess halfway through\r
138                         log.debug("Min: " + min + " Guess: " + guess + " Max: " + max);\r
139                         a = shape.getShape(Amount.valueOf(guess, SI.MILLIMETER));\r
140                         if (a.isEmpty()) {\r
141                                 // guess is too big\r
142                                 max = guess;\r
143                         } else {\r
144                                 // min is too big\r
145                                 min = guess;\r
146                         }\r
147                         if ((max - min) < .01)\r
148                                 break;\r
149                 }\r
150                 web = Amount.valueOf(guess, SI.MILLIMETER);\r
151 \r
152                 return web;\r
153 \r
154         }\r
155 \r
156         private Shape square(Shape a) {\r
157                 PathIterator i = a.getPathIterator(new AffineTransform(), quality.squareFlatteningError);\r
158                 GeneralPath cur = new GeneralPath();\r
159 \r
160                 double last[] = {0,0};\r
161                 while (!i.isDone()) {\r
162                         double coords[] = new double[6];\r
163                         int type = i.currentSegment(coords);\r
164                         switch (type) {\r
165                         case PathIterator.SEG_CLOSE:\r
166                                 cur.closePath();\r
167                                 break;\r
168                         case PathIterator.SEG_MOVETO:\r
169                                 cur.moveTo(Math.pow(coords[0],2), coords[1]);\r
170                                 last[0] = coords[0];\r
171                                 last[1] = coords[1];\r
172                                 break;\r
173                         case PathIterator.SEG_CUBICTO:\r
174                                 throw new Error("Non-flattened geometry!");\r
175                         case PathIterator.SEG_LINETO:\r
176                                 double x = last[0];\r
177                                 double y = last[1];\r
178                                 double len = Math.sqrt(Math.pow(last[0]-coords[0], 2) + Math.pow(last[1]-coords[1], 2));\r
179                                 int steps = (int)(len / quality.squareSubdivide) + 5;\r
180                                 for (int s = 0; s < steps; s++) {\r
181                                         x += (coords[0] - last[0]) / steps;\r
182                                         y += (coords[1] - last[1]) / steps;\r
183                                         cur.lineTo(Math.pow(x, 2), y);\r
184                                 }\r
185                                 last[0] = coords[0];\r
186                                 last[1] = coords[1];\r
187                                 break;\r
188                         case PathIterator.SEG_QUADTO:\r
189                                 throw new Error("Non-flattened geometry!");\r
190 \r
191                         }\r
192                         i.next();\r
193                 }\r
194                 return cur;\r
195         }\r
196         \r
197         private double yRotatedSurfaceArea(Shape a) {\r
198                 PathIterator i = a.getPathIterator(new AffineTransform(), quality.areaFlatteningError);\r
199                 double x = 0, y = 0;\r
200                 double mx = 0, my = 0;\r
201                 double len = 0;\r
202                 while (!i.isDone()) {\r
203                         double coords[] = new double[6];\r
204                         int type = i.currentSegment(coords);\r
205                         if (type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE) {\r
206 \r
207 \r
208                                 double nx = coords[0];\r
209                                 double ny = coords[1];\r
210                                 \r
211                                 if ( type == PathIterator.SEG_CLOSE ){\r
212                                         nx = mx;\r
213                                         ny = my;\r
214                                 }\r
215                                 \r
216                                 double dy = Math.abs(y-ny);\r
217                                 double dx = Math.abs(x-nx);\r
218                                 double xl = x>nx?x:nx;\r
219                                 double xs = x<nx?x:nx;  \r
220                                 \r
221                                 double add = 0;\r
222                                 if ( dx == 0 ){\r
223                                         //Cylender\r
224                                         add = 2 * Math.PI * xl * dy;\r
225                                 } else if ( dy == 0 ){\r
226                                         //disk\r
227                                          add = Math.PI * xl * xl - Math.PI * xs * xs;\r
228                                 }else{\r
229                                         double h = xl/dx * dy;\r
230                                         double s1 = Math.sqrt(xl*xl + h*h);\r
231                                         double s2 = Math.sqrt(xs*xs + (h-dy)*(h-dy));\r
232                                         add = Math.PI * (xl*s1 - xs*s2);\r
233                                 }\r
234                                 \r
235                                 len += add;\r
236 \r
237                                 x = nx;\r
238                                 y = ny;\r
239                         } else if (type == PathIterator.SEG_MOVETO) {\r
240                                 mx = x = coords[0];\r
241                                 my = y = coords[1];\r
242                         } else {\r
243                                 throw new Error("Non-flattened geometry!");\r
244                         }\r
245                         i.next();\r
246                 }\r
247                 return len;\r
248         }\r
249 \r
250         \r
251 \r
252         public static void main(String args[]) throws Exception {\r
253                 RotatedShapeGrain e = DEFAULT_GRAIN;\r
254                 new Editor(e).showAsWindow();\r
255                 new GrainPanel(e).showAsWindow();\r
256         }\r
257         \r
258 }\r