EndBurner Validation
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / grain / util / ExtrudedShapeGrain.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.Ellipse2D;\r
7 import java.awt.geom.Rectangle2D;\r
8 \r
9 import javax.measure.quantity.Area;\r
10 import javax.measure.quantity.Length;\r
11 import javax.measure.quantity.Volume;\r
12 import javax.measure.unit.SI;\r
13 \r
14 import org.jscience.physics.amount.Amount;\r
15 \r
16 import com.billkuker.rocketry.motorsim.grain.ExtrudedGrain;\r
17 \r
18 public abstract class ExtrudedShapeGrain extends ExtrudedGrain {\r
19         \r
20         public static ExtrudedShapeGrain DEFAULT_GRAIN = new ExtrudedShapeGrain(){\r
21                 {\r
22                         try{\r
23                                 Shape outside = new Ellipse2D.Double(0, 0, 30, 30);\r
24                                 xsection.add(outside);\r
25                                 xsection.inhibit(outside);\r
26                                 xsection.subtract(new Ellipse2D.Double(10,10, 10, 10));\r
27                                 setLength(Amount.valueOf(70, SI.MILLIMETER));\r
28                                 setForeEndInhibited(false);\r
29                                 setAftEndInhibited(false);\r
30                         } catch ( Exception e ){\r
31                                 throw new Error(e);\r
32                         }\r
33                 }\r
34         };\r
35 \r
36         protected BurningShape xsection = new BurningShape();\r
37 \r
38         protected Amount<Length> webThickness;\r
39 \r
40         public Amount<Area> surfaceArea(Amount<Length> regression) {\r
41                 Amount<Area> zero = Amount.valueOf(0, Area.UNIT);\r
42                 \r
43                 if (regression.isGreaterThan(webThickness()))\r
44                         return zero;\r
45                 \r
46                 Amount<Length> rLen = regressedLength(regression);\r
47                 \r
48                 if (rLen.isLessThan(Amount.valueOf(0, SI.MILLIMETER)))\r
49                         return zero;\r
50 \r
51                 java.awt.geom.Area burn = getCrossSection(regression);\r
52                 \r
53                 if (burn.isEmpty())\r
54                         return zero;\r
55                 \r
56                 burn.subtract(getCrossSection(regression.plus(Amount.valueOf(.001,\r
57                                 SI.MILLIMETER))));\r
58         \r
59                 Amount<Area> xSection = ShapeUtil.area(xsection.getShape(regression));\r
60                 \r
61                 Amount<Area> sides = ShapeUtil.perimeter(burn).divide(2).times(rLen).to(Area.UNIT);\r
62                 Amount<Area> ends = xSection.times(numberOfBurningEnds(regression));\r
63                 \r
64                 return sides.plus(ends);\r
65 \r
66         }\r
67 \r
68         public Amount<Volume> volume(Amount<Length> regression) {\r
69                 Amount<Volume> zero = Amount.valueOf(0, Volume.UNIT);\r
70                 \r
71                 Amount<Length> rLen = regressedLength(regression);\r
72                 \r
73                 if (rLen.isLessThan(Amount.valueOf(0, SI.MILLIMETER)))\r
74                         return zero;\r
75                 \r
76                 Amount<Area> xSection = ShapeUtil.area(xsection.getShape(regression));\r
77 \r
78                 return xSection.times(rLen).to(Volume.UNIT);\r
79 \r
80         }\r
81 \r
82 \r
83         public Amount<Length> webThickness() {\r
84                 if ( webThickness != null )\r
85                         return webThickness;\r
86                 java.awt.geom.Area a = getCrossSection(Amount.valueOf(0, SI.MILLIMETER));\r
87                 Rectangle r = a.getBounds();\r
88                 double max = r.getWidth() < r.getHeight() ? r.getHeight() : r\r
89                                 .getWidth(); // The max size\r
90                 double min = 0;\r
91                 double guess;\r
92                 while (true) {\r
93                         guess = min + (max - min) / 2; // Guess halfway through\r
94 \r
95                         a = getCrossSection(Amount.valueOf(guess, SI.MILLIMETER));\r
96                         if (a.isEmpty()) {\r
97                                 // guess is too big\r
98                                 max = guess;\r
99                         } else {\r
100                                 // min is too big\r
101                                 min = guess;\r
102                         }\r
103                         if ((max - min) < .01)\r
104                                 break;\r
105                 }\r
106                 webThickness = Amount.valueOf(guess, SI.MILLIMETER);\r
107                 \r
108                 //TODO Need to check # of burning ends!\r
109                 if (webThickness.isGreaterThan(getLength().divide(2)))\r
110                         webThickness = getLength().divide(2);\r
111                 \r
112                 return webThickness;\r
113         }\r
114 \r
115         public java.awt.geom.Area getCrossSection(Amount<Length> regression) {\r
116                 return xsection.getShape(regression);\r
117         }\r
118         \r
119         public java.awt.geom.Area getSideView(Amount<Length> regression) {\r
120                 java.awt.geom.Area res = new java.awt.geom.Area();\r
121                 \r
122                 Amount<Length> rLen = regressedLength(regression);\r
123                 \r
124                 double rLenmm = rLen.doubleValue(SI.MILLIMETER);\r
125                 \r
126                 for( java.awt.geom.Area a : ShapeUtil.separate(getCrossSection(regression))){\r
127                         Rectangle2D bounds = a.getBounds2D();\r
128                         Rectangle2D side = new Rectangle2D.Double(bounds.getMinX(), -rLenmm/2.0, bounds.getWidth(), rLenmm);\r
129                         res.add(new java.awt.geom.Area(side));\r
130                 }\r
131                 \r
132                 //Shift up or down based on burning ends\r
133                 if ( isForeEndInhibited() ){\r
134                         res.transform(AffineTransform.getTranslateInstance(0, +rLenmm/2.0));\r
135                 }\r
136                 if ( isAftEndInhibited() ){\r
137                         res.transform(AffineTransform.getTranslateInstance(0, -rLenmm/2.0));\r
138                 }\r
139                 return res;\r
140         }\r
141 \r
142 }\r