Added DEFAULT_GRAIN to grain types. When possible these should all be the same grain.
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / grain / ExtrudedGrain.java
index 70ae575058a871990cddab56c87075767aac215d..fb5bd5b627eaead3332e07470effad3a2342c174 100644 (file)
 package com.billkuker.rocketry.motorsim.grain;\r
 \r
-import java.awt.BasicStroke;\r
-import java.awt.BorderLayout;\r
-import java.awt.Color;\r
-import java.awt.Dimension;\r
-import java.awt.Graphics;\r
-import java.awt.Graphics2D;\r
-import java.awt.Rectangle;\r
-import java.awt.Shape;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Ellipse2D;\r
-import java.awt.geom.PathIterator;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.HashSet;\r
-import java.util.NoSuchElementException;\r
-import java.util.Set;\r
-import java.util.SortedMap;\r
-import java.util.TreeMap;\r
+import java.beans.PropertyVetoException;\r
 \r
-import javax.measure.quantity.Area;\r
-import javax.measure.quantity.Dimensionless;\r
 import javax.measure.quantity.Length;\r
-import javax.measure.quantity.Volume;\r
 import javax.measure.unit.SI;\r
-import javax.swing.JFrame;\r
-import javax.swing.JLabel;\r
-import javax.swing.JPanel;\r
-import javax.swing.JSlider;\r
-import javax.swing.JSplitPane;\r
-import javax.swing.event.ChangeEvent;\r
-import javax.swing.event.ChangeListener;\r
 \r
 import org.jscience.physics.amount.Amount;\r
 \r
 import com.billkuker.rocketry.motorsim.Grain;\r
-import com.billkuker.rocketry.motorsim.visual.Chart;\r
+import com.billkuker.rocketry.motorsim.MotorPart;\r
 \r
-public class ExtrudedGrain implements Grain, Grain.Graphical {\r
+public abstract class ExtrudedGrain extends MotorPart implements Grain {\r
+       private boolean foreEndInhibited = false;\r
+       private boolean aftEndInhibited = false;\r
+       private Amount<Length> length = Amount.valueOf(100, SI.MILLIMETER);\r
+       private Amount<Length> endLight = Amount.valueOf(0, SI.MILLIMETER);\r
        \r
-       Set<Shape> plus =  new HashSet<Shape>();\r
-       Set<Shape> minus =  new HashSet<Shape>();\r
-       Set<Shape> inhibited = new HashSet<Shape>();\r
-       \r
-       Amount<Length> length = Amount.valueOf(25, SI.MILLIMETER);\r
-       \r
-       Amount<Length> rStep;\r
-       \r
-       private class RegEntry{\r
-               Amount<Area> surfaceArea;\r
-               Amount<Volume> volume;\r
-       }\r
-       \r
-       SortedMap<Amount<Length>, RegEntry> data = new TreeMap<Amount<Length>, RegEntry>();\r
-       \r
-       Amount<Length> webThickness;\r
-       \r
-       {\r
-               /* Similar test grain*/\r
-               Shape outside = new Ellipse2D.Double(50,50,30,30);\r
-               plus.add(outside);\r
-               minus.add(new Ellipse2D.Double(50,60,10,10));\r
-               inhibited.add(outside);\r
-               length = Amount.valueOf(70, 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(12,12,6,20));\r
-               length = Amount.valueOf(70, SI.MILLIMETER);\r
-               */\r
-               \r
-               /*Plus sign\r
-               Shape outside = new Ellipse2D.Double(0,0,200,200);\r
-               plus.add(outside);\r
-               inhibited.add(outside);\r
-               minus.add(new Rectangle2D.Double(90,40,20,120));\r
-               minus.add(new Rectangle2D.Double(40,90,120,20));\r
-               */\r
-               \r
-               findWebThickness();\r
-               \r
-               fillInData();\r
+       protected int numberOfBurningEnds(Amount<Length> regression){\r
+               if ( regression.isLessThan(endLight) )\r
+                       return 0;\r
+               return (foreEndInhibited?0:1) + (aftEndInhibited?0:1);\r
        }\r
        \r
-       @Override\r
-       public Amount<Area> surfaceArea(Amount<Length> regression) {\r
-               if ( regression.isGreaterThan(webThickness) )\r
-                       return Amount.valueOf(0, Area.UNIT);\r
-               Amount<Length> highKey =  data.tailMap(regression).firstKey();\r
-               Amount<Length> lowKey;\r
-               try{\r
-                       lowKey =  data.headMap(regression).lastKey();\r
-               } catch ( NoSuchElementException e ){\r
-                       return data.get(highKey).surfaceArea;\r
-               }\r
-               \r
-               double lp = regression.minus(lowKey).divide(highKey.minus(lowKey)).to(Dimensionless.UNIT).doubleValue(Dimensionless.UNIT);\r
-               \r
-               Amount<Area> lowVal = data.get(lowKey).surfaceArea;\r
-               Amount<Area> highVal = data.get(highKey).surfaceArea;\r
-               \r
-               return lowVal.times(1-lp).plus(highVal.times(lp));\r
+       protected Amount<Length> regressedLength(Amount<Length> regression){\r
+               if ( regression.isLessThan(endLight) )\r
+                       return length;\r
+               return length.minus(regression.minus(endLight).times(numberOfBurningEnds(regression)));\r
        }\r
-       \r
-       @Override\r
-       public Amount<Volume> volume(Amount<Length> regression) {\r
-               if ( regression.isGreaterThan(webThickness) )\r
-                       return Amount.valueOf(0, Volume.UNIT);\r
-               Amount<Length> highKey =  data.tailMap(regression).firstKey();\r
-               Amount<Length> lowKey;\r
-               try{\r
-                       lowKey =  data.headMap(regression).lastKey();\r
-               } catch ( NoSuchElementException e ){\r
-                       return data.get(highKey).volume;\r
-               }\r
-               \r
-               double lp = regression.minus(lowKey).divide(highKey.minus(lowKey)).to(Dimensionless.UNIT).doubleValue(Dimensionless.UNIT);\r
-               \r
-               Amount<Volume> lowVal = data.get(lowKey).volume;\r
-               Amount<Volume> highVal = data.get(highKey).volume;\r
-               \r
-               return lowVal.times(1-lp).plus(highVal.times(lp));\r
-       }\r
-       \r
-       private void fillInData(){\r
-               double max = webThickness().doubleValue(SI.MILLIMETER);\r
-               double delta = max / 5;\r
-               rStep = Amount.valueOf(delta, SI.MILLIMETER).divide(10);\r
-               for( double r = 0; r <= max+1; r += delta){\r
-                       RegEntry e = new RegEntry();\r
-                       Amount<Length> regression = Amount.valueOf(r, SI.MILLIMETER);\r
-\r
-                       \r
-                       Amount<Length> rLen = length.minus(regression.times(2));\r
-                       if ( rLen.isLessThan(Amount.valueOf(0, SI.MILLIMETER)))\r
-                               break;\r
-                       \r
-                       System.out.println("Calculating area for regression " + regression);\r
-                       \r
-                       java.awt.geom.Area burn = getArea(regression);\r
-                       if ( burn.isEmpty() )\r
-                               break;\r
-                       burn.subtract(getArea(regression.plus(Amount.valueOf(.001, SI.MILLIMETER))));\r
-                       \r
-                       Amount<Area> xSection = crossSectionArea(getArea(regression));\r
-                       \r
-                       e.volume = xSection.times(rLen).to(Volume.UNIT);\r
-                       \r
-                       e.surfaceArea = perimeter(burn).divide(2).times(rLen).plus(xSection.times(2)).to(Area.UNIT);\r
-                       \r
-                       data.put(regression, e);\r
 \r
-               }\r
-               \r
-               RegEntry e = new RegEntry();\r
-               e.surfaceArea = Amount.valueOf(0, Area.UNIT);\r
-               e.volume = Amount.valueOf(0, Volume.UNIT);\r
-               data.put( webThickness(), e );\r
+       public boolean isForeEndInhibited() {\r
+               return foreEndInhibited;\r
        }\r
-       \r
-       private Amount<Area> crossSectionArea(java.awt.geom.Area a ) {\r
-               Rectangle r = a.getBounds();\r
-               int cnt = 0;\r
-               int total = 0;\r
-               double e = .1;\r
-               for( double x = r.getMinX(); x < r.getMaxX(); x += e){\r
-                       for( double y = r.getMinY(); y < r.getMaxY(); y += e){\r
-                               if ( a.contains(x, y) )\r
-                                       cnt++;\r
-                               total++;\r
-                       }\r
-               }\r
-               System.out.println(cnt + " out of " + total);\r
-               return Amount.valueOf(r.getWidth() * r.getHeight() * cnt / total, SI.MILLIMETER.pow(2)).to(Area.UNIT);\r
 \r
+       public void setForeEndInhibited(boolean foreEndInhibited)throws PropertyVetoException {\r
+               fireVetoableChange("foreEndInhibited", this.foreEndInhibited, foreEndInhibited);\r
+               boolean old = this.foreEndInhibited;\r
+               this.foreEndInhibited = foreEndInhibited;\r
+               firePropertyChange("foreEndInhibited", old, foreEndInhibited);\r
        }\r
-       \r
-       \r
 \r
-\r
-\r
-       @Override\r
-       public Amount<Length> webThickness() {\r
-               return webThickness;\r
-       }\r
-       \r
-       private Amount<Length> perimeter(java.awt.geom.Area a){\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 = getArea(Amount.valueOf(0, SI.MILLIMETER));\r
-               Rectangle r = a.getBounds();\r
-               double max = r.getWidth()<r.getHeight()?r.getHeight():r.getWidth(); //The max size\r
-               double min = 0;\r
-               double guess;\r
-               while(true){\r
-                       guess = min + (max-min)/2; //Guess halfway through\r
-                       System.out.println("Min: " + min + " Guess: " + guess + " Max: " + max);\r
-                       a = getArea(Amount.valueOf(guess, SI.MILLIMETER));\r
-                       if ( a.isEmpty() ){\r
-                               //guess is too big\r
-                               max = guess;\r
-                       } else {\r
-                               //min is too big\r
-                               min = guess;\r
-                       }\r
-                       if ( (max-min) < .01 )\r
-                               break;\r
-               }\r
-               webThickness = Amount.valueOf(guess, SI.MILLIMETER);\r
-               if ( webThickness.isGreaterThan(length.divide(2)))\r
-                       webThickness = length.divide(2);\r
-       }\r
-       private java.awt.geom.Area getArea(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.doubleValue(SI.MILLIMETER), true)));\r
-               for ( Shape s: minus )\r
-                       a.subtract(new java.awt.geom.Area(regress(s, regression.doubleValue(SI.MILLIMETER), false)));\r
-               return a;\r
+       public boolean isAftEndInhibited() {\r
+               return aftEndInhibited;\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
-                       double d = plus?-2*mm:2*mm;\r
-                       \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
-                       \r
-                       return new Rectangle2D.Double(x,y,w,h);\r
-               }\r
-               return null;\r
-       }\r
-       \r
 \r
-       public static void main(String args[]) throws Exception {\r
-               ExtrudedGrain e = new ExtrudedGrain();\r
-                       new GrainPanel(e).show();\r
-               \r
+       public void setAftEndInhibited(boolean aftEndInhibited) throws PropertyVetoException {\r
+               fireVetoableChange("aftEndInhibited", this.aftEndInhibited, aftEndInhibited);\r
+               boolean old = this.aftEndInhibited;\r
+               this.aftEndInhibited = aftEndInhibited;\r
+               firePropertyChange("aftEndInhibited", old, aftEndInhibited);\r
        }\r
 \r
-       @Override\r
-       public void draw(Graphics2D g2d, Amount<Length> regression) {\r
-               \r
-               java.awt.geom.Area reg = getArea(regression);\r
-               java.awt.geom.Area burn = getArea(regression);\r
-               burn.subtract(getArea(regression.plus(Amount.valueOf(.001, SI.MILLIMETER))));\r
-               java.awt.geom.Area noreg = getArea(Amount.valueOf(0,SI.MILLIMETER));\r
-               \r
-               Rectangle bounds = noreg.getBounds();\r
-               g2d.scale(200/bounds.getWidth(), 200/bounds.getHeight());\r
-               g2d.translate(-bounds.getX(), -bounds.getY());\r
-               \r
-\r
-               g2d.setStroke(new BasicStroke(0.5f));\r
-               \r
-               g2d.setColor(Color.GRAY);\r
-               g2d.fill(reg);\r
-               g2d.setColor(Color.RED);\r
-               g2d.draw(burn);\r
-               g2d.setColor(Color.BLACK);\r
-               g2d.draw(noreg);\r
-               \r
+       public Amount<Length> getLength() {\r
+               return length;\r
        }\r
 \r
-\r
+       public void setLength(Amount<Length> length) throws PropertyVetoException {\r
+               fireVetoableChange("length", this.length, length);\r
+               Amount<Length> old = this.length;\r
+               this.length = length;\r
+               firePropertyChange("length", old, length);\r
+       }\r
 }\r