package com.billkuker.rocketry.motorsim.grain.util;\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
import java.awt.geom.RoundRectangle2D;\r
import java.util.HashSet;\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
+ private static class RegressableShape {\r
+ private Area a;\r
+ public RegressableShape( Shape s ){\r
+ if ( s instanceof Area )\r
+ a = (Area)s;\r
+ a = new Area(s);\r
+ }\r
+ \r
+ public Area getRegressedShape(double regression){\r
+ if ( regression == 0 )\r
+ return a;\r
+ \r
+ //Build these separatly because intersecting the line\r
+ //with the circle creates a small amount of error which\r
+ //shows up when the resulting edge is no longer exactly\r
+ //colinear with the original edge.\r
+ Area rRect = new Area(); \r
+ Area rCirc = new Area();\r
+ \r
+ PathIterator i = a.getPathIterator(new AffineTransform(), .001);\r
+ double last[] = {0,0};\r
+ double first[] = {0,0};\r
+\r
+ while (!i.isDone()) {\r
+ double coords[] = new double[6];\r
+ int type = i.currentSegment(coords);\r
+ switch (type){\r
+ case PathIterator.SEG_MOVETO:\r
+ first[0] = last[0] = coords[0];\r
+ first[1] = last[1] = coords[1];\r
+ break;\r
+ case PathIterator.SEG_CLOSE:\r
+ coords[0] = first[0];\r
+ coords[1] = first[1];\r
+ case PathIterator.SEG_LINETO:\r
+ //Calculate the normal to this edge\r
+ double dx = coords[0]-last[0];\r
+ double dy = coords[1]-last[1];\r
+ double len = Math.sqrt(dx*dx + dy*dy); \r
+ double normal[] = {-dy/len,dx/len};\r
+ \r
+ //Calculate the displacement of the endpoints\r
+ //to create a rect\r
+ double displacement[] = {regression*normal[0], regression*normal[1]};\r
+\r
+ //Create that rect. Winding does not seem to matter...\r
+ GeneralPath p = new GeneralPath();\r
+ p.moveTo(last[0], last[1]);\r
+ p.lineTo(last[0]+displacement[0], last[1]+displacement[1]);\r
+ p.lineTo(coords[0]+displacement[0], coords[1]+displacement[1]);\r
+ p.lineTo(coords[0], coords[1]);\r
+ p.closePath();\r
+ rRect.add( new Area(p));\r
+\r
+ double er = Math.abs(regression);\r
+ rCirc.add(new Area(new Ellipse2D.Double(coords[0]-er, coords[1]-er, 2*er, 2*er)));\r
+ \r
+ last[0] = coords[0];\r
+ last[1] = coords[1];\r
+ break;\r
+ case PathIterator.SEG_CUBICTO:\r
+ case PathIterator.SEG_QUADTO:\r
+ throw new Error("Unflattend Geometry!");\r
+ }\r
+ i.next();\r
+ }\r
+ \r
+\r
+ if ( regression > 0 ){\r
+ //Combine all together\r
+ rRect.add(a);\r
+ rRect.add(rCirc);\r
+ }else{\r
+ Area acp = (Area)a.clone();\r
+ acp.subtract(rRect);\r
+ acp.subtract(rCirc);\r
+ rRect = acp;\r
+ }\r
+ return rRect;\r
+ }\r
+ }\r
+ \r
+\r
+ private static class ShapeAndTrans{\r
+ Shape shape;\r
+ AffineTransform trans;\r
+ ShapeAndTrans(Shape s, AffineTransform t){\r
+ shape = s;\r
+ trans = t;\r
+ }\r
+ ShapeAndTrans(Shape s){\r
+ shape = s;\r
+ trans = null;\r
+ }\r
+ @Override public int hashCode(){\r
+ if ( trans != null )\r
+ return shape.hashCode() * trans.hashCode();\r
+ return shape.hashCode();\r
+ }\r
+ @Override public boolean equals(Object o){\r
+ if ( o instanceof ShapeAndTrans ){\r
+ ShapeAndTrans s = (ShapeAndTrans)o;\r
+ if ( !s.shape.equals(shape) )\r
+ return false;\r
+ if ( s.trans == null && trans != null )\r
+ return false;\r
+ if ( s.trans != null && trans == null )\r
+ return false;\r
+ if ( trans != null && !trans.equals(s.trans) )\r
+ return false;\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ }\r
+ \r
+ Set<ShapeAndTrans> plus = new HashSet<ShapeAndTrans>();\r
+ Set<ShapeAndTrans> minus = new HashSet<ShapeAndTrans>();\r
+ Set<ShapeAndTrans> inhibited = new HashSet<ShapeAndTrans>();\r
\r
public void add(Shape s){\r
- plus.add(s);\r
+ plus.add(new ShapeAndTrans(s));\r
+ }\r
+ \r
+ public void add(Shape s, AffineTransform t){\r
+ plus.add(new ShapeAndTrans(s,t));\r
}\r
\r
public void subtract(Shape s){\r
- minus.add(s);\r
+ minus.add(new ShapeAndTrans(s));\r
+ }\r
+ \r
+ public void subtract(Shape s, AffineTransform t){\r
+ minus.add(new ShapeAndTrans(s,t));\r
}\r
\r
public void inhibit(Shape s){\r
- inhibited.add(s);\r
+ inhibited.add(new ShapeAndTrans(s));\r
+ }\r
+ \r
+ public void inhibit(Shape s, AffineTransform t){\r
+ inhibited.add(new ShapeAndTrans(s,t));\r
}\r
\r
/*\r
lastRegression = regression;\r
\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
- for (Shape s : minus)\r
- a.subtract(new java.awt.geom.Area(regress(s, regression\r
- .doubleValue(SI.MILLIMETER), false)));\r
+ for (ShapeAndTrans st : plus){\r
+ Shape s = st.shape;\r
+ if ( !inhibited.contains(st) )\r
+ s = regress(st.shape, regression\r
+ .doubleValue(SI.MILLIMETER), true);\r
+ if ( st.trans != null )\r
+ s = st.trans.createTransformedShape(s);\r
+ a.add(new java.awt.geom.Area(s));\r
+ }\r
+ for (ShapeAndTrans st : minus){\r
+ Shape s = st.shape;\r
+ if ( !inhibited.contains(st) )\r
+ s = regress(st.shape, regression\r
+ .doubleValue(SI.MILLIMETER), false);\r
+ if ( st.trans != null )\r
+ s = st.trans.createTransformedShape(s);\r
+ a.subtract(new java.awt.geom.Area(s));\r
+ }\r
\r
return lastArea = 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
return a;\r
}\r
\r
+ } else {\r
+ RegressableShape r = new RegressableShape(s);\r
+ System.err.println("Warning: Complex (non circle / square) geometry slows me down.");\r
+ return r.getRegressedShape(mm * (plus?-1:1));\r
}\r
- return null;\r
+\r
}\r
\r
\r