--- /dev/null
+package com.billkuker.rocketry.motorsim.grain;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.Ellipse2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import javax.measure.quantity.Length;\r
+import javax.measure.unit.SI;\r
+\r
+import org.jscience.physics.amount.Amount;\r
+\r
+public class BurningShape {\r
+ Set<Shape> plus = new HashSet<Shape>();\r
+ Set<Shape> minus = new HashSet<Shape>();\r
+ Set<Shape> inhibited = new HashSet<Shape>();\r
+ \r
+ public void add(Shape s){\r
+ plus.add(s);\r
+ }\r
+ \r
+ public void subtract(Shape s){\r
+ minus.add(s);\r
+ }\r
+ \r
+ public void inhibit(Shape s){\r
+ inhibited.add(s);\r
+ }\r
+\r
+ public java.awt.geom.Area getShape(Amount<Length> regression) {\r
+ java.awt.geom.Area res = getPlus(regression);\r
+ res.subtract(getMinus(regression));\r
+ return res;\r
+ }\r
+ \r
+ \r
+ public java.awt.geom.Area getPlus(Amount<Length> regression) {\r
+ java.awt.geom.Area a = new java.awt.geom.Area();\r
+ for (Shape s : plus)\r
+ a.add(new java.awt.geom.Area(regress(s, regression\r
+ .doubleValue(SI.MILLIMETER), true)));\r
+ return a;\r
+ }\r
+ \r
+ public java.awt.geom.Area getMinus(Amount<Length> regression) {\r
+ java.awt.geom.Area a = new java.awt.geom.Area();\r
+ for (Shape s : minus)\r
+ a.add(new java.awt.geom.Area(regress(s, regression\r
+ .doubleValue(SI.MILLIMETER), false)));\r
+ return a;\r
+ }\r
+ \r
+ private Shape regress(Shape s, double mm, boolean plus) {\r
+ if (inhibited.contains(s))\r
+ return s;\r
+ if (s instanceof Ellipse2D) {\r
+ Ellipse2D e = (Ellipse2D) s;\r
+\r
+ double d = plus ? -2 * mm : 2 * mm;\r
+\r
+ double w = e.getWidth() + d;\r
+ double h = e.getHeight() + d;\r
+ double x = e.getX() - d / 2;\r
+ double y = e.getY() - d / 2;\r
+\r
+ return new Ellipse2D.Double(x, y, w, h);\r
+ } else if (s instanceof Rectangle2D) {\r
+ Rectangle2D r = (Rectangle2D) s;\r
+ \r
+ if ( plus ){\r
+ double d = -2 * mm;\r
+ double w = r.getWidth() + d;\r
+ double h = r.getHeight() + d;\r
+ double x = r.getX() - d / 2;\r
+ double y = r.getY() - d / 2;\r
+ return new Rectangle2D.Double(x, y, w, h);\r
+ } else {\r
+ //A rectangular hole gets rounded corners as it grows\r
+ java.awt.geom.Area a = new java.awt.geom.Area();\r
+ double d = 2 * mm;\r
+ \r
+ //Make it wider\r
+ double w = r.getWidth() + d;\r
+ double h = r.getHeight();\r
+ double x = r.getX() - d / 2;\r
+ double y = r.getY();\r
+ a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h)));\r
+ \r
+ //Make it taller\r
+ w = r.getWidth();\r
+ h = r.getHeight() + d;\r
+ x = r.getX();\r
+ y = r.getY() - d / 2;\r
+ a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h)));\r
+ \r
+ //Add rounded corners\r
+ a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()-mm, mm*2, mm*2)));\r
+ a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()-mm, mm*2, mm*2)));\r
+ a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2)));\r
+ a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2)));\r
+ \r
+ return a;\r
+ }\r
+\r
+ }\r
+ return null;\r
+ }\r
+\r
+ \r
+}\r
\r
public class ExtrudedShapeGrain extends MotorPart implements Grain {\r
\r
- Set<Shape> plus = new HashSet<Shape>();\r
-\r
- Set<Shape> minus = new HashSet<Shape>();\r
-\r
- Set<Shape> inhibited = new HashSet<Shape>();\r
+ BurningShape xsection = new BurningShape();\r
\r
Amount<Length> length = Amount.valueOf(25, SI.MILLIMETER);\r
\r
\r
/* Big c-slot */\r
Shape outside = new Ellipse2D.Double(0, 0, 30, 30);\r
- plus.add(outside);\r
- inhibited.add(outside);\r
- minus.add(new Rectangle2D.Double(13, 13, 4, 30));\r
+ xsection.add(outside);\r
+ xsection.inhibit(outside);\r
+ xsection.subtract(new Rectangle2D.Double(13, 13, 4, 30));\r
//minus.add(new Ellipse2D.Double(12, 12, 6, 6));\r
length = Amount.valueOf(70, SI.MILLIMETER);\r
/**/\r
\r
Amount<Area> xSection = crossSectionArea(regression);\r
\r
- return perimeter(burn).divide(2).times(rLen).plus(\r
+ return ShapeUtil.perimeter(burn).divide(2).times(rLen).plus(\r
xSection.times(2)).to(Area.UNIT);\r
}\r
\r
\r
private Amount<Area> crossSectionArea(Amount<Length> regression) {\r
//Get the PLUS shape and sum its area\r
- java.awt.geom.Area plus = getPlus(regression);\r
+ java.awt.geom.Area plus = xsection.getPlus(regression);\r
Amount<Area> plusArea = Amount.valueOf(0, SI.SQUARE_METRE);\r
- for (java.awt.geom.Area a : separate(plus)) {\r
- plusArea = plusArea.plus(area(a));\r
+ for (java.awt.geom.Area a : ShapeUtil.separate(plus)) {\r
+ plusArea = plusArea.plus(ShapeUtil.area(a));\r
}\r
\r
//Get the MINUS shape, intersect it with PLUS to get just the parts\r
//that are removed, sum it's area\r
- java.awt.geom.Area minus = getMinus(regression);\r
+ java.awt.geom.Area minus = xsection.getMinus(regression);\r
minus.intersect(plus);\r
Amount<Area> minusArea = Amount.valueOf(0, SI.SQUARE_METRE);\r
- for (java.awt.geom.Area a : separate(minus)) {\r
- minusArea = minusArea.plus(area(a));\r
+ for (java.awt.geom.Area a : ShapeUtil.separate(minus)) {\r
+ minusArea = minusArea.plus(ShapeUtil.area(a));\r
}\r
\r
//Subtract PLUS from MINUS and return\r
return webThickness;\r
}\r
\r
- private Amount<Length> perimeter(java.awt.geom.Area a) {\r
- //TODO: I think I need to handle seg_close!!\r
- PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
- double x = 0, y = 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) {\r
- // System.out.println("Line");\r
- double nx = coords[0];\r
- double ny = coords[1];\r
- // System.out.println(x+","+y+ " to " + nx+"," + ny);\r
- len += Math.sqrt(Math.pow(x - nx, 2) + Math.pow(y - ny, 2));\r
- x = nx;\r
- y = ny;\r
- } else if (type == PathIterator.SEG_MOVETO) {\r
- // System.out.println("Move");\r
- x = coords[0];\r
- y = coords[1];\r
- } else {\r
- // System.err.println("Got " + type);\r
- }\r
- i.next();\r
- }\r
- return Amount.valueOf(len, SI.MILLIMETER);\r
- }\r
-\r
private void findWebThickness() {\r
java.awt.geom.Area a = getCrossSection(Amount.valueOf(0, SI.MILLIMETER));\r
Rectangle r = a.getBounds();\r
\r
@Override\r
public java.awt.geom.Area getCrossSection(Amount<Length> regression) {\r
- java.awt.geom.Area res = getPlus(regression);\r
- res.subtract(getMinus(regression));\r
- return res;\r
+ return xsection.getShape(regression);\r
}\r
\r
@Override\r
\r
double rLenmm = rLen.doubleValue(SI.MILLIMETER);\r
\r
- for( java.awt.geom.Area a : separate(getCrossSection(regression))){\r
+ for( java.awt.geom.Area a : ShapeUtil.separate(getCrossSection(regression))){\r
Rectangle2D bounds = a.getBounds2D();\r
Rectangle2D side = new Rectangle2D.Double(bounds.getMinX(), -rLenmm/2.0, bounds.getWidth(), rLenmm);\r
res.add(new java.awt.geom.Area(side));\r
return res;\r
}\r
\r
- private java.awt.geom.Area getPlus(Amount<Length> regression) {\r
- java.awt.geom.Area a = new java.awt.geom.Area();\r
- for (Shape s : plus)\r
- a.add(new java.awt.geom.Area(regress(s, regression\r
- .doubleValue(SI.MILLIMETER), true)));\r
- return a;\r
- }\r
- \r
- private java.awt.geom.Area getMinus(Amount<Length> regression) {\r
- java.awt.geom.Area a = new java.awt.geom.Area();\r
- for (Shape s : minus)\r
- a.add(new java.awt.geom.Area(regress(s, regression\r
- .doubleValue(SI.MILLIMETER), false)));\r
- return a;\r
- }\r
- \r
-\r
- private Shape regress(Shape s, double mm, boolean plus) {\r
- if (inhibited.contains(s))\r
- return s;\r
- if (s instanceof Ellipse2D) {\r
- Ellipse2D e = (Ellipse2D) s;\r
-\r
- double d = plus ? -2 * mm : 2 * mm;\r
-\r
- double w = e.getWidth() + d;\r
- double h = e.getHeight() + d;\r
- double x = e.getX() - d / 2;\r
- double y = e.getY() - d / 2;\r
-\r
- return new Ellipse2D.Double(x, y, w, h);\r
- } else if (s instanceof Rectangle2D) {\r
- Rectangle2D r = (Rectangle2D) s;\r
- \r
- if ( plus ){\r
- double d = -2 * mm;\r
- double w = r.getWidth() + d;\r
- double h = r.getHeight() + d;\r
- double x = r.getX() - d / 2;\r
- double y = r.getY() - d / 2;\r
- return new Rectangle2D.Double(x, y, w, h);\r
- } else {\r
- //A rectangular hole gets rounded corners as it grows\r
- java.awt.geom.Area a = new java.awt.geom.Area();\r
- double d = 2 * mm;\r
- \r
- //Make it wider\r
- double w = r.getWidth() + d;\r
- double h = r.getHeight();\r
- double x = r.getX() - d / 2;\r
- double y = r.getY();\r
- a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h)));\r
- \r
- //Make it taller\r
- w = r.getWidth();\r
- h = r.getHeight() + d;\r
- x = r.getX();\r
- y = r.getY() - d / 2;\r
- a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h)));\r
- \r
- //Add rounded corners\r
- a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()-mm, mm*2, mm*2)));\r
- a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()-mm, mm*2, mm*2)));\r
- a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2)));\r
- a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2)));\r
- \r
- return a;\r
- }\r
-\r
- }\r
- return null;\r
- }\r
-\r
public static void main(String args[]) throws Exception {\r
ExtrudedShapeGrain e = new ExtrudedShapeGrain();\r
new Editor(e).show();\r
}\r
\r
\r
- /*\r
- * Separate an area into multiple distinct area.\r
- * Area CAN NOT HAVE HOLES. HOLES WILL BE RETURNED AS AREAS,\r
- * SO A DONUT WILL TURN INTO TWO CIRCLES.\r
- */\r
- private Set<java.awt.geom.Area> separate(java.awt.geom.Area a) {\r
- Set<java.awt.geom.Area> res = new HashSet<java.awt.geom.Area>();\r
- PathIterator i = a.getPathIterator(new AffineTransform());\r
- GeneralPath cur = null;\r
-\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
- if (cur != null ){\r
- java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
- if ( !a.isEmpty() )\r
- res.add(area);\r
- }\r
- cur = new GeneralPath(i.getWindingRule());\r
- break;\r
- case PathIterator.SEG_MOVETO:\r
- if (cur != null ){\r
- java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
- if ( !a.isEmpty() )\r
- res.add(area);\r
- }\r
- cur = new GeneralPath(i.getWindingRule());\r
- cur.moveTo(coords[0], coords[1]);\r
- break;\r
- case PathIterator.SEG_CUBICTO:\r
- cur.curveTo(coords[0], coords[1], coords[2], coords[3],\r
- coords[4], coords[5]);\r
- break;\r
- case PathIterator.SEG_LINETO:\r
- cur.lineTo(coords[0], coords[1]);\r
- break;\r
- case PathIterator.SEG_QUADTO:\r
- cur.quadTo(coords[0], coords[1], coords[2], coords[3]);\r
- break;\r
-\r
- }\r
- i.next();\r
- }\r
-\r
- return res;\r
- }\r
- \r
- /*\r
- * Return the Area of a singular polygon (NO HOLES OR DISJOINT PARTS).\r
- * Coordinates assumed to be in MM.\r
- * http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html\r
- * http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon\r
- * http://www.wikihow.com/Calculate-the-Area-of-a-Polygon\r
- */\r
- private Amount<Area> area(java.awt.geom.Area a) {\r
- if ( !a.isSingular() )\r
- throw new IllegalArgumentException("Can not calculate area of non-singular shape!");\r
- PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
- \r
- \r
- double x = 0, y = 0, sx = 0, sy = 0;\r
- double nx, ny;\r
- double area = 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
- //Go back to the start\r
- nx = sx;\r
- ny = sy;\r
- area += x * ny;\r
- area -= y * nx;\r
- break;\r
- case PathIterator.SEG_LINETO:\r
- nx = coords[0];\r
- ny = coords[1];\r
- area += x * ny;\r
- area -= y * nx;\r
-\r
- //Remember the last points\r
- x = nx;\r
- y = ny;\r
- \r
- break;\r
- case PathIterator.SEG_MOVETO:\r
- //Remember the starting point\r
- x = sx = coords[0];\r
- y = sy = coords[1];\r
- break;\r
- default:\r
- throw new Error("Bad segment type from Flattening Path Iterator");\r
- }\r
- i.next();\r
- }\r
- \r
- area = area / 2.0; // Result so far is double the signed area\r
- \r
- if ( area < 0 ) //Depending on winding it could be negative\r
- area = area * -1.0;\r
- \r
- \r
- return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT);\r
- }\r
-\r
public Amount<Length> getLength() {\r
return length;\r
}\r
--- /dev/null
+package com.billkuker.rocketry.motorsim.grain;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.GeneralPath;\r
+import java.awt.geom.PathIterator;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import javax.measure.quantity.Area;\r
+import javax.measure.quantity.Length;\r
+import javax.measure.unit.SI;\r
+\r
+import org.jscience.physics.amount.Amount;\r
+public class ShapeUtil {\r
+ private ShapeUtil(){}\r
+\r
+ /*\r
+ * Return the Area of a singular polygon (NO HOLES OR DISJOINT PARTS).\r
+ * Coordinates assumed to be in MM.\r
+ * http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html\r
+ * http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon\r
+ * http://www.wikihow.com/Calculate-the-Area-of-a-Polygon\r
+ */\r
+ public static Amount<Area> area(java.awt.geom.Area a) {\r
+ if ( !a.isSingular() )\r
+ throw new IllegalArgumentException("Can not calculate area of non-singular shape!");\r
+ PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
+ \r
+ \r
+ double x = 0, y = 0, sx = 0, sy = 0;\r
+ double nx, ny;\r
+ double area = 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
+ //Go back to the start\r
+ nx = sx;\r
+ ny = sy;\r
+ area += x * ny;\r
+ area -= y * nx;\r
+ break;\r
+ case PathIterator.SEG_LINETO:\r
+ nx = coords[0];\r
+ ny = coords[1];\r
+ area += x * ny;\r
+ area -= y * nx;\r
+ \r
+ //Remember the last points\r
+ x = nx;\r
+ y = ny;\r
+ \r
+ break;\r
+ case PathIterator.SEG_MOVETO:\r
+ //Remember the starting point\r
+ x = sx = coords[0];\r
+ y = sy = coords[1];\r
+ break;\r
+ default:\r
+ throw new Error("Bad segment type from Flattening Path Iterator");\r
+ }\r
+ i.next();\r
+ }\r
+ \r
+ area = area / 2.0; // Result so far is double the signed area\r
+ \r
+ if ( area < 0 ) //Depending on winding it could be negative\r
+ area = area * -1.0;\r
+ \r
+ \r
+ return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT);\r
+ }\r
+\r
+ public static Amount<Length> perimeter(java.awt.geom.Area a) {\r
+ //TODO: I think I need to handle seg_close!!\r
+ PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
+ double x = 0, y = 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) {\r
+ // System.out.println("Line");\r
+ double nx = coords[0];\r
+ double ny = coords[1];\r
+ // System.out.println(x+","+y+ " to " + nx+"," + ny);\r
+ len += Math.sqrt(Math.pow(x - nx, 2) + Math.pow(y - ny, 2));\r
+ x = nx;\r
+ y = ny;\r
+ } else if (type == PathIterator.SEG_MOVETO) {\r
+ // System.out.println("Move");\r
+ x = coords[0];\r
+ y = coords[1];\r
+ } else {\r
+ // System.err.println("Got " + type);\r
+ }\r
+ i.next();\r
+ }\r
+ return Amount.valueOf(len, SI.MILLIMETER);\r
+ }\r
+\r
+ /*\r
+ * Separate an area into multiple distinct area.\r
+ * Area CAN NOT HAVE HOLES. HOLES WILL BE RETURNED AS AREAS,\r
+ * SO A DONUT WILL TURN INTO TWO CIRCLES.\r
+ */\r
+ public static Set<java.awt.geom.Area> separate(java.awt.geom.Area a) {\r
+ Set<java.awt.geom.Area> res = new HashSet<java.awt.geom.Area>();\r
+ PathIterator i = a.getPathIterator(new AffineTransform());\r
+ GeneralPath cur = null;\r
+ \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
+ if (cur != null ){\r
+ java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
+ if ( !a.isEmpty() )\r
+ res.add(area);\r
+ }\r
+ cur = new GeneralPath(i.getWindingRule());\r
+ break;\r
+ case PathIterator.SEG_MOVETO:\r
+ if (cur != null ){\r
+ java.awt.geom.Area area = new java.awt.geom.Area(cur);\r
+ if ( !a.isEmpty() )\r
+ res.add(area);\r
+ }\r
+ cur = new GeneralPath(i.getWindingRule());\r
+ cur.moveTo(coords[0], coords[1]);\r
+ break;\r
+ case PathIterator.SEG_CUBICTO:\r
+ cur.curveTo(coords[0], coords[1], coords[2], coords[3],\r
+ coords[4], coords[5]);\r
+ break;\r
+ case PathIterator.SEG_LINETO:\r
+ cur.lineTo(coords[0], coords[1]);\r
+ break;\r
+ case PathIterator.SEG_QUADTO:\r
+ cur.quadTo(coords[0], coords[1], coords[2], coords[3]);\r
+ break;\r
+ \r
+ }\r
+ i.next();\r
+ }\r
+ \r
+ return res;\r
+ }\r
+ \r
+ \r
+}\r