SR Fuel Editor, not integrated with motors
authorBill Kuker <bkuker@billkuker.com>
Sat, 1 May 2010 14:34:14 +0000 (14:34 +0000)
committerBill Kuker <bkuker@billkuker.com>
Sat, 1 May 2010 14:34:14 +0000 (14:34 +0000)
src/com/billkuker/rocketry/motorsim/GraphSimplifier.java
src/com/billkuker/rocketry/motorsim/visual/workbench/MotorWorkbench.java
src/com/billkuker/rocketry/motorsim/visual/workbench/SRFuelEditor.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java
src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java

index 921a36ddaa4d81f6298d819b6ca3394be7f72be6..b9dde99a0bebca9132e71e67bea25ef1c77e2cb1 100644 (file)
@@ -2,10 +2,13 @@ package com.billkuker.rocketry.motorsim;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.Vector;
 
 import javax.measure.quantity.Area;
@@ -26,15 +29,24 @@ public class GraphSimplifier<X extends Quantity, Y extends Quantity> {
                Amount<Y> y;
        }
 
-       private Map<Amount<X>, Amount<Y>> out = new HashMap<Amount<X>, Amount<Y>>();
-       private List<Amount<X>> outDomain = new Vector<Amount<X>>();
+       private class DDEntry implements Comparable<DDEntry> {
+               Amount<X> x;
+               Amount dd;
+
+               @Override
+               public int compareTo(DDEntry o) {
+                       return dd.compareTo(o.dd);
+               }
+       }
+
+       private SortedMap<Amount<X>, Amount<Y>> out = new TreeMap<Amount<X>, Amount<Y>>();
 
        public Amount<Y> value(Amount<X> x) {
                return out.get(x);
        }
 
        public Iterable<Amount<X>> getDomain() {
-               return outDomain;
+               return out.keySet();
        }
 
        public GraphSimplifier(Object source, String method,
@@ -45,7 +57,6 @@ public class GraphSimplifier<X extends Quantity, Y extends Quantity> {
 
                Vector<Entry> oldEntries = new Vector<Entry>();
 
-               Amount<Y> max = null, min = null, range = null, err = null;
                while (domain.hasNext()) {
                        Amount<X> x = domain.next();
                        Amount<Y> y = (Amount<Y>) f.invoke(source, x);
@@ -53,54 +64,43 @@ public class GraphSimplifier<X extends Quantity, Y extends Quantity> {
                        e.x = x;
                        e.y = y;
                        oldEntries.add(e);
-
-                       if (max == null || max.isLessThan(y))
-                               max = y;
-                       if (min == null || min.isGreaterThan(y))
-                               min = y;
                }
-               range = max.minus(min).abs();
-               err = range.divide(50);
-
-               for (int j = 0; j < 10 && oldEntries.size() > 30; j++) {
-                       Vector<Entry> newEntries = new Vector<Entry>();
-                       newEntries.add(oldEntries.firstElement());
-                       for (int i = 1; i < oldEntries.size() - 1; i = i + 2) {
-                               Entry low = oldEntries.elementAt(i - 1);
-                               Entry middle = oldEntries.elementAt(i);
-                               Entry high = oldEntries.elementAt(i + 1);
 
-                               Amount<X> dx = high.x.minus(low.x);
-                               Amount<Y> dy = high.y.minus(low.y);
+               List<DDEntry> byDD = new Vector<DDEntry>();
+               Map<Amount<X>, Amount<Y>> byX = new HashMap<Amount<X>, Amount<Y>>();
 
-                               Amount<X> ddx = middle.x.minus(low.x);
-                               Amount<Y> lin = low.y.plus(dy.times(ddx.divide(dx)));
+               for (int i = 1; i < oldEntries.size() - 1; i++) {
+                       Entry low = oldEntries.elementAt(i - 1);
+                       Entry middle = oldEntries.elementAt(i);
+                       Entry high = oldEntries.elementAt(i + 1);
 
-                               Amount<Y> dif = middle.y.minus(lin).abs();
+                       Amount<?> d1, d2, dd;
 
-                               // System.out.println(middle.x + "\t" + middle.y + "\t" + lin +
-                               // "\t" + dif + "\t"
-                               // + dy);
+                       d1 = middle.y.minus(low.y).divide(middle.x.minus(low.x));
+                       d2 = high.y.minus(middle.y).divide(high.x.minus(middle.x));
+                       dd = d2.minus(d1).divide(high.x.minus(low.x));
 
-                               if (dif.isGreaterThan(err)) {
-                                       newEntries.add(middle);
-                               }
-                               newEntries.add(high);
-                       }
-                       newEntries.add(oldEntries.lastElement());
+                       DDEntry dde = new DDEntry();
+                       dde.dd = dd.abs();
+                       dde.x = middle.x;
 
-                       System.out.println("Size " + oldEntries.size() + " -> "
-                                       + newEntries.size());
+                       byDD.add(dde);
+                       byX.put(middle.x, middle.y);
 
-                       oldEntries = newEntries;
                }
 
-               for (Entry ee : oldEntries) {
-                       if (out.get(ee.x) == null) {
-                               out.put(ee.x, ee.y);
-                               outDomain.add(ee.x);
-                       }
+               Collections.sort(byDD);
+
+               out.put(oldEntries.elementAt(0).x, oldEntries.elementAt(0).y);
+               int count = 0;
+               for (DDEntry dde : byDD) {
+                       out.put(dde.x, byX.get(dde.x));
+                       if (++count >= 20)
+                               break;
                }
+               int last = oldEntries.size() - 1;
+               out.put(oldEntries.elementAt(last).x, oldEntries.elementAt(last).y);
+
        }
 
        public static void main(String args[]) throws Exception {
index 8886f5e1b6250454c33c10517b79e7f013029126..f256c51879ca451b8add1442fa41c72fde1e246e 100644 (file)
@@ -1,10 +1,13 @@
 package com.billkuker.rocketry.motorsim.visual.workbench;\r
 \r
 import java.awt.BorderLayout;\r
+import java.awt.Component;\r
 import java.awt.Dimension;\r
 import java.awt.FileDialog;\r
 import java.awt.event.ActionEvent;\r
 import java.awt.event.ActionListener;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
 import java.io.File;\r
 import java.util.HashMap;\r
 import java.util.Vector;\r
@@ -35,6 +38,7 @@ import com.billkuker.rocketry.motorsim.Motor;
 import com.billkuker.rocketry.motorsim.RocketScience.UnitPreference;\r
 import com.billkuker.rocketry.motorsim.io.ENGExporter;\r
 import com.billkuker.rocketry.motorsim.io.MotorIO;\r
+import com.billkuker.rocketry.motorsim.visual.workbench.WorkbenchTreeModel.FuelEditNode;\r
 \r
 public class MotorWorkbench extends JFrame implements TreeSelectionListener {\r
        private static final long serialVersionUID = 1L;\r
@@ -98,7 +102,7 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                                        private static final long serialVersionUID = 1L;\r
 \r
                                        {\r
-                                               add(new JMenuItem("New") {\r
+                                               add(new JMenuItem("New Motor") {\r
                                                        private static final long serialVersionUID = 1L;\r
                                                        {\r
                                                                addActionListener(new ActionListener() {\r
@@ -142,7 +146,10 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                                                                });\r
                                                        }\r
                                                });\r
-                                               add(new JMenuItem("Close") {\r
+                                               \r
+                                               add(new JSeparator());\r
+                                               \r
+                                               add(new JMenuItem("Close Motor") {\r
                                                        private static final long serialVersionUID = 1L;\r
                                                        {\r
                                                                addActionListener(new ActionListener() {\r
@@ -160,8 +167,8 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                                                                });\r
                                                        }\r
                                                });\r
-                                               add(new JSeparator());\r
-                                               add(new JMenuItem("Save") {\r
+                                               \r
+                                               add(new JMenuItem("Save Motor") {\r
                                                        private static final long serialVersionUID = 1L;\r
                                                        {\r
                                                                addActionListener(new ActionListener() {\r
@@ -177,7 +184,7 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                                                                });\r
                                                        }\r
                                                });\r
-                                               add(new JMenuItem("Save As...") {\r
+                                               add(new JMenuItem("Save Motor As...") {\r
                                                        private static final long serialVersionUID = 1L;\r
                                                        {\r
                                                                addActionListener(new ActionListener() {\r
@@ -208,6 +215,22 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                                                                });\r
                                                        }\r
                                                });\r
+                                               \r
+\r
+                                               add(new JSeparator());\r
+                                               add(new JMenuItem("New Fuel") {\r
+                                                       private static final long serialVersionUID = 1L;\r
+                                                       {\r
+                                                               addActionListener(new ActionListener() {\r
+                                                                       @Override\r
+                                                                       public void actionPerformed(ActionEvent arg0) {\r
+                                                                               addFuel();\r
+                                                                       }\r
+                                                               });\r
+\r
+                                                       }\r
+                                               });\r
+                                               add(new JMenuItem("Save Fuel") {});\r
                                                add(new JSeparator());\r
                                                add(new JMenuItem("Export .ENG"){\r
                                                        private static final long serialVersionUID = 1L;\r
@@ -265,6 +288,26 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
                        }\r
                });\r
        }\r
+       \r
+       private void addFuel(){\r
+               final SRFuelEditor ed = new SRFuelEditor();\r
+               final FuelEditNode node = tm.new FuelEditNode(ed);\r
+               tm.getFuels().add(node);\r
+               tm.nodeStructureChanged(tm.getFuels());\r
+               motors.addTab(ed.getFuel().getName(), ed);\r
+               ed.getFuel().addPropertyChangeListener(new PropertyChangeListener(){\r
+                       @Override\r
+                       public void propertyChange(PropertyChangeEvent evt) {\r
+                               if ( evt.getPropertyName().equals("Name")){\r
+                                       for ( int i = 0; i < motors.getTabCount(); i++ ){\r
+                                               if ( motors.getComponent(i) == ed ){\r
+                                                       motors.setTitleAt(i, ed.getFuel().getName());\r
+                                                       tm.nodeChanged(node);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }});\r
+       }\r
 \r
        private void save(Motor m, File f) {\r
                try {\r
@@ -292,11 +335,19 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener {
 \r
        @Override\r
        public void valueChanged(TreeSelectionEvent e) {\r
-               if ( e.getPath().getLastPathComponent() == tm.getRoot() ){\r
+               if ( e.getPath().getLastPathComponent() == tm.getMotors() ){\r
                        allBurns.setVisible(true);\r
                        allBurns.toFront();\r
                }\r
                \r
+               if ( e.getPath().getLastPathComponent() instanceof FuelEditNode ){\r
+                       FuelEditNode fen = ((FuelEditNode)e.getPath().getLastPathComponent());\r
+                       SRFuelEditor ed = fen.getUserObject();\r
+                       for ( int i = 0 ; i < motors.getTabCount(); i++ ){\r
+                               motors.setSelectedComponent(ed);\r
+                       }\r
+               }\r
+               \r
                Motor m = getMotor(e.getPath());\r
                \r
                if ( m == null )\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/SRFuelEditor.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/SRFuelEditor.java
new file mode 100644 (file)
index 0000000..07b94a1
--- /dev/null
@@ -0,0 +1,337 @@
+package com.billkuker.rocketry.motorsim.visual.workbench;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.TextListener;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Collections;
+import java.util.Vector;
+
+import javax.measure.quantity.Pressure;
+import javax.measure.quantity.Velocity;
+import javax.measure.quantity.VolumetricDensity;
+import javax.measure.unit.SI;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableModel;
+
+import org.jscience.physics.amount.Amount;
+
+import com.billkuker.rocketry.motorsim.Fuel;
+import com.billkuker.rocketry.motorsim.RocketScience;
+import com.billkuker.rocketry.motorsim.RocketScience.UnitPreference;
+import com.billkuker.rocketry.motorsim.fuel.PiecewiseSaintRobertFuel;
+import com.billkuker.rocketry.motorsim.fuel.SaintRobertFuel;
+import com.billkuker.rocketry.motorsim.fuel.EditableFuel.EditableCombustionProduct;
+import com.billkuker.rocketry.motorsim.fuel.SaintRobertFuel.Type;
+import com.billkuker.rocketry.motorsim.visual.Chart;
+import com.billkuker.rocketry.motorsim.visual.Editor;
+import com.billkuker.rocketry.motorsim.visual.Chart.IntervalDomain;
+
+public class SRFuelEditor extends JSplitPane {
+       private static final NumberFormat nf = new DecimalFormat("##########.###");
+
+       Chart<Pressure, Velocity> burnRate;
+
+       private class Entry implements Comparable<Entry> {
+               Amount<Pressure> p = Amount.valueOf(0, RocketScience.UnitPreference.preference.getPreferredUnit(RocketScience.PSI));
+               double a;
+               double n;
+
+               @Override
+               public int compareTo(Entry o) {
+                       return p.compareTo(o.p);
+               }
+       }
+
+       public class EditablePSRFuel extends PiecewiseSaintRobertFuel {
+
+               private Amount<VolumetricDensity> idealDensity = (Amount<VolumetricDensity>) Amount
+                               .valueOf("1 g/mm^3");
+               private double combustionEfficiency = 1;
+               private double densityRatio = 1;
+               private EditableCombustionProduct cp;
+               private String name = "New Fuel";
+
+               public EditablePSRFuel(Type t) {
+                       super(t);
+                       cp = new EditableCombustionProduct();
+               }
+               
+               public void setType(Type t){
+                       super.setType(t);
+               }
+
+               public void add(Amount<Pressure> p, final double _a, final double _n) {
+                       super.add(p, _a, _n);
+
+               }
+
+               public Amount<VolumetricDensity> getIdealDensity() {
+                       return idealDensity;
+               }
+
+               public void setIdealDensity(Amount<VolumetricDensity> idealDensity) {
+                       this.idealDensity = idealDensity;
+               }
+
+               public double getCombustionEfficiency() {
+                       return combustionEfficiency;
+               }
+
+               public void setCombustionEfficiency(double combustionEfficiency) {
+                       this.combustionEfficiency = combustionEfficiency;
+               }
+
+               public double getDensityRatio() {
+                       return densityRatio;
+               }
+
+               public void setDensityRatio(double densityRatio) {
+                       this.densityRatio = densityRatio;
+               }
+
+               @Override
+               public CombustionProduct getCombustionProduct() {
+                       return cp;
+               }
+
+               public String getName() {
+                       return name;
+               }
+
+               public void setName(String name) {
+                       this.name = name;
+               }
+
+       }
+
+       EditablePSRFuel f = new EditablePSRFuel(SaintRobertFuel.Type.SI);
+
+       private class TM extends AbstractTableModel {
+
+               @Override
+               public int getColumnCount() {
+                       return 3;
+               }
+
+               @Override
+               public int getRowCount() {
+                       return entries.size();
+               }
+
+               @Override
+               public String getColumnName(int col) {
+                       switch (col) {
+                       case 0:
+                               return "Pressure";
+                       case 1:
+                               return "Coefficient (a)";
+                       case 2:
+                               return "Exponent (n)";
+                       }
+                       return null;
+               }
+
+               @Override
+               public Object getValueAt(int rowIndex, int columnIndex) {
+                       Entry e = entries.get(rowIndex);
+                       switch (columnIndex) {
+                       case 0:
+                               //Format like 100 psi or 4.8 Mpa
+                               return nf.format(e.p.doubleValue(e.p.getUnit())) + " " + e.p.getUnit();
+                       case 1:
+                               return e.a;
+                       case 2:
+                               return e.n;
+                       }
+                       return null;
+               }
+
+               public boolean isCellEditable(int row, int col) {
+                       return true;
+               }
+
+               public void setValueAt(Object value, int row, int col) {
+                       Entry e = entries.get(row);
+                       try {
+                               switch (col) {
+                               case 0:
+                                       try {
+                                               e.p = (Amount<Pressure>) Amount.valueOf((String) value);
+                                       } catch ( Exception ee ){
+                                               double d = Double.parseDouble((String)value);
+                                               e.p = (Amount<Pressure>)Amount.valueOf(d, e.p.getUnit());
+                                       }
+                                       break;
+                               case 1:
+                                       e.a = Double.valueOf((String) value);
+                                       break;
+                               case 2:
+                                       e.n = Double.valueOf((String) value);
+                               }
+                       } catch (Exception ex) {
+                               ex.printStackTrace();
+                       }
+                       Collections.sort(entries);
+                       fireTableDataChanged();
+                       f = new EditablePSRFuel(SaintRobertFuel.Type.NONSI);
+                       for (Entry en : entries) {
+                               f.add(en.p, en.a, en.n);
+                       }
+
+                       update();
+
+               }
+
+               @Override
+               public void fireTableDataChanged() {
+                       super.fireTableDataChanged();
+               }
+
+       };
+       
+       public Fuel getFuel(){
+               return f;
+       }
+
+       private void update() {
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @Override
+                       public void run() {
+                               editTop.setTopComponent(new Editor(f));
+                               editTop.setBottomComponent(new Editor(f.getCombustionProduct()));
+                               if (burnRate != null)
+                                       SRFuelEditor.this.remove(burnRate);
+                               try {
+                                       burnRate = new Chart<Pressure, Velocity>(
+                                                       SI.MEGA(SI.PASCAL), SI.MILLIMETER.divide(SI.SECOND)
+                                                                       .asType(Velocity.class), f, "burnRate");
+                               } catch (NoSuchMethodException e) {
+                                       throw new Error(e);
+                               }
+                               burnRate.setDomain(burnRate.new IntervalDomain(Amount.valueOf(
+                                               0, SI.MEGA(SI.PASCAL)), Amount.valueOf(11, SI
+                                               .MEGA(SI.PASCAL)), 50));
+                               SRFuelEditor.this.setRightComponent(burnRate);
+                               SRFuelEditor.this.revalidate();
+                       }
+               });
+       }
+
+       private Vector<Entry> entries = new Vector<Entry>();
+
+       JSplitPane editParent;
+       JSplitPane editTop;
+       JSplitPane editBottom;
+       JPanel controls;
+
+       public SRFuelEditor() {
+               super(HORIZONTAL_SPLIT);
+               setResizeWeight(0);
+               setDividerLocation(.3);
+               
+               final TM tm = new TM();
+
+               editParent = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+               editTop = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+               editBottom = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+               
+               editParent.setTopComponent(editTop);
+               editParent.setBottomComponent(editBottom);
+               
+               editTop.setTopComponent(new Editor(f));
+
+               JTable table = new JTable(tm);
+               JScrollPane scrollpane = new JScrollPane(table);
+               scrollpane.setMinimumSize(new Dimension(200, 200));
+               editBottom.setTopComponent(scrollpane);
+
+               setLeftComponent(editParent);
+
+               JButton add = new JButton("Add Data");
+               add.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               entries.add(new Entry());
+                               tm.fireTableDataChanged();
+                       }
+               });
+               controls = new JPanel();
+               controls.setPreferredSize(new Dimension(200, 50));
+               controls.setLayout(new FlowLayout());
+                       
+               controls.add(add);
+
+               
+               final JRadioButton si, nonsi;
+               ButtonGroup type = new ButtonGroup();
+               JPanel radio = new JPanel();
+               radio.add(si = new JRadioButton("SI"));
+               radio.add(nonsi = new JRadioButton("NonSI"));
+               controls.add(radio);
+               type.add(si);
+               type.add(nonsi);
+
+               si.setSelected(true);
+               
+               si.addChangeListener(new ChangeListener(){
+                       @Override
+                       public void stateChanged(ChangeEvent e) {
+                               if ( si.isSelected() ){
+                                       System.err.println("SI");
+                                       f.setType(Type.SI);
+                                       RocketScience.UnitPreference.preference = UnitPreference.SI;
+                               } else {
+                                       System.err.println("NONSI");
+                                       f.setType(Type.NONSI);
+                                       RocketScience.UnitPreference.preference = UnitPreference.NONSI;
+                               }
+                               update();
+                       }});
+               
+               editBottom.setBottomComponent(controls);
+               
+               
+               editParent.setDividerLocation(.5);
+               editTop.setDividerLocation(.5);
+               editBottom.setDividerLocation(.8);
+               
+               editParent.resetToPreferredSizes();
+               revalidate();
+
+               update();
+       }
+
+       public static void main(String args[]) {
+               SRFuelEditor ed;
+               JFrame f = new JFrame();
+               f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+               f.setContentPane(ed = new SRFuelEditor());
+               f.setSize(800, 600);
+               f.setVisible(true);
+
+       }
+
+}
index 03d85c3dfcc4d814f6e4f708be8af6567fa11151..e1b43553de6d74bd787a96f30f6b89ecd39d3cd3 100644 (file)
@@ -44,6 +44,8 @@ public class WorkbenchTreeCellRenderer extends DefaultTreeCellRenderer {
 
                if (part instanceof Motor) {
                        setText(((Motor) part).getName());
+               } else if ( part instanceof SRFuelEditor ){
+                       setText(((SRFuelEditor)part).getFuel().getName());
                } else if ( part instanceof String ) {
                        setText((String)part);
                } else if ( part == null ) {
index db6b523c5e1c272e29adb46c10476b5b22bb3ff1..96999f77de9352ab5544cafcd33397bdcc0f3c7e 100644 (file)
@@ -6,6 +6,7 @@ import java.util.Enumeration;
 
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.MutableTreeNode;
 import javax.swing.tree.TreeNode;
 
 import com.billkuker.rocketry.motorsim.ChangeListening;
@@ -15,7 +16,11 @@ import com.billkuker.rocketry.motorsim.grain.MultiGrain;
 public class WorkbenchTreeModel extends DefaultTreeModel {
 
        private static final long serialVersionUID = 1L;
-
+       
+       //TreeNode root = new DefaultMutableTreeNode("Root");
+       DefaultMutableTreeNode motors = new DefaultMutableTreeNode("All Motors");
+       DefaultMutableTreeNode fuel = new DefaultMutableTreeNode("Fuels");
+       
        public class MultiGrainNode extends PartNode{
                private static final long serialVersionUID = 1L;
                public MultiGrainNode(MultiGrain part) {
@@ -33,6 +38,25 @@ public class WorkbenchTreeModel extends DefaultTreeModel {
                        super.propertyChange(e);
                }
        }
+       
+       public class FuelEditNode extends DefaultMutableTreeNode {
+               private static final long serialVersionUID = 1L;
+
+               public FuelEditNode(SRFuelEditor sr){
+                       super(sr, false);
+                       sr.getFuel().addPropertyChangeListener(new PropertyChangeListener(){
+
+                               @Override
+                               public void propertyChange(PropertyChangeEvent evt) {
+                                       nodeChanged(FuelEditNode.this);
+                               }});
+               }
+               
+               @Override
+               public SRFuelEditor getUserObject(){
+                       return (SRFuelEditor)super.getUserObject();
+               }
+       }
 
        public class PartNode extends DefaultMutableTreeNode implements PropertyChangeListener {
                private static final long serialVersionUID = 1L;
@@ -95,7 +119,9 @@ public class WorkbenchTreeModel extends DefaultTreeModel {
        }
 
        public WorkbenchTreeModel() {
-               super(new DefaultMutableTreeNode("All Motors"), true);
+               super(new DefaultMutableTreeNode("Root"), true);
+               getRoot().add(motors);
+               getRoot().add(fuel);
        }
        
        @Override
@@ -103,16 +129,24 @@ public class WorkbenchTreeModel extends DefaultTreeModel {
                return (DefaultMutableTreeNode)super.getRoot();
        }
        
+       public DefaultMutableTreeNode getMotors(){
+               return motors;
+       }
+       
+       public DefaultMutableTreeNode getFuels(){
+               return fuel;
+       }
+       
        public void addMotor(Motor m){
                DefaultMutableTreeNode root = getRoot();
-               root.add(new MotorNode(m));
-               nodesWereInserted(root, new int[]{root.getChildCount()-1});
+               motors.add(new MotorNode(m));
+               nodesWereInserted(motors, new int[]{motors.getChildCount()-1});
                
        }
        
        @SuppressWarnings("unchecked")
        public void removeMotor(Motor m){
-               Enumeration<TreeNode> e = getRoot().children();
+               Enumeration<TreeNode> e = motors.children();
                while ( e.hasMoreElements() ){
                        TreeNode n = e.nextElement();
                        if ( n instanceof MotorNode ){