Allow an affinetrans to be passed in with a shape
authorBill Kuker <bkuker@billkuker.com>
Tue, 23 Jun 2009 18:17:14 +0000 (18:17 +0000)
committerBill Kuker <bkuker@billkuker.com>
Tue, 23 Jun 2009 18:17:14 +0000 (18:17 +0000)
src/com/billkuker/rocketry/motorsim/grain/util/BurningShape.java

index c17c667becc9766e5e546285c5a1881c9e7aada4..6bcd15da9e3e3e83ea7280b1d683225fafd81769 100644 (file)
@@ -1,8 +1,11 @@
 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
@@ -14,20 +17,149 @@ import javax.measure.unit.SI;
 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
@@ -44,19 +176,29 @@ public class BurningShape {
                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
@@ -95,8 +237,12 @@ public class BurningShape {
                                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