From: Bill Kuker Date: Sat, 1 May 2010 14:34:14 +0000 (+0000) Subject: SR Fuel Editor, not integrated with motors X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=a397314fc48361e6c0cf120419be0f60b986a401;p=sw%2Fmotorsim SR Fuel Editor, not integrated with motors --- diff --git a/src/com/billkuker/rocketry/motorsim/GraphSimplifier.java b/src/com/billkuker/rocketry/motorsim/GraphSimplifier.java index 921a36d..b9dde99 100644 --- a/src/com/billkuker/rocketry/motorsim/GraphSimplifier.java +++ b/src/com/billkuker/rocketry/motorsim/GraphSimplifier.java @@ -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 { Amount y; } - private Map, Amount> out = new HashMap, Amount>(); - private List> outDomain = new Vector>(); + private class DDEntry implements Comparable { + Amount x; + Amount dd; + + @Override + public int compareTo(DDEntry o) { + return dd.compareTo(o.dd); + } + } + + private SortedMap, Amount> out = new TreeMap, Amount>(); public Amount value(Amount x) { return out.get(x); } public Iterable> getDomain() { - return outDomain; + return out.keySet(); } public GraphSimplifier(Object source, String method, @@ -45,7 +57,6 @@ public class GraphSimplifier { Vector oldEntries = new Vector(); - Amount max = null, min = null, range = null, err = null; while (domain.hasNext()) { Amount x = domain.next(); Amount y = (Amount) f.invoke(source, x); @@ -53,54 +64,43 @@ public class GraphSimplifier { 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 newEntries = new Vector(); - 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 dx = high.x.minus(low.x); - Amount dy = high.y.minus(low.y); + List byDD = new Vector(); + Map, Amount> byX = new HashMap, Amount>(); - Amount ddx = middle.x.minus(low.x); - Amount 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 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 { diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/MotorWorkbench.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/MotorWorkbench.java index 8886f5e..f256c51 100644 --- a/src/com/billkuker/rocketry/motorsim/visual/workbench/MotorWorkbench.java +++ b/src/com/billkuker/rocketry/motorsim/visual/workbench/MotorWorkbench.java @@ -1,10 +1,13 @@ package com.billkuker.rocketry.motorsim.visual.workbench; import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.File; import java.util.HashMap; import java.util.Vector; @@ -35,6 +38,7 @@ import com.billkuker.rocketry.motorsim.Motor; import com.billkuker.rocketry.motorsim.RocketScience.UnitPreference; import com.billkuker.rocketry.motorsim.io.ENGExporter; import com.billkuker.rocketry.motorsim.io.MotorIO; +import com.billkuker.rocketry.motorsim.visual.workbench.WorkbenchTreeModel.FuelEditNode; public class MotorWorkbench extends JFrame implements TreeSelectionListener { private static final long serialVersionUID = 1L; @@ -98,7 +102,7 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { private static final long serialVersionUID = 1L; { - add(new JMenuItem("New") { + add(new JMenuItem("New Motor") { private static final long serialVersionUID = 1L; { addActionListener(new ActionListener() { @@ -142,7 +146,10 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { }); } }); - add(new JMenuItem("Close") { + + add(new JSeparator()); + + add(new JMenuItem("Close Motor") { private static final long serialVersionUID = 1L; { addActionListener(new ActionListener() { @@ -160,8 +167,8 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { }); } }); - add(new JSeparator()); - add(new JMenuItem("Save") { + + add(new JMenuItem("Save Motor") { private static final long serialVersionUID = 1L; { addActionListener(new ActionListener() { @@ -177,7 +184,7 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { }); } }); - add(new JMenuItem("Save As...") { + add(new JMenuItem("Save Motor As...") { private static final long serialVersionUID = 1L; { addActionListener(new ActionListener() { @@ -208,6 +215,22 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { }); } }); + + + add(new JSeparator()); + add(new JMenuItem("New Fuel") { + private static final long serialVersionUID = 1L; + { + addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + addFuel(); + } + }); + + } + }); + add(new JMenuItem("Save Fuel") {}); add(new JSeparator()); add(new JMenuItem("Export .ENG"){ private static final long serialVersionUID = 1L; @@ -265,6 +288,26 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { } }); } + + private void addFuel(){ + final SRFuelEditor ed = new SRFuelEditor(); + final FuelEditNode node = tm.new FuelEditNode(ed); + tm.getFuels().add(node); + tm.nodeStructureChanged(tm.getFuels()); + motors.addTab(ed.getFuel().getName(), ed); + ed.getFuel().addPropertyChangeListener(new PropertyChangeListener(){ + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ( evt.getPropertyName().equals("Name")){ + for ( int i = 0; i < motors.getTabCount(); i++ ){ + if ( motors.getComponent(i) == ed ){ + motors.setTitleAt(i, ed.getFuel().getName()); + tm.nodeChanged(node); + } + } + } + }}); + } private void save(Motor m, File f) { try { @@ -292,11 +335,19 @@ public class MotorWorkbench extends JFrame implements TreeSelectionListener { @Override public void valueChanged(TreeSelectionEvent e) { - if ( e.getPath().getLastPathComponent() == tm.getRoot() ){ + if ( e.getPath().getLastPathComponent() == tm.getMotors() ){ allBurns.setVisible(true); allBurns.toFront(); } + if ( e.getPath().getLastPathComponent() instanceof FuelEditNode ){ + FuelEditNode fen = ((FuelEditNode)e.getPath().getLastPathComponent()); + SRFuelEditor ed = fen.getUserObject(); + for ( int i = 0 ; i < motors.getTabCount(); i++ ){ + motors.setSelectedComponent(ed); + } + } + Motor m = getMotor(e.getPath()); if ( m == null ) 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 index 0000000..07b94a1 --- /dev/null +++ b/src/com/billkuker/rocketry/motorsim/visual/workbench/SRFuelEditor.java @@ -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 burnRate; + + private class Entry implements Comparable { + Amount 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 idealDensity = (Amount) 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 p, final double _a, final double _n) { + super.add(p, _a, _n); + + } + + public Amount getIdealDensity() { + return idealDensity; + } + + public void setIdealDensity(Amount 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) Amount.valueOf((String) value); + } catch ( Exception ee ){ + double d = Double.parseDouble((String)value); + e.p = (Amount)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( + 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 entries = new Vector(); + + 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); + + } + +} diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java index 03d85c3..e1b4355 100644 --- a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java +++ b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java @@ -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 ) { diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java index db6b523..96999f7 100644 --- a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java +++ b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java @@ -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 e = getRoot().children(); + Enumeration e = motors.children(); while ( e.hasMoreElements() ){ TreeNode n = e.nextElement(); if ( n instanceof MotorNode ){