--- /dev/null
+package com.billkuker.rocketry.motorsim.grain;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Area;\r
+import java.awt.geom.Ellipse2D;\r
+import java.awt.geom.GeneralPath;\r
+import java.awt.geom.PathIterator;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import javax.measure.quantity.Length;\r
+import javax.measure.quantity.Volume;\r
+import javax.measure.unit.SI;\r
+\r
+import org.jscience.physics.amount.Amount;\r
+\r
+import com.billkuker.rocketry.motorsim.Grain;\r
+import com.billkuker.rocketry.motorsim.visual.Editor;\r
+import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
+\r
+public class RotatedShapeGrain implements Grain {\r
+ \r
+ BurningShape shape = new BurningShape();\r
+ \r
+ Amount<Length> web = null;\r
+ \r
+ {\r
+ Shape outside = new Rectangle2D.Double(0,0,15,70);\r
+ shape.add( outside );\r
+ shape.inhibit( outside );\r
+ shape.subtract( new Rectangle2D.Double(0,50,5,70));\r
+ shape.subtract(new Rectangle2D.Double(0, 70, 15, 10));\r
+ }\r
+\r
+ @Override\r
+ public Area getCrossSection(Amount<Length> regression) {\r
+ Area ret = new Area();\r
+ for( Area a : ShapeUtil.separate(shape.getShape(regression))){\r
+ Rectangle2D b = a.getBounds2D();\r
+ Ellipse2D inner = new Ellipse2D.Double(-b.getMinX(), -b.getMinX(), b.getMinX()*2, b.getMinX()*2);\r
+ Ellipse2D outer = new Ellipse2D.Double(-b.getMaxX(), -b.getMaxX(), b.getMaxX()*2, b.getMaxX()*2);\r
+ Area aa = new Area(outer);\r
+ aa.subtract(new Area(inner));\r
+ ret.add(aa);\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ @Override\r
+ public Area getSideView(Amount<Length> regression) {\r
+ Area a = new Area();\r
+ Area reg = shape.getShape(regression);\r
+ a.add(reg);\r
+ a.transform(AffineTransform.getScaleInstance(-1, 1));\r
+ a.add(reg);\r
+ return a;\r
+ }\r
+\r
+ @Override\r
+ public Amount<javax.measure.quantity.Area> surfaceArea(\r
+ Amount<Length> regression) {\r
+ Amount<javax.measure.quantity.Area> zero = Amount.valueOf(0, javax.measure.quantity.Area.UNIT);\r
+ \r
+ if (regression.isGreaterThan(webThickness()))\r
+ return zero;\r
+\r
+ java.awt.geom.Area burn = shape.getShape(regression);\r
+ \r
+ if (burn.isEmpty())\r
+ return zero;\r
+ \r
+ burn.subtract(shape.getShape(regression.plus(Amount.valueOf(.001,\r
+ SI.MILLIMETER))));\r
+ \r
+ System.out.println(regression);\r
+ \r
+ double sqmm = yRotatedSurfaceArea(burn);\r
+\r
+ \r
+ return Amount.valueOf(sqmm, SI.MILLIMETER.pow(2).asType(javax.measure.quantity.Area.class)).divide(2);\r
+\r
+ }\r
+\r
+ @Override\r
+ public Amount<Volume> volume(Amount<Length> regression) {\r
+ Area squared = new Area(square(shape.getShape(regression)));\r
+ Amount<javax.measure.quantity.Area> sum = Amount.valueOf(0, SI.SQUARE_METRE);\r
+ for( Area a: ShapeUtil.separate(squared) ){\r
+ sum = sum.plus( ShapeUtil.area(a) );\r
+ }\r
+ Amount<Volume> v = sum.times(Amount.valueOf(Math.PI, SI.MILLIMETER)).to(Volume.UNIT);\r
+ return v;\r
+ }\r
+\r
+ @Override\r
+ public Amount<Length> webThickness() {\r
+ if ( web != null )\r
+ return web;\r
+ Rectangle2D b = shape.getShape(Amount.valueOf(0, SI.MILLIMETER)).getBounds2D();\r
+ double webmm = b.getWidth()>b.getHeight()?b.getWidth():b.getHeight();\r
+ return web = Amount.valueOf(webmm, SI.MILLIMETER);\r
+ }\r
+ \r
+\r
+ public static void main(String args[]) throws Exception {\r
+ RotatedShapeGrain e = new RotatedShapeGrain();\r
+ new Editor(e).show();\r
+ new GrainPanel(e).show();\r
+ }\r
+ \r
+ private Shape square(java.awt.geom.Area a) {\r
+ PathIterator i = a.getPathIterator(new AffineTransform(), 0.001);\r
+ GeneralPath cur = new GeneralPath();\r
+\r
+ double last[] = {0,0};\r
+ while (!i.isDone()) {\r
+ double coords[] = new double[6];\r
+ int type = i.currentSegment(coords);\r
+ switch (type) {\r
+ case PathIterator.SEG_CLOSE:\r
+ cur.closePath();\r
+ break;\r
+ case PathIterator.SEG_MOVETO:\r
+ cur.moveTo(Math.pow(coords[0],2), coords[1]);\r
+ last[0] = coords[0];\r
+ last[1] = coords[1];\r
+ break;\r
+ case PathIterator.SEG_CUBICTO:\r
+ throw new Error("Non-flattened geometry!");\r
+ case PathIterator.SEG_LINETO:\r
+ double x = last[0];\r
+ double y = last[1];\r
+ double len = Math.sqrt(Math.pow(last[0]-coords[0], 2) + Math.pow(last[1]-coords[1], 2));\r
+ int steps = (int)(len / .01) + 5;\r
+ for (int s = 0; s < steps; s++) {\r
+ x += (coords[0] - last[0]) / steps;\r
+ y += (coords[1] - last[1]) / steps;\r
+ cur.lineTo(Math.pow(x, 2), y);\r
+ }\r
+ last[0] = coords[0];\r
+ last[1] = coords[1];\r
+ break;\r
+ case PathIterator.SEG_QUADTO:\r
+ throw new Error("Non-flattened geometry!");\r
+\r
+ }\r
+ i.next();\r
+ }\r
+ return cur;\r
+ }\r
+ \r
+ private double yRotatedSurfaceArea(java.awt.geom.Area a) {\r
+ PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
+ double x = 0, y = 0;\r
+ double mx = 0, my = 0;\r
+ double len = 0;\r
+ while (!i.isDone()) {\r
+ double coords[] = new double[6];\r
+ int type = i.currentSegment(coords);\r
+ if (type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE) {\r
+\r
+\r
+ double nx = coords[0];\r
+ double ny = coords[1];\r
+ \r
+ if ( type == PathIterator.SEG_CLOSE ){\r
+ nx = mx;\r
+ ny = my;\r
+ }\r
+ \r
+ double dy = Math.abs(y-ny);\r
+ double dx = Math.abs(x-nx);\r
+ double xl = x>nx?x:nx;\r
+ double xs = x<nx?x:nx; \r
+ \r
+ double add = 0;\r
+ if ( dx == 0 ){\r
+ //Cylender\r
+ add = 2 * Math.PI * xl * dy;\r
+ } else if ( dy == 0 ){\r
+ //disk\r
+ add = Math.PI * xl * xl - Math.PI * xs * xs;\r
+ }else{\r
+ double h = xl/dx * dy;\r
+ double s1 = Math.sqrt(xl*xl + h*h);\r
+ double s2 = Math.sqrt(xs*xs + (h-dy)*(h-dy));\r
+ add = Math.PI * (xl*s1 - xs*s2);\r
+ }\r
+ \r
+ System.out.println(add);\r
+ \r
+ len += add;\r
+\r
+ x = nx;\r
+ y = ny;\r
+ } else if (type == PathIterator.SEG_MOVETO) {\r
+ mx = x = coords[0];\r
+ my = y = coords[1];\r
+ } else {\r
+ throw new Error("Non-flattened geometry!");\r
+ }\r
+ i.next();\r
+ }\r
+ return len;\r
+ }\r
+\r
+\r
+}\r