1 package com.billkuker.rocketry.motorsim.grain;
\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
12 import javax.measure.quantity.Length;
\r
13 import javax.measure.quantity.Volume;
\r
14 import javax.measure.unit.SI;
\r
16 import org.apache.log4j.Logger;
\r
17 import org.jscience.physics.amount.Amount;
\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
23 public class RotatedShapeGrain implements Grain {
\r
25 private static Logger log = Logger.getLogger(RotatedShapeGrain.class);
\r
27 public static RotatedShapeGrain DEFAULT_GRAIN = new RotatedShapeGrain(){
\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
44 public enum Quality {
\r
47 surfaceAreaStep = .001;
\r
48 squareFlatteningError = 0.001;
\r
49 squareSubdivide = .01;
\r
50 areaFlatteningError = .001;
\r
53 surfaceAreaStep = .001;
\r
54 squareFlatteningError = .1;
\r
55 squareSubdivide = .1;
\r
56 areaFlatteningError = .1;
\r
59 double surfaceAreaStep = .001;
\r
60 double squareFlatteningError = 0.001;
\r
61 double squareSubdivide = .01;
\r
62 double areaFlatteningError = .001;
\r
65 Quality quality = Quality.Low;
\r
67 protected BurningShape shape = new BurningShape();
\r
69 Amount<Length> web = null;
\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
84 public Area getSideView(Amount<Length> regression) {
\r
85 Area a = new Area();
\r
86 Area reg = shape.getShape(regression);
\r
88 a.transform(AffineTransform.getScaleInstance(-1, 1));
\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
97 if (regression.isGreaterThan(webThickness()))
\r
100 java.awt.geom.Area burn = shape.getShape(regression);
\r
102 if (burn.isEmpty())
\r
105 burn.subtract(shape.getShape(regression.plus(Amount.valueOf(quality.surfaceAreaStep,
\r
108 double sqmm = yRotatedSurfaceArea(burn);
\r
111 return Amount.valueOf(sqmm, SI.MILLIMETER.pow(2).asType(javax.measure.quantity.Area.class)).divide(2);
\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
121 sum = ShapeUtil.area(squared);
\r
122 Amount<Volume> v = sum.times(Amount.valueOf(Math.PI, SI.MILLIMETER)).to(Volume.UNIT);
\r
126 public Amount<Length> webThickness() {
\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
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
141 // guess is too big
\r
147 if ((max - min) < .01)
\r
150 web = Amount.valueOf(guess, SI.MILLIMETER);
\r
156 private Shape square(Shape a) {
\r
157 PathIterator i = a.getPathIterator(new AffineTransform(), quality.squareFlatteningError);
\r
158 GeneralPath cur = new GeneralPath();
\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
165 case PathIterator.SEG_CLOSE:
\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
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
185 last[0] = coords[0];
\r
186 last[1] = coords[1];
\r
188 case PathIterator.SEG_QUADTO:
\r
189 throw new Error("Non-flattened geometry!");
\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
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
208 double nx = coords[0];
\r
209 double ny = coords[1];
\r
211 if ( type == PathIterator.SEG_CLOSE ){
\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
224 add = 2 * Math.PI * xl * dy;
\r
225 } else if ( dy == 0 ){
\r
227 add = Math.PI * xl * xl - Math.PI * xs * xs;
\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
239 } else if (type == PathIterator.SEG_MOVETO) {
\r
240 mx = x = coords[0];
\r
241 my = y = coords[1];
\r
243 throw new Error("Non-flattened geometry!");
\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