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;
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,
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);
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 {
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
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
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
});\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
});\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
});\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
});\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
}\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
\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
--- /dev/null
+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);
+
+ }
+
+}
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 ) {
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;
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) {
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;
}
public WorkbenchTreeModel() {
- super(new DefaultMutableTreeNode("All Motors"), true);
+ super(new DefaultMutableTreeNode("Root"), true);
+ getRoot().add(motors);
+ getRoot().add(fuel);
}
@Override
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 ){