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