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