Started adding beanish stuff - property listeners and such, first stab at a generic...
authorBill Kuker <bkuker@billkuker.com>
Sun, 12 Apr 2009 02:33:38 +0000 (02:33 +0000)
committerBill Kuker <bkuker@billkuker.com>
Sun, 12 Apr 2009 02:33:38 +0000 (02:33 +0000)
19 files changed:
.classpath
l2fprod-common-sheet.jar [new file with mode: 0644]
src/Test.java [deleted file]
src/com/billkuker/rocketry/motorsim/Burn.java
src/com/billkuker/rocketry/motorsim/ConvergentDivergentNozzle.java
src/com/billkuker/rocketry/motorsim/MotorPart.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/Nozzle.java
src/com/billkuker/rocketry/motorsim/grain/BurnPanel.java [deleted file]
src/com/billkuker/rocketry/motorsim/grain/CoredCylindricalGrain.java
src/com/billkuker/rocketry/motorsim/grain/ExtrudedGrain.java
src/com/billkuker/rocketry/motorsim/grain/GrainPanel.java [deleted file]
src/com/billkuker/rocketry/motorsim/test/CoredCylindricalGrainTest.java
src/com/billkuker/rocketry/motorsim/validation/Validating.java [deleted file]
src/com/billkuker/rocketry/motorsim/validation/ValidationException.java [deleted file]
src/com/billkuker/rocketry/motorsim/visual/BurnPanel.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/visual/Chart.java
src/com/billkuker/rocketry/motorsim/visual/Editor.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/visual/GrainPanel.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/visual/NozzlePanel.java [new file with mode: 0644]

index dadcce85b3238fd4913c157104121706f1c52bd9..7b7173fdfe3a983a0b46c0fb7b5fc1aa29a36d96 100644 (file)
@@ -6,6 +6,7 @@
        <classpathentry kind="lib" path="jfreechart-1.0.12.jar"/>\r
        <classpathentry kind="lib" path="jcommon-1.0.15.jar"/>\r
        <classpathentry kind="lib" path="log4j-1.2.15.jar"/>\r
+       <classpathentry kind="lib" path="l2fprod-common-sheet.jar"/>\r
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>\r
        <classpathentry kind="output" path="bin"/>\r
 </classpath>\r
diff --git a/l2fprod-common-sheet.jar b/l2fprod-common-sheet.jar
new file mode 100644 (file)
index 0000000..c6a0e46
Binary files /dev/null and b/l2fprod-common-sheet.jar differ
diff --git a/src/Test.java b/src/Test.java
deleted file mode 100644 (file)
index f46a3c7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-import javax.measure.quantity.Dimensionless;\r
-import javax.measure.quantity.Length;\r
-import javax.measure.unit.SI;\r
-\r
-import org.jscience.physics.amount.Amount;\r
-\r
-\r
-public class Test {\r
-       public static void main(String args[]){\r
-               Amount<Length> l = (Amount<Length>)Amount.valueOf(1, SI.METER);\r
-               System.out.println(l.divide(l).to(Dimensionless.UNIT));\r
-               Amount<Length> m = (Amount<Length>)Amount.valueOf(1, 10000000000000000.0, SI.METER);\r
-               System.out.println(m);\r
-       \r
-       }\r
-}\r
index 51804655c811c67298bae292434c16423dc6a504..64f8b53dd92082fb6665b7908476cdbf31c99ec2 100644 (file)
@@ -24,10 +24,10 @@ import org.jscience.physics.amount.Amount;
 import org.jscience.physics.amount.Constants;\r
 \r
 import com.billkuker.rocketry.motorsim.fuel.KNSU;\r
-import com.billkuker.rocketry.motorsim.grain.BurnPanel;\r
 import com.billkuker.rocketry.motorsim.grain.CompoundGrain;\r
 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
 import com.billkuker.rocketry.motorsim.grain.MultiGrain;\r
+import com.billkuker.rocketry.motorsim.visual.BurnPanel;\r
 \r
 public class Burn {\r
        \r
index c9978ed2acd8799434b72718efafb2995d34b50e..09e0598a43a48d071a2c775d7727e0ad601ab9b7 100644 (file)
@@ -1,14 +1,21 @@
 package com.billkuker.rocketry.motorsim;\r
 \r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Ellipse2D;\r
+import java.awt.geom.GeneralPath;\r
+import java.awt.geom.Line2D;\r
+\r
 import javax.measure.quantity.Area;\r
 import javax.measure.quantity.Dimensionless;\r
 import javax.measure.quantity.Force;\r
 import javax.measure.quantity.Length;\r
 import javax.measure.quantity.Pressure;\r
+import javax.measure.unit.SI;\r
 \r
 import org.jscience.physics.amount.Amount;\r
 \r
-public class ConvergentDivergentNozzle implements Nozzle {\r
+public class ConvergentDivergentNozzle extends MotorPart implements Nozzle {\r
 \r
        private Amount<Length> throatDiameter;\r
        \r
@@ -33,7 +40,11 @@ public class ConvergentDivergentNozzle implements Nozzle {
 \r
 \r
        public void setThroatDiameter(Amount<Length> throatDiameter) {\r
+               if ( exitDiameter != null && throatDiameter.isGreaterThan(exitDiameter))\r
+                       throw new IllegalArgumentException("Throat > Exit");\r
+               Amount<Length> old = this.throatDiameter;\r
                this.throatDiameter = throatDiameter;\r
+               firePropertyChange("throatDiameter", old, throatDiameter);\r
        }\r
        \r
 \r
@@ -43,7 +54,11 @@ public class ConvergentDivergentNozzle implements Nozzle {
 \r
 \r
        public void setExitDiameter(Amount<Length> exitDiameter) {\r
+               if ( throatDiameter != null && exitDiameter.isLessThan(throatDiameter))\r
+                       throw new IllegalArgumentException("Throat > Exit");\r
+               Amount<Length> old = this.exitDiameter;\r
                this.exitDiameter = exitDiameter;\r
+               firePropertyChange("exitDiameter", old, exitDiameter);\r
        }\r
        \r
        @Override\r
@@ -75,8 +90,35 @@ public class ConvergentDivergentNozzle implements Nozzle {
        }\r
 \r
        public void setEfficiency(double efficiency) {\r
+               double old = this.efficiency;\r
                this.efficiency = efficiency;\r
+               firePropertyChange("efficiency", old, efficiency);\r
        }\r
 \r
-\r
+       public Shape nozzleShape(Amount<Length> chamberDiameter){\r
+               GeneralPath s = new GeneralPath();\r
+               double throatR = throatDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
+               double exitR = exitDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
+               double cR;\r
+               if (chamberDiameter == null)\r
+                       cR = exitR;\r
+               else\r
+                       cR = chamberDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
+               \r
+               double diff = exitR-throatR;\r
+               double cDiff = cR-throatR;\r
+               \r
+               s.append(new Line2D.Double(0,0,diff,diff*3), false);\r
+               s.append(new Line2D.Double(0,0,cDiff,-cDiff), true);\r
+               \r
+               s.transform(AffineTransform.getScaleInstance(-1, 1));\r
+               s.transform(AffineTransform.getTranslateInstance(-throatR, 0));\r
+               \r
+               s.append(new Line2D.Double(0,0,diff,diff*3), false);\r
+               s.append(new Line2D.Double(0,0,cDiff,-cDiff), true);\r
+               \r
+               //a.add(new java.awt.geom.Area( new Ellipse2D.Double(0,0,5,5)));\r
+               \r
+               return s;\r
+       }\r
 }\r
diff --git a/src/com/billkuker/rocketry/motorsim/MotorPart.java b/src/com/billkuker/rocketry/motorsim/MotorPart.java
new file mode 100644 (file)
index 0000000..0e6ea80
--- /dev/null
@@ -0,0 +1,131 @@
+package com.billkuker.rocketry.motorsim;\r
+\r
+import java.beans.BeanInfo;\r
+import java.beans.IntrospectionException;\r
+import java.beans.Introspector;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.beans.PropertyChangeSupport;\r
+import java.beans.PropertyDescriptor;\r
+import java.beans.PropertyVetoException;\r
+import java.beans.VetoableChangeListener;\r
+import java.beans.VetoableChangeSupport;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+\r
+import javax.measure.quantity.Quantity;\r
+import javax.measure.unit.Unit;\r
+\r
+import org.jscience.physics.amount.Amount;\r
+\r
+public class MotorPart {\r
+       PropertyChangeSupport pcs;\r
+       VetoableChangeSupport vcs;\r
+       \r
+       public interface Validating {\r
+                void checkValidity() throws ValidationException;\r
+                \r
+                public class ValidationException extends Exception {\r
+                               private static final long serialVersionUID = 1L;\r
+                               public ValidationException(Validating v, String error) {\r
+                                       super(v.toString() + ": " + error);\r
+                               }\r
+                       }\r
+       }\r
+       \r
+       public MotorPart() {\r
+               pcs = new PropertyChangeSupport(this);\r
+               vcs = new VetoableChangeSupport(this);\r
+               \r
+               vcs.addVetoableChangeListener(new VetoableChangeListener(){\r
+                       @Override\r
+                       public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {\r
+                               if ( evt.getNewValue() instanceof Amount ){\r
+                               try {\r
+                                       BeanInfo b = Introspector.getBeanInfo(evt.getSource().getClass());\r
+                                       PropertyDescriptor ps[] = b.getPropertyDescriptors();\r
+                                       for ( int i = 0; i < ps.length; i++ ){\r
+                                               if (ps[i].getName().equals(evt.getPropertyName())){\r
+                                                       Type t = ps[i].getReadMethod().getGenericReturnType();\r
+                                                       ParameterizedType p = (ParameterizedType) t;\r
+                                                       Class expected = (Class) p.getActualTypeArguments()[0];\r
+                                                       Field f = expected.getDeclaredField("UNIT");\r
+                                                       Unit u = (Unit) f.get(null);\r
+                                                       \r
+                                                       Amount a = (Amount)evt.getNewValue();\r
+                                                       if (!a.getUnit().isCompatible(u))\r
+                                                               throw new PropertyVetoException(ps[i].getShortDescription()\r
+                                                                               + " must be in units of "\r
+                                                                               + expected.getSimpleName(), evt);\r
+\r
+                                                       System.out.println("Expected " + expected + " got " + u);\r
+                                               }\r
+                                       }\r
+                               } catch ( PropertyVetoException e ){\r
+                                       throw e;\r
+                               } catch (Exception e) {\r
+                                       e.printStackTrace();\r
+                               }\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+\r
+       public <T extends Quantity> void checkAmount(Amount<T> a, Class<T> q)\r
+                       throws IllegalArgumentException {\r
+               try {\r
+                       Field f = q.getDeclaredField("UNIT");\r
+                       Unit<T> u = (Unit<T>)f.get(null);\r
+\r
+                       if (!a.getUnit().isCompatible(u)) {\r
+                               throw new IllegalArgumentException("Value "\r
+                                               + " must be in units of " + q.getSimpleName());\r
+                       }\r
+               } catch (Exception e) {\r
+                       throw new Error(e);\r
+               }\r
+       }\r
+\r
+       public void addPropertyChangeListener(PropertyChangeListener listener) {\r
+               pcs.addPropertyChangeListener(listener);\r
+       }\r
+\r
+       public void removePropertyChangeListener(PropertyChangeListener listener) {\r
+               pcs.removePropertyChangeListener(listener);\r
+       }\r
+\r
+       protected void firePropertyChange(PropertyChangeEvent evt) {\r
+               pcs.firePropertyChange(evt);\r
+       }\r
+\r
+       protected void firePropertyChange(String propertyName, boolean oldValue,\r
+                       boolean newValue) {\r
+               pcs.firePropertyChange(propertyName, oldValue, newValue);\r
+       }\r
+\r
+       protected void firePropertyChange(String propertyName, int oldValue,\r
+                       int newValue) {\r
+               pcs.firePropertyChange(propertyName, oldValue, newValue);\r
+       }\r
+\r
+       protected void firePropertyChange(String propertyName, Object oldValue,\r
+                       Object newValue) {\r
+               pcs.firePropertyChange(propertyName, oldValue, newValue);\r
+       }\r
+\r
+       public void fireVetoableChange(String arg0, boolean arg1, boolean arg2)\r
+                       throws PropertyVetoException {\r
+               vcs.fireVetoableChange(arg0, arg1, arg2);\r
+       }\r
+\r
+       public void fireVetoableChange(String arg0, int arg1, int arg2)\r
+                       throws PropertyVetoException {\r
+               vcs.fireVetoableChange(arg0, arg1, arg2);\r
+       }\r
+\r
+       public void fireVetoableChange(String arg0, Object arg1, Object arg2)\r
+                       throws PropertyVetoException {\r
+               vcs.fireVetoableChange(arg0, arg1, arg2);\r
+       }\r
+}\r
index e11a76ced10ec22a45a7994fd4d6c9c49f0b1d77..2f316668bbcc6d91c4e84d9a560f4254dc460cf1 100644 (file)
@@ -1,7 +1,10 @@
 package com.billkuker.rocketry.motorsim;\r
 \r
+import java.awt.Shape;\r
+\r
 import javax.measure.quantity.Area;\r
 import javax.measure.quantity.Force;\r
+import javax.measure.quantity.Length;\r
 import javax.measure.quantity.Pressure;\r
 \r
 import org.jscience.physics.amount.Amount;\r
@@ -10,4 +13,6 @@ public interface Nozzle {
        public Amount<Area> throatArea();\r
        \r
        public Amount<Force> thrust(Amount<Pressure> Po, Amount<Pressure> Pe, Amount<Pressure> Patm, final double k );\r
+       \r
+       public Shape nozzleShape(Amount<Length> chamberDiameter);\r
 }\r
diff --git a/src/com/billkuker/rocketry/motorsim/grain/BurnPanel.java b/src/com/billkuker/rocketry/motorsim/grain/BurnPanel.java
deleted file mode 100644 (file)
index c0a2673..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.billkuker.rocketry.motorsim.grain;\r
-\r
-import java.awt.BorderLayout;\r
-import java.text.NumberFormat;\r
-\r
-import javax.measure.quantity.Duration;\r
-import javax.measure.quantity.Force;\r
-import javax.measure.quantity.Pressure;\r
-import javax.measure.quantity.Velocity;\r
-import javax.measure.unit.SI;\r
-import javax.swing.JFrame;\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.Burn;\r
-import com.billkuker.rocketry.motorsim.visual.Chart;\r
-\r
-public class BurnPanel extends JPanel {\r
-       private Burn burn;\r
-       Chart<Duration, Pressure> pressure;\r
-       Chart<Duration, Force> thrust;\r
-       Chart<Pressure, Velocity> burnRate;\r
-       GrainPanel grain;\r
-       Amount<Duration> displayedTime = Amount.valueOf(0, SI.SECOND);\r
-       \r
-       public BurnPanel(Burn b){\r
-               super( new BorderLayout() );\r
-               burn = b;\r
-               \r
-               try {\r
-                       pressure = new Chart<Duration, Pressure>(\r
-                                       SI.SECOND,\r
-                                       SI.MEGA(SI.PASCAL),\r
-                                       b,\r
-                                       "pressure");\r
-                       pressure.setDomain(burn.getData().keySet());\r
-                       \r
-                       thrust = new Chart<Duration, Force>(\r
-                                       SI.SECOND,\r
-                                       SI.NEWTON,\r
-                                       b,\r
-                                       "thrust");\r
-                       thrust.setDomain(burn.getData().keySet());\r
-                       \r
-                       burnRate = new Chart<Pressure, Velocity>(\r
-                                       SI.MEGA(SI.PASCAL),\r
-                                       SI.METERS_PER_SECOND,\r
-                                       burn.getMotor().getFuel(),\r
-                                       "burnRate");\r
-                       burnRate.setDomain(\r
-                                       burnRate.new IntervalDomain(\r
-                                                       Amount.valueOf(0, SI.MEGA(SI.PASCAL)),\r
-                                                       Amount.valueOf(3, SI.MEGA(SI.PASCAL)),\r
-                                                       20\r
-                                                       ));\r
-                       \r
-                       \r
-                       JSplitPane tp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, thrust, pressure);\r
-                       tp.setDividerLocation(.5);\r
-                       tp.setResizeWeight(.5);\r
-                       \r
-                       grain = new GrainPanel(burn.getMotor().getGrain());\r
-                       \r
-                       JSplitPane grains = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, grain, burnRate);\r
-                       grains.setDividerLocation(.5);\r
-                       grains.setResizeWeight(.5);\r
-                       \r
-                       JSplitPane main = new JSplitPane(JSplitPane.VERTICAL_SPLIT, grains, tp);\r
-                       main.setDividerLocation(.5);\r
-                       main.setResizeWeight(.5);\r
-                       \r
-                       add( main, BorderLayout.CENTER );\r
-                       \r
-                       add( new SL(), BorderLayout.SOUTH);\r
-                       \r
-               } catch (NoSuchMethodException e){\r
-                       throw new Error(e);\r
-               }\r
-               \r
-\r
-       }\r
-       \r
-       private class SL extends JSlider implements ChangeListener{\r
-               private static final long serialVersionUID = 1L;\r
-               private static final int STEPS = 150;\r
-               public SL(){\r
-                       addChangeListener(this);\r
-                       setMinimum(0);\r
-                       setMaximum(STEPS);\r
-                       setValue(0);\r
-               }\r
-               \r
-               @Override\r
-               public void stateChanged(ChangeEvent e) {\r
-                       double t = ((SL)e.getSource()).getValue();\r
-                       displayedTime = burn.burnTime().divide(STEPS).times(t);\r
-                       \r
-                       //Find the nearest key in the data set\r
-                       displayedTime =burn.getData().tailMap(displayedTime).firstKey();\r
-                       \r
-                       NumberFormat nf = NumberFormat.getInstance();\r
-                       nf.setMaximumFractionDigits(2);\r
-                       //System.out.println("Time: " + nf.format(displayedTime.doubleValue(SI.SECOND)) + "s");\r
-                       \r
-                       pressure.mark(displayedTime);\r
-                       thrust.mark(displayedTime);\r
-                       \r
-                       grain.setDisplayedRegression(burn.getData().get(displayedTime).regression);\r
-                       \r
-                       burnRate.mark(burn.getData().get(displayedTime).chamberPressure);\r
-                       \r
-                       \r
-                       /*\r
-                       double r = ((SL)e.getSource()).getValue();\r
-                       displayedRegression = grain.webThickness().divide(STEPS).times(r);\r
-                       NumberFormat nf = NumberFormat.getInstance();\r
-                       nf.setMaximumFractionDigits(2);\r
-                       l.setText("Regression: " + nf.format(displayedRegression.doubleValue(SI.MILLIMETER)) + "mm");\r
-                       area.mark(displayedRegression);\r
-                       volume.mark(displayedRegression);\r
-                       if ( xc != null )\r
-                               xc.repaint();\r
-                       */\r
-               }\r
-       }\r
-       \r
-       public void show(){\r
-               JFrame f = new JFrame();\r
-               f.setSize(1280,720);\r
-               f.setLocation(0, 0);\r
-               f.setContentPane(this);\r
-               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
-               f.setVisible(true);\r
-       }\r
-}\r
index 7853c93529a1c5f354faa19c514703371a940542..9ba8f97e98c3507676036ea4e27920c7b8c6e951 100644 (file)
@@ -3,6 +3,7 @@ package com.billkuker.rocketry.motorsim.grain;
 import java.awt.Shape;\r
 import java.awt.geom.Ellipse2D;\r
 import java.awt.geom.Rectangle2D;\r
+import java.beans.PropertyVetoException;\r
 \r
 import javax.measure.quantity.Area;\r
 import javax.measure.quantity.Length;\r
@@ -12,22 +13,25 @@ import javax.measure.unit.SI;
 import org.jscience.physics.amount.Amount;\r
 \r
 import com.billkuker.rocketry.motorsim.Grain;\r
-import com.billkuker.rocketry.motorsim.validation.Validating;\r
-import com.billkuker.rocketry.motorsim.validation.ValidationException;\r
+import com.billkuker.rocketry.motorsim.MotorPart;\r
 \r
-public class CoredCylindricalGrain implements Grain, Validating {\r
+\r
+public class CoredCylindricalGrain extends MotorPart implements Grain, MotorPart.Validating {\r
 \r
        private Amount<Length> length, oD, iD;\r
-       private boolean oInh = true, iInh = false, eInh = false;\r
+       private boolean outerSurfaceInhibited = true, innerSurfaceInhibited = false, endSurfaceInhibited = false;\r
 \r
        public CoredCylindricalGrain() {\r
-\r
+               length = Amount.valueOf(100, SI.MILLIMETER);\r
+               oD = Amount.valueOf(30, SI.MILLIMETER);\r
+               iD = Amount.valueOf(10, SI.MILLIMETER);\r
        }\r
        \r
+       \r
        public void inhibit(boolean in, boolean out, boolean end){\r
-               oInh = out;\r
-               iInh = in;\r
-               eInh = end;\r
+               outerSurfaceInhibited = out;\r
+               innerSurfaceInhibited = in;\r
+               endSurfaceInhibited = end;\r
        }\r
 \r
        @Override\r
@@ -36,19 +40,19 @@ public class CoredCylindricalGrain implements Grain, Validating {
                \r
                //Calculated regressed length\r
                Amount<Length> cLength = length;\r
-               if ( !eInh ){\r
+               if ( !endSurfaceInhibited ){\r
                        cLength = cLength.minus(regression.times(2));\r
                }\r
                \r
                //Calculate regressed iD\r
                Amount<Length> cID = iD;\r
-               if ( !iInh ){\r
+               if ( !innerSurfaceInhibited ){\r
                        cID = iD.plus(regression.times(2));\r
                }\r
                \r
                //Calculate regressed oD\r
                Amount<Length> cOD = oD;\r
-               if ( !oInh ){\r
+               if ( !outerSurfaceInhibited ){\r
                        cOD = oD.minus(regression.times(2));\r
                }\r
                \r
@@ -65,7 +69,7 @@ public class CoredCylindricalGrain implements Grain, Validating {
                \r
                Amount<Area> ends = (cOD.divide(2).pow(2).times(Math.PI)).minus(cID.divide(2).pow(2).times(Math.PI)).times(2).to(SI.SQUARE_METRE);\r
                \r
-               Amount<Area> total = inner.times(iInh?0:1).plus(outer.times(oInh?0:1)).plus(ends.times(eInh?0:1));\r
+               Amount<Area> total = inner.times(innerSurfaceInhibited?0:1).plus(outer.times(outerSurfaceInhibited?0:1)).plus(ends.times(endSurfaceInhibited?0:1));\r
                \r
                return total;\r
        }\r
@@ -76,19 +80,19 @@ public class CoredCylindricalGrain implements Grain, Validating {
                \r
                //Calculated regressed length\r
                Amount<Length> cLength = length;\r
-               if ( !eInh ){\r
+               if ( !endSurfaceInhibited ){\r
                        cLength = cLength.minus(regression.times(2));\r
                }\r
                \r
                //Calculate regressed iD\r
                Amount<Length> cID = iD;\r
-               if ( !iInh ){\r
+               if ( !innerSurfaceInhibited ){\r
                        cID = iD.plus(regression.times(2));\r
                }\r
                \r
                //Calculate regressed oD\r
                Amount<Length> cOD = oD;\r
-               if ( !oInh ){\r
+               if ( !outerSurfaceInhibited ){\r
                        cOD = oD.minus(regression.times(2));\r
                }\r
                \r
@@ -105,16 +109,25 @@ public class CoredCylindricalGrain implements Grain, Validating {
                return end.times(cLength).to(SI.CUBIC_METRE);\r
        }\r
 \r
-       public void setLength(Amount<Length> length) {\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
-       public void setOD(Amount<Length> od) {\r
-               oD = od;\r
+       public void setOD(Amount<Length> od) throws PropertyVetoException {\r
+               fireVetoableChange("od", this.oD, od);\r
+               Amount<Length> old = this.oD;\r
+               this.oD = od;\r
+               firePropertyChange("OD", old, oD);\r
        }\r
 \r
-       public void setID(Amount<Length> id) {\r
+       public void setID(Amount<Length> id) throws PropertyVetoException {\r
+               fireVetoableChange("id", this.iD, id);\r
+               Amount<Length> old = this.iD;\r
                iD = id;\r
+               firePropertyChange("ID", old, iD);\r
        }\r
        \r
        public void checkValidity() throws ValidationException{\r
@@ -127,22 +140,26 @@ public class CoredCylindricalGrain implements Grain, Validating {
                if ( iD.isGreaterThan(oD) )\r
                        throw new ValidationException(this, "iD > oD");\r
                \r
-               if ( iInh && oInh && eInh )\r
+               if ( innerSurfaceInhibited && outerSurfaceInhibited && endSurfaceInhibited )\r
                        throw new ValidationException(this, "No exposed grain surface");\r
                \r
        }\r
 \r
        @Override\r
        public Amount<Length> webThickness() {\r
+               if ( innerSurfaceInhibited && outerSurfaceInhibited && endSurfaceInhibited ){\r
+                       return oD;\r
+               }\r
+               \r
                Amount<Length> radial = null;\r
-               if ( !iInh && !oInh )\r
+               if ( !innerSurfaceInhibited && !outerSurfaceInhibited )\r
                        radial = oD.minus(iD).divide(4); //Outer and inner exposed\r
-               else if ( !iInh || !oInh )\r
+               else if ( !innerSurfaceInhibited || !outerSurfaceInhibited )\r
                        radial = oD.minus(iD).divide(2); //Outer or inner exposed\r
                \r
                Amount<Length> axial = null;\r
                \r
-               if ( !eInh )\r
+               if ( !endSurfaceInhibited )\r
                        axial = length.divide(2);\r
                \r
                if ( axial == null )\r
@@ -172,9 +189,9 @@ public class CoredCylindricalGrain implements Grain, Validating {
                double oDmm = oD.doubleValue(SI.MILLIMETER);\r
                double iDmm = iD.doubleValue(SI.MILLIMETER);\r
 \r
-               if ( !oInh )\r
+               if ( !outerSurfaceInhibited )\r
                        oDmm -= 2.0 * rmm;\r
-               if ( !iInh )\r
+               if ( !innerSurfaceInhibited )\r
                        iDmm += 2.0 * rmm;\r
                \r
                Shape oDs = new Ellipse2D.Double(-oDmm/2.0, -oDmm/2.0, oDmm, oDmm);\r
@@ -191,11 +208,11 @@ public class CoredCylindricalGrain implements Grain, Validating {
                double iDmm = iD.doubleValue(SI.MILLIMETER);\r
                double lmm = length.doubleValue(SI.MILLIMETER);\r
 \r
-               if ( !oInh )\r
+               if ( !outerSurfaceInhibited )\r
                        oDmm -= 2.0 * rmm;\r
-               if ( !iInh )\r
+               if ( !innerSurfaceInhibited )\r
                        iDmm += 2.0 * rmm;\r
-               if ( !eInh )\r
+               if ( !endSurfaceInhibited )\r
                        lmm -= 2.0 * rmm;\r
                \r
                java.awt.geom.Area a = new java.awt.geom.Area();\r
@@ -204,6 +221,45 @@ public class CoredCylindricalGrain implements Grain, Validating {
                \r
                return a;\r
        }\r
+\r
+\r
+       public boolean isOuterSurfaceInhibited() {\r
+               return outerSurfaceInhibited;\r
+       }\r
+\r
+\r
+       public void setOuterSurfaceInhibited(boolean outerSurfaceInhibited) throws PropertyVetoException {\r
+               fireVetoableChange("outerSurfaceInhibited", this.outerSurfaceInhibited, outerSurfaceInhibited);\r
+               boolean old = this.outerSurfaceInhibited;\r
+               this.outerSurfaceInhibited = outerSurfaceInhibited;\r
+               firePropertyChange("outerSurfaceInhibited", old, outerSurfaceInhibited);\r
+       }\r
+\r
+\r
+       public boolean isInnerSurfaceInhibited() {\r
+               return innerSurfaceInhibited;\r
+       }\r
+\r
+\r
+       public void setInnerSurfaceInhibited(boolean innerSurfaceInhibited)  throws PropertyVetoException {\r
+               fireVetoableChange("innerSurfaceInhibited", this.innerSurfaceInhibited, innerSurfaceInhibited);\r
+               boolean old = this.innerSurfaceInhibited;\r
+               this.innerSurfaceInhibited = innerSurfaceInhibited;\r
+               firePropertyChange("innerSurfaceInhibited", old, innerSurfaceInhibited);\r
+       }\r
+\r
+\r
+       public boolean isEndSurfaceInhibited() {\r
+               return endSurfaceInhibited;\r
+       }\r
+\r
+\r
+       public void setEndSurfaceInhibited(boolean endSurfaceInhibited) throws PropertyVetoException {\r
+               fireVetoableChange("endSurfaceInhibited", this.endSurfaceInhibited, endSurfaceInhibited);\r
+               boolean old = this.endSurfaceInhibited;\r
+               this.endSurfaceInhibited = endSurfaceInhibited;\r
+               firePropertyChange("endSurfaceInhibited", old, endSurfaceInhibited);\r
+       }\r
        \r
 \r
 }\r
index cebe3f998f420d00cde45c5b84d576fb2b0e7608..cf9963c92d1a604b68f6a88311c52433c7d32f28 100644 (file)
@@ -18,6 +18,7 @@ import javax.measure.unit.SI;
 import org.jscience.physics.amount.Amount;\r
 \r
 import com.billkuker.rocketry.motorsim.Grain;\r
+import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
 \r
 public class ExtrudedGrain implements Grain {\r
 \r
diff --git a/src/com/billkuker/rocketry/motorsim/grain/GrainPanel.java b/src/com/billkuker/rocketry/motorsim/grain/GrainPanel.java
deleted file mode 100644 (file)
index d30e51f..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-package com.billkuker.rocketry.motorsim.grain;\r
-\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.geom.AffineTransform;\r
-import java.text.NumberFormat;\r
-\r
-import javax.measure.quantity.Area;\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
-\r
-public class GrainPanel extends JPanel {\r
-       private static final long serialVersionUID = 1L;\r
-       private Amount<Length> displayedRegression = Amount.valueOf(0, SI.MILLIMETER);\r
-       private JLabel l = new JLabel();\r
-       private Chart<Length,Area> area;\r
-       Chart<Length, Volume> volume;\r
-       private XC xc;\r
-       private SL sl;\r
-       private Grain grain;\r
-       \r
-       public GrainPanel(Grain g){\r
-               super(new BorderLayout());\r
-\r
-               grain = g;\r
-\r
-               try {\r
-\r
-                       area = new Chart<Length, Area>(\r
-                                       SI.MILLIMETER,\r
-                                       SI.MILLIMETER.pow(2).asType(Area.class),\r
-                                       grain,\r
-                                       "surfaceArea");\r
-                       area.setDomain(area.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
-                       \r
-                       volume = new Chart<Length, Volume>(\r
-                                       SI.MILLIMETER,\r
-                                       SI.MILLIMETER.pow(3).asType(Volume.class),\r
-                                       grain,\r
-                                       "volume");\r
-                       volume.setDomain(volume.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
-\r
-                       area.setMaximumSize(new Dimension(200,100));\r
-                       volume.setMaximumSize(new Dimension(200,100));\r
-                       \r
-\r
-               } catch (ClassCastException e) {\r
-                       // TODO Auto-generated catch block\r
-                       e.printStackTrace();\r
-               } catch (NoSuchMethodException e) {\r
-                       // TODO Auto-generated catch block\r
-                       e.printStackTrace();\r
-               }\r
-               \r
-               \r
-               JSplitPane charts = new JSplitPane(JSplitPane.VERTICAL_SPLIT, area, volume);\r
-               charts.setDividerLocation(.5);\r
-               charts.setResizeWeight(.5);\r
-               \r
-               JPanel left = new JPanel(new BorderLayout());\r
-               \r
-               add(xc = new XC(grain), BorderLayout.CENTER);\r
-               left.add(xc);\r
-\r
-               left.add(l, BorderLayout.NORTH);\r
-               left.add( sl = new SL(), BorderLayout.SOUTH);\r
-       \r
-               add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, charts));\r
-\r
-       }\r
-       \r
-       public void setDisplayedRegression( Amount<Length> r ){\r
-               displayedRegression = r;\r
-               \r
-               NumberFormat nf = NumberFormat.getInstance();\r
-               nf.setMaximumFractionDigits(2);\r
-               l.setText("Regression: " + nf.format(displayedRegression.doubleValue(SI.MILLIMETER)) + "mm");\r
-               \r
-               area.mark(displayedRegression);\r
-               volume.mark(displayedRegression);\r
-               if ( xc != null )\r
-                       xc.repaint();\r
-       }\r
-       \r
-       private class XC extends JPanel{\r
-               private static final long serialVersionUID = 1L;\r
-               Grain grain;\r
-               public XC(Grain g){\r
-                       setMinimumSize(new Dimension(440,250));\r
-                       grain = g;\r
-               }\r
-               public void paint(Graphics g){\r
-                       super.paint(g);\r
-                       Graphics2D g2d = (Graphics2D)g;\r
-                       g2d.translate(10, 30);\r
-                       /*\r
-                       grain.draw(g2d, displayedRegression );\r
-                       */\r
-                       \r
-                       {\r
-                               AffineTransform t = g2d.getTransform();\r
-                               java.awt.geom.Area unburnt = grain.getCrossSection(Amount.valueOf(0, SI.MILLIMETER));\r
-                               \r
-                               Rectangle bounds = unburnt.getBounds();\r
-                               g2d.scale(200 / bounds.getWidth(), 200 / bounds.getHeight());\r
-                               g2d.translate(-bounds.getX(), -bounds.getY());\r
-       \r
-                               //Draw the fuel that is left\r
-                               java.awt.geom.Area burning = grain.getCrossSection(displayedRegression);\r
-                               g2d.setColor(Color.RED);\r
-                               g2d.fill(burning);\r
-                               //Draw the fuel that is left\r
-                               java.awt.geom.Area left = grain.getCrossSection(displayedRegression.plus(grain.webThickness().divide(30)));\r
-                               g2d.setColor(Color.GRAY);\r
-                               g2d.fill(left);\r
-                               //Draw the outline of the unburnt grain\r
-                               g2d.setColor(Color.BLACK);\r
-                               g2d.draw(unburnt);\r
-                               //untranslate\r
-                               g2d.setTransform(t);\r
-                       }\r
-                       {\r
-                               AffineTransform t = g2d.getTransform();\r
-                               java.awt.geom.Area unburnt = grain.getSideView(Amount.valueOf(0, SI.MILLIMETER));\r
-                               \r
-                               Rectangle bounds = unburnt.getBounds();\r
-                               g2d.translate(220, 0);\r
-                               \r
-                               double max = bounds.getWidth();\r
-                               if ( bounds.getHeight() > max )\r
-                                       max = bounds.getHeight();\r
-                               \r
-                               g2d.scale(200 / max, 200 / max);\r
-                               g2d.translate(-bounds.getX(), -bounds.getY());\r
-       \r
-                               //Draw the fuel that is left\r
-                               java.awt.geom.Area burning = grain.getSideView(displayedRegression);\r
-                               g2d.setColor(Color.RED);\r
-                               g2d.fill(burning);\r
-                               //Draw the fuel that is left\r
-                               java.awt.geom.Area left = grain.getSideView(displayedRegression.plus(grain.webThickness().divide(30)));\r
-                               g2d.setColor(Color.GRAY);\r
-                               g2d.fill(left);\r
-                               //Draw the outline of the unburnt grain\r
-                               g2d.setColor(Color.BLACK);\r
-                               g2d.draw(unburnt);\r
-                               //untranslate\r
-                               g2d.setTransform(t);\r
-                       }\r
-                       \r
-               }\r
-       }\r
-       \r
-       private class SL extends JSlider implements ChangeListener{\r
-               private static final long serialVersionUID = 1L;\r
-               private static final int STEPS = 60;\r
-               public SL(){\r
-                       addChangeListener(this);\r
-                       setMinimum(0);\r
-                       setMaximum(STEPS);\r
-                       setValue(0);\r
-               }\r
-               \r
-               @Override\r
-               public void stateChanged(ChangeEvent e) {\r
-                       double r = ((SL)e.getSource()).getValue();\r
-\r
-                       setDisplayedRegression(grain.webThickness().divide(STEPS).times(r));\r
-               }\r
-       }\r
-       \r
-       public void show(){\r
-               JFrame f = new JFrame();\r
-               f.setSize(1024,600);\r
-               f.setContentPane(this);\r
-               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
-               f.setVisible(true);\r
-       }\r
-\r
-}\r
index a2fddab0fc1522d9699ddd9570e3b91eb2e7280a..f54bd1d91ced0a3f2877ca9812be553464578e10 100644 (file)
@@ -1,18 +1,20 @@
 package com.billkuker.rocketry.motorsim.test;\r
 \r
+import java.beans.PropertyVetoException;\r
+\r
 import javax.measure.quantity.Length;\r
 import javax.measure.unit.SI;\r
 \r
 import org.jscience.physics.amount.Amount;\r
 import org.junit.Test;\r
 \r
+import com.billkuker.rocketry.motorsim.MotorPart;\r
 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
-import com.billkuker.rocketry.motorsim.validation.ValidationException;\r
 \r
 public class CoredCylindricalGrainTest extends RocketTest {\r
 \r
        @Test\r
-       public void testSurfaceArea() {\r
+       public void testSurfaceArea() throws PropertyVetoException {\r
                CoredCylindricalGrain g = new CoredCylindricalGrain();\r
                \r
                g.setLength(Amount.valueOf(70, SI.MILLIMETER));\r
@@ -29,7 +31,7 @@ public class CoredCylindricalGrainTest extends RocketTest {
        \r
 \r
        @Test\r
-       public void testWebThickness() {\r
+       public void testWebThickness() throws PropertyVetoException {\r
                CoredCylindricalGrain g = new CoredCylindricalGrain();\r
                \r
                //thin and long\r
@@ -48,7 +50,7 @@ public class CoredCylindricalGrainTest extends RocketTest {
        }\r
 \r
        @Test\r
-       public void testVolume() {\r
+       public void testVolume() throws PropertyVetoException {\r
                CoredCylindricalGrain g = new CoredCylindricalGrain();\r
                \r
                //thin and long\r
@@ -59,8 +61,8 @@ public class CoredCylindricalGrainTest extends RocketTest {
                System.out.println(g.volume(Amount.valueOf(0, SI.MILLIMETER)));\r
        }\r
 \r
-       @Test(expected=ValidationException.class)\r
-       public void testCheckValidity() throws ValidationException{\r
+       @Test(expected=MotorPart.Validating.ValidationException.class)\r
+       public void testCheckValidity() throws MotorPart.Validating.ValidationException, PropertyVetoException{\r
                CoredCylindricalGrain g = new CoredCylindricalGrain();\r
                \r
                //thin and long\r
diff --git a/src/com/billkuker/rocketry/motorsim/validation/Validating.java b/src/com/billkuker/rocketry/motorsim/validation/Validating.java
deleted file mode 100644 (file)
index f84a742..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.billkuker.rocketry.motorsim.validation;\r
-\r
-public interface Validating {\r
-        void checkValidity() throws ValidationException;\r
-}\r
diff --git a/src/com/billkuker/rocketry/motorsim/validation/ValidationException.java b/src/com/billkuker/rocketry/motorsim/validation/ValidationException.java
deleted file mode 100644 (file)
index 88ae846..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.billkuker.rocketry.motorsim.validation;\r
-\r
-public class ValidationException extends Exception {\r
-       /**\r
-        * \r
-        */\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       public ValidationException(Validating v, String error) {\r
-               super(v.toString() + ": " + error);\r
-       }\r
-}\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/BurnPanel.java b/src/com/billkuker/rocketry/motorsim/visual/BurnPanel.java
new file mode 100644 (file)
index 0000000..11def56
--- /dev/null
@@ -0,0 +1,139 @@
+package com.billkuker.rocketry.motorsim.visual;\r
+\r
+import java.awt.BorderLayout;\r
+import java.text.NumberFormat;\r
+\r
+import javax.measure.quantity.Duration;\r
+import javax.measure.quantity.Force;\r
+import javax.measure.quantity.Pressure;\r
+import javax.measure.quantity.Velocity;\r
+import javax.measure.unit.SI;\r
+import javax.swing.JFrame;\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.Burn;\r
+\r
+public class BurnPanel extends JPanel {\r
+       private Burn burn;\r
+       Chart<Duration, Pressure> pressure;\r
+       Chart<Duration, Force> thrust;\r
+       Chart<Pressure, Velocity> burnRate;\r
+       GrainPanel grain;\r
+       Amount<Duration> displayedTime = Amount.valueOf(0, SI.SECOND);\r
+       \r
+       public BurnPanel(Burn b){\r
+               super( new BorderLayout() );\r
+               burn = b;\r
+               \r
+               try {\r
+                       pressure = new Chart<Duration, Pressure>(\r
+                                       SI.SECOND,\r
+                                       SI.MEGA(SI.PASCAL),\r
+                                       b,\r
+                                       "pressure");\r
+                       pressure.setDomain(burn.getData().keySet());\r
+                       \r
+                       thrust = new Chart<Duration, Force>(\r
+                                       SI.SECOND,\r
+                                       SI.NEWTON,\r
+                                       b,\r
+                                       "thrust");\r
+                       thrust.setDomain(burn.getData().keySet());\r
+                       \r
+                       burnRate = new Chart<Pressure, Velocity>(\r
+                                       SI.MEGA(SI.PASCAL),\r
+                                       SI.METERS_PER_SECOND,\r
+                                       burn.getMotor().getFuel(),\r
+                                       "burnRate");\r
+                       burnRate.setDomain(\r
+                                       burnRate.new IntervalDomain(\r
+                                                       Amount.valueOf(0, SI.MEGA(SI.PASCAL)),\r
+                                                       Amount.valueOf(3, SI.MEGA(SI.PASCAL)),\r
+                                                       20\r
+                                                       ));\r
+                       \r
+                       \r
+                       JSplitPane tp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, thrust, pressure);\r
+                       tp.setDividerLocation(.5);\r
+                       tp.setResizeWeight(.5);\r
+                       \r
+                       grain = new GrainPanel(burn.getMotor().getGrain());\r
+                       \r
+                       JSplitPane grains = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, grain, burnRate);\r
+                       grains.setDividerLocation(.5);\r
+                       grains.setResizeWeight(.5);\r
+                       \r
+                       JSplitPane main = new JSplitPane(JSplitPane.VERTICAL_SPLIT, grains, tp);\r
+                       main.setDividerLocation(.5);\r
+                       main.setResizeWeight(.5);\r
+                       \r
+                       add( main, BorderLayout.CENTER );\r
+                       \r
+                       add( new SL(), BorderLayout.SOUTH);\r
+                       \r
+               } catch (NoSuchMethodException e){\r
+                       throw new Error(e);\r
+               }\r
+               \r
+\r
+       }\r
+       \r
+       private class SL extends JSlider implements ChangeListener{\r
+               private static final long serialVersionUID = 1L;\r
+               private static final int STEPS = 150;\r
+               public SL(){\r
+                       addChangeListener(this);\r
+                       setMinimum(0);\r
+                       setMaximum(STEPS);\r
+                       setValue(0);\r
+               }\r
+               \r
+               @Override\r
+               public void stateChanged(ChangeEvent e) {\r
+                       double t = ((SL)e.getSource()).getValue();\r
+                       displayedTime = burn.burnTime().divide(STEPS).times(t);\r
+                       \r
+                       //Find the nearest key in the data set\r
+                       displayedTime =burn.getData().tailMap(displayedTime).firstKey();\r
+                       \r
+                       NumberFormat nf = NumberFormat.getInstance();\r
+                       nf.setMaximumFractionDigits(2);\r
+                       //System.out.println("Time: " + nf.format(displayedTime.doubleValue(SI.SECOND)) + "s");\r
+                       \r
+                       pressure.mark(displayedTime);\r
+                       thrust.mark(displayedTime);\r
+                       \r
+                       grain.setDisplayedRegression(burn.getData().get(displayedTime).regression);\r
+                       \r
+                       burnRate.mark(burn.getData().get(displayedTime).chamberPressure);\r
+                       \r
+                       \r
+                       /*\r
+                       double r = ((SL)e.getSource()).getValue();\r
+                       displayedRegression = grain.webThickness().divide(STEPS).times(r);\r
+                       NumberFormat nf = NumberFormat.getInstance();\r
+                       nf.setMaximumFractionDigits(2);\r
+                       l.setText("Regression: " + nf.format(displayedRegression.doubleValue(SI.MILLIMETER)) + "mm");\r
+                       area.mark(displayedRegression);\r
+                       volume.mark(displayedRegression);\r
+                       if ( xc != null )\r
+                               xc.repaint();\r
+                       */\r
+               }\r
+       }\r
+       \r
+       public void show(){\r
+               JFrame f = new JFrame();\r
+               f.setSize(1280,720);\r
+               f.setLocation(0, 0);\r
+               f.setContentPane(this);\r
+               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
+               f.setVisible(true);\r
+       }\r
+}\r
index 61fd9cfe257194fd71df712dd26daca09772d788..2bf853793815f708fd77328e6415bb67a41d1d4f 100644 (file)
@@ -24,6 +24,8 @@ import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;\r
 import org.jscience.physics.amount.Amount;\r
 \r
+import sun.misc.Cleaner;\r
+\r
 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
 \r
 public class Chart<X extends Quantity, Y extends Quantity> extends JPanel  {\r
@@ -128,6 +130,7 @@ public class Chart<X extends Quantity, Y extends Quantity> extends JPanel  {
                //double step = (high - low) / 50;\r
                //for (double x = low; x < high; x += step) {\r
                        //Amount<X> ax = Amount.valueOf(x, xUnit);\r
+               series.clear();\r
                for( Amount<X> ax: d){\r
                        try {\r
                                Amount<Y> y = (Amount<Y>) f.invoke(source, ax);\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/Editor.java b/src/com/billkuker/rocketry/motorsim/visual/Editor.java
new file mode 100644 (file)
index 0000000..de5f491
--- /dev/null
@@ -0,0 +1,164 @@
+package com.billkuker.rocketry.motorsim.visual;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.GridLayout;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.FocusEvent;\r
+import java.awt.event.FocusListener;\r
+import java.awt.event.InputEvent;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.beans.BeanInfo;\r
+import java.beans.IntrospectionException;\r
+import java.beans.Introspector;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.beans.PropertyDescriptor;\r
+import java.beans.PropertyEditor;\r
+import java.beans.PropertyEditorManager;\r
+import java.beans.PropertyEditorSupport;\r
+import java.beans.PropertyVetoException;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+\r
+import javax.measure.unit.NonSI;\r
+import javax.measure.unit.Unit;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTextField;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.jscience.physics.amount.Amount;\r
+\r
+import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
+import com.l2fprod.common.propertysheet.PropertySheetPanel;\r
+import com.sun.media.sound.Toolkit;\r
+\r
+public class Editor extends JPanel {\r
+       private static Logger log = Logger.getLogger(Editor.class);\r
+\r
+       private Object obj;\r
+\r
+       public Editor(Object o) throws IntrospectionException,\r
+                       IllegalArgumentException, IllegalAccessException,\r
+                       InvocationTargetException {\r
+               obj = o;\r
+               /*\r
+               BeanInfo b = Introspector.getBeanInfo(o.getClass());\r
+               setLayout(new GridLayout(b.getPropertyDescriptors().length - 1, 2));\r
+               for (PropertyDescriptor p : b.getPropertyDescriptors()) {\r
+                       log.debug(p.getName());\r
+                       if (p.getName().equals("class"))\r
+                               continue;\r
+\r
+                       add(new JLabel(p.getName()));\r
+                       if (p.getPropertyType().isAssignableFrom(Amount.class)){\r
+                               add(new AmountEditor(p));\r
+                       } else if ( p.getPropertyType() == boolean.class ){\r
+                               add( new BooleanEditor(p) );\r
+                               \r
+                       }\r
+               }\r
+               log.debug(PropertyEditorManager.findEditor(boolean.class).supportsCustomEditor());*/\r
+               \r
+               PropertyEditorManager.registerEditor(Amount.class, AmountPropertyEditor.class);\r
+               \r
+               final PropertySheetPanel ps = new PropertySheetPanel();\r
+               ps.setBeanInfo(Introspector.getBeanInfo(obj.getClass()));\r
+               ps.readFromObject(obj);\r
+               add(ps);\r
+               \r
+               ps.addPropertySheetChangeListener(new PropertyChangeListener(){\r
+\r
+                       @Override\r
+                       public void propertyChange(PropertyChangeEvent evt) {\r
+                               //When something changes just update the\r
+                               //object, I want the changes to be immediate.\r
+                               try {\r
+                                       ps.writeToObject(obj);\r
+                               } catch ( Exception v ){\r
+                                       //TODO\r
+                                       v.printStackTrace();\r
+                                       java.awt.Toolkit.getDefaultToolkit().beep();\r
+                               } finally {\r
+                                       ps.readFromObject(obj);\r
+                               }\r
+                       }\r
+                       \r
+               });\r
+       }\r
+\r
+       public static void main(String args[]) throws Exception {\r
+               CoredCylindricalGrain g = new CoredCylindricalGrain();\r
+               g.setOD(Amount.valueOf(1, NonSI.INCH));\r
+               new Editor(g).show();\r
+               \r
+               new GrainPanel(g).show();\r
+       }\r
+\r
+       public void show() {\r
+               JFrame f = new JFrame();\r
+               f.setSize(600,400);\r
+               f.setContentPane(this);\r
+               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
+               f.setVisible(true);\r
+       }\r
+       \r
+       public static class AmountPropertyEditor extends PropertyEditorSupport{\r
+               JTextField editor = new JTextField();\r
+               Unit oldUnit;\r
+\r
+               @Override\r
+               public boolean supportsCustomEditor() {\r
+                       return true;\r
+               }\r
+               @Override public String getAsText(){\r
+                       return editor.getText();\r
+               }\r
+               \r
+               @Override public Object getValue(){\r
+                       String text = editor.getText().trim();\r
+                       try {\r
+                               try {\r
+                                       return Amount.valueOf(Integer.parseInt(text), oldUnit);\r
+                               } catch (NumberFormatException e){\r
+                                       \r
+                               }\r
+                               Amount a = Amount.valueOf(Double.parseDouble(text), oldUnit);\r
+                               return a;\r
+                       } catch (NumberFormatException e){\r
+                               Amount a = Amount.valueOf(editor.getText());\r
+                               oldUnit = a.getUnit();\r
+                               return a;\r
+                       }\r
+\r
+               }\r
+               @Override public void setValue(Object o){\r
+                       Amount a = (Amount)o;\r
+                       oldUnit = a.getUnit();\r
+                       \r
+                       String text;\r
+                       if ( a.isExact() )\r
+                                        text = a.getExactValue() + " " + a.getUnit();\r
+                       else\r
+                                text = a.doubleValue(a.getUnit()) + " " + a.getUnit();\r
+                       \r
+                       setAsText(text);\r
+               }\r
+               @Override public void setAsText(String text) throws IllegalArgumentException {\r
+                       editor.setText(text);\r
+               };\r
+               @Override\r
+               public Component getCustomEditor(){\r
+                       return editor;\r
+               }\r
+       }\r
+\r
+\r
+\r
+}\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/GrainPanel.java b/src/com/billkuker/rocketry/motorsim/visual/GrainPanel.java
new file mode 100644 (file)
index 0000000..035c187
--- /dev/null
@@ -0,0 +1,211 @@
+package com.billkuker.rocketry.motorsim.visual;\r
+\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.geom.AffineTransform;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.text.NumberFormat;\r
+\r
+import javax.measure.quantity.Area;\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.MotorPart;\r
+\r
+public class GrainPanel extends JPanel {\r
+       private static final long serialVersionUID = 1L;\r
+       private Amount<Length> displayedRegression = Amount.valueOf(0, SI.MILLIMETER);\r
+       private JLabel l = new JLabel();\r
+       private Chart<Length,Area> area;\r
+       Chart<Length, Volume> volume;\r
+       private XC xc;\r
+       private SL sl;\r
+       private Grain grain;\r
+       \r
+       public GrainPanel(Grain g){\r
+               super(new BorderLayout());\r
+\r
+               grain = g;\r
+               \r
+               if ( g instanceof MotorPart ){\r
+                       ((MotorPart)g).addPropertyChangeListener(new PropertyChangeListener(){\r
+                               @Override\r
+                               public void propertyChange(PropertyChangeEvent evt) {\r
+                                       repaint();\r
+                                       area.setDomain(area.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
+                                       volume.setDomain(volume.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
+                               }\r
+                       });\r
+               }\r
+\r
+\r
+               try {\r
+\r
+                       area = new Chart<Length, Area>(\r
+                                       SI.MILLIMETER,\r
+                                       SI.MILLIMETER.pow(2).asType(Area.class),\r
+                                       grain,\r
+                                       "surfaceArea");\r
+                       area.setDomain(area.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
+                       \r
+                       volume = new Chart<Length, Volume>(\r
+                                       SI.MILLIMETER,\r
+                                       SI.MILLIMETER.pow(3).asType(Volume.class),\r
+                                       grain,\r
+                                       "volume");\r
+                       volume.setDomain(volume.new IntervalDomain(Amount.valueOf(0, SI.MILLIMETER), grain.webThickness()));\r
+\r
+                       area.setMaximumSize(new Dimension(200,100));\r
+                       volume.setMaximumSize(new Dimension(200,100));\r
+                       \r
+\r
+               } catch (ClassCastException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (NoSuchMethodException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+               \r
+               JSplitPane charts = new JSplitPane(JSplitPane.VERTICAL_SPLIT, area, volume);\r
+               charts.setDividerLocation(.5);\r
+               charts.setResizeWeight(.5);\r
+               \r
+               JPanel left = new JPanel(new BorderLayout());\r
+               \r
+               add(xc = new XC(grain), BorderLayout.CENTER);\r
+               left.add(xc);\r
+\r
+               left.add(l, BorderLayout.NORTH);\r
+               left.add( sl = new SL(), BorderLayout.SOUTH);\r
+       \r
+               add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, charts));\r
+\r
+       }\r
+       \r
+       public void setDisplayedRegression( Amount<Length> r ){\r
+               displayedRegression = r;\r
+               \r
+               NumberFormat nf = NumberFormat.getInstance();\r
+               nf.setMaximumFractionDigits(2);\r
+               l.setText("Regression: " + nf.format(displayedRegression.doubleValue(SI.MILLIMETER)) + "mm");\r
+               \r
+               area.mark(displayedRegression);\r
+               volume.mark(displayedRegression);\r
+               if ( xc != null )\r
+                       xc.repaint();\r
+       }\r
+       \r
+       private class XC extends JPanel{\r
+               private static final long serialVersionUID = 1L;\r
+               Grain grain;\r
+               public XC(Grain g){\r
+                       setMinimumSize(new Dimension(440,250));\r
+                       grain = g;\r
+               }\r
+               public void paint(Graphics g){\r
+                       super.paint(g);\r
+                       Graphics2D g2d = (Graphics2D)g;\r
+                       g2d.translate(10, 30);\r
+                       /*\r
+                       grain.draw(g2d, displayedRegression );\r
+                       */\r
+                       \r
+                       {\r
+                               AffineTransform t = g2d.getTransform();\r
+                               java.awt.geom.Area unburnt = grain.getCrossSection(Amount.valueOf(0, SI.MILLIMETER));\r
+                               \r
+                               Rectangle bounds = unburnt.getBounds();\r
+                               g2d.scale(200 / bounds.getWidth(), 200 / bounds.getHeight());\r
+                               g2d.translate(-bounds.getX(), -bounds.getY());\r
+       \r
+                               //Draw the fuel that is left\r
+                               java.awt.geom.Area burning = grain.getCrossSection(displayedRegression);\r
+                               g2d.setColor(Color.RED);\r
+                               g2d.fill(burning);\r
+                               //Draw the fuel that is left\r
+                               java.awt.geom.Area left = grain.getCrossSection(displayedRegression.plus(grain.webThickness().divide(30)));\r
+                               g2d.setColor(Color.GRAY);\r
+                               g2d.fill(left);\r
+                               //Draw the outline of the unburnt grain\r
+                               g2d.setColor(Color.BLACK);\r
+                               g2d.draw(unburnt);\r
+                               //untranslate\r
+                               g2d.setTransform(t);\r
+                       }\r
+                       {\r
+                               AffineTransform t = g2d.getTransform();\r
+                               java.awt.geom.Area unburnt = grain.getSideView(Amount.valueOf(0, SI.MILLIMETER));\r
+                               \r
+                               Rectangle bounds = unburnt.getBounds();\r
+                               g2d.translate(220, 0);\r
+                               \r
+                               double max = bounds.getWidth();\r
+                               if ( bounds.getHeight() > max )\r
+                                       max = bounds.getHeight();\r
+                               \r
+                               g2d.scale(200 / max, 200 / max);\r
+                               g2d.translate(-bounds.getX(), -bounds.getY());\r
+       \r
+                               //Draw the fuel that is left\r
+                               java.awt.geom.Area burning = grain.getSideView(displayedRegression);\r
+                               g2d.setColor(Color.RED);\r
+                               g2d.fill(burning);\r
+                               //Draw the fuel that is left\r
+                               java.awt.geom.Area left = grain.getSideView(displayedRegression.plus(grain.webThickness().divide(30)));\r
+                               g2d.setColor(Color.GRAY);\r
+                               g2d.fill(left);\r
+                               //Draw the outline of the unburnt grain\r
+                               g2d.setColor(Color.BLACK);\r
+                               g2d.draw(unburnt);\r
+                               //untranslate\r
+                               g2d.setTransform(t);\r
+                       }\r
+                       \r
+               }\r
+       }\r
+       \r
+       private class SL extends JSlider implements ChangeListener{\r
+               private static final long serialVersionUID = 1L;\r
+               private static final int STEPS = 60;\r
+               public SL(){\r
+                       addChangeListener(this);\r
+                       setMinimum(0);\r
+                       setMaximum(STEPS);\r
+                       setValue(0);\r
+               }\r
+               \r
+               @Override\r
+               public void stateChanged(ChangeEvent e) {\r
+                       double r = ((SL)e.getSource()).getValue();\r
+\r
+                       setDisplayedRegression(grain.webThickness().divide(STEPS).times(r));\r
+               }\r
+       }\r
+       \r
+       public void show(){\r
+               JFrame f = new JFrame();\r
+               f.setSize(1024,600);\r
+               f.setContentPane(this);\r
+               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
+               f.setVisible(true);\r
+       }\r
+\r
+}\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/NozzlePanel.java b/src/com/billkuker/rocketry/motorsim/visual/NozzlePanel.java
new file mode 100644 (file)
index 0000000..8920a65
--- /dev/null
@@ -0,0 +1,79 @@
+package com.billkuker.rocketry.motorsim.visual;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.Rectangle;\r
+import java.awt.Shape;\r
+import java.awt.Stroke;\r
+import java.awt.geom.Area;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+\r
+import javax.measure.unit.SI;\r
+import javax.swing.JFrame;\r
+import javax.swing.JPanel;\r
+import javax.swing.text.EditorKit;\r
+\r
+import org.jscience.physics.amount.Amount;\r
+\r
+import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;\r
+import com.billkuker.rocketry.motorsim.MotorPart;\r
+import com.billkuker.rocketry.motorsim.Nozzle;\r
+import com.billkuker.rocketry.motorsim.visual.Chart.IntervalDomain;\r
+\r
+public class NozzlePanel extends JPanel {\r
+       private Nozzle nozzle;\r
+       public NozzlePanel(Nozzle n){\r
+               nozzle = n;\r
+               \r
+               if ( n instanceof MotorPart ){\r
+                       ((MotorPart)n).addPropertyChangeListener(new PropertyChangeListener(){\r
+                               @Override\r
+                               public void propertyChange(PropertyChangeEvent evt) {\r
+                                       repaint();\r
+                               }\r
+                       });\r
+               }\r
+       }\r
+       \r
+       public void paint(Graphics g){\r
+               super.paint(g);\r
+               Graphics2D g2d = (Graphics2D)g;\r
+               g2d.translate(10, 10);\r
+\r
+               g2d.setColor(Color.black);\r
+               \r
+               \r
+               Shape a = nozzle.nozzleShape(Amount.valueOf(30, SI.MILLIMETER));\r
+               \r
+               Rectangle bounds = a.getBounds();\r
+               double max = bounds.getWidth();\r
+               if ( bounds.getHeight() > max )\r
+                       max = bounds.getHeight();\r
+               \r
+               g2d.scale(200 / max, 200 / max);\r
+               g2d.translate(-bounds.getX(), -bounds.getY());\r
+               \r
+               g2d.setStroke(new BasicStroke(4*(float)max/200f));\r
+               \r
+               g2d.draw( a );\r
+       }\r
+       \r
+       public void show(){\r
+               JFrame f = new JFrame();\r
+               f.setSize(220,250);\r
+               f.setContentPane(this);\r
+               f.setDefaultCloseOperation(f.DISPOSE_ON_CLOSE);\r
+               f.setVisible(true);\r
+       }\r
+       \r
+       public static void main(String args[]) throws Exception{\r
+               ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();\r
+               n.setThroatDiameter(Amount.valueOf(5, SI.MILLIMETER));\r
+               n.setExitDiameter(Amount.valueOf(9, SI.MILLIMETER));\r
+               new Editor(n).show();\r
+               new NozzlePanel(n).show();\r
+       }\r
+}\r