1 package com.billkuker.rocketry.motorsim.visual.workbench;
\r
3 import java.awt.BorderLayout;
\r
4 import java.awt.event.ActionEvent;
\r
5 import java.awt.event.ActionListener;
\r
6 import java.awt.event.FocusEvent;
\r
7 import java.awt.event.FocusListener;
\r
8 import java.beans.PropertyChangeEvent;
\r
9 import java.beans.PropertyChangeListener;
\r
10 import java.beans.PropertyVetoException;
\r
11 import java.io.IOException;
\r
13 import javax.measure.quantity.Pressure;
\r
14 import javax.measure.quantity.Velocity;
\r
15 import javax.measure.unit.SI;
\r
16 import javax.swing.BoxLayout;
\r
17 import javax.swing.JButton;
\r
18 import javax.swing.JFrame;
\r
19 import javax.swing.JPanel;
\r
20 import javax.swing.JProgressBar;
\r
21 import javax.swing.JSplitPane;
\r
22 import javax.swing.JTabbedPane;
\r
23 import javax.swing.JTextArea;
\r
24 import javax.swing.JTextField;
\r
25 import javax.swing.SwingUtilities;
\r
26 import javax.swing.UIManager;
\r
27 import javax.swing.WindowConstants;
\r
29 import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
\r
30 import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
\r
31 import org.jscience.physics.amount.Amount;
\r
33 import com.billkuker.rocketry.motorsim.Burn;
\r
34 import com.billkuker.rocketry.motorsim.Chamber;
\r
35 import com.billkuker.rocketry.motorsim.ChangeListening;
\r
36 import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;
\r
37 import com.billkuker.rocketry.motorsim.CylindricalChamber;
\r
38 import com.billkuker.rocketry.motorsim.Fuel;
\r
39 import com.billkuker.rocketry.motorsim.Grain;
\r
40 import com.billkuker.rocketry.motorsim.Motor;
\r
41 import com.billkuker.rocketry.motorsim.Nozzle;
\r
42 import com.billkuker.rocketry.motorsim.fuel.EditableFuel;
\r
43 import com.billkuker.rocketry.motorsim.fuel.KNDX;
\r
44 import com.billkuker.rocketry.motorsim.fuel.KNER;
\r
45 import com.billkuker.rocketry.motorsim.fuel.KNSB;
\r
46 import com.billkuker.rocketry.motorsim.fuel.KNSU;
\r
47 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;
\r
48 import com.billkuker.rocketry.motorsim.grain.Finocyl;
\r
49 import com.billkuker.rocketry.motorsim.grain.Moonburner;
\r
50 import com.billkuker.rocketry.motorsim.grain.MultiGrain;
\r
51 import com.billkuker.rocketry.motorsim.grain.RodAndTubeGrain;
\r
52 import com.billkuker.rocketry.motorsim.io.MotorIO;
\r
53 import com.billkuker.rocketry.motorsim.visual.BurnPanel;
\r
54 import com.billkuker.rocketry.motorsim.visual.Chart;
\r
55 import com.billkuker.rocketry.motorsim.visual.Editor;
\r
56 import com.billkuker.rocketry.motorsim.visual.GrainPanel;
\r
57 import com.billkuker.rocketry.motorsim.visual.NozzlePanel;
\r
59 public class MotorEditor extends JTabbedPane implements PropertyChangeListener {
\r
60 private static final long serialVersionUID = 1L;
\r
61 RSyntaxTextArea text = new RSyntaxTextArea();
\r
63 GrainEditor grainEditor;
\r
66 private static final int XML_TAB = 0;
\r
67 private static final int CASING_TAB = 1;
\r
68 private static final int GRAIN_TAB = 2;
\r
69 private static final int FUEL_TAB = 3;
\r
70 private static final int BURN_TAB = 4;
\r
72 @SuppressWarnings("unchecked")
\r
73 private Class[] grainTypes = { CoredCylindricalGrain.class, Finocyl.class,
\r
74 Moonburner.class, RodAndTubeGrain.class };
\r
76 @SuppressWarnings("unchecked")
\r
77 private Class[] fuelTypes = { KNSB.class, KNSU.class, KNER.class,
\r
78 KNDX.class, EditableFuel.class };
\r
80 private abstract class Chooser<T> extends JPanel {
\r
81 private static final long serialVersionUID = 1L;
\r
82 private Class<? extends T>[] types;
\r
84 public Chooser(Class<? extends T>... ts) {
\r
86 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
\r
87 for (final Class<? extends T> c : types) {
\r
88 JButton b = new JButton(c.getSimpleName());
\r
90 b.addActionListener(new ActionListener() {
\r
91 public void actionPerformed(ActionEvent e) {
\r
93 choiceMade(c.newInstance());
\r
94 } catch (InstantiationException e1) {
\r
95 e1.printStackTrace();
\r
96 } catch (IllegalAccessException e1) {
\r
97 e1.printStackTrace();
\r
104 protected abstract void choiceMade(T o);
\r
107 private class BurnTab extends JPanel {
\r
108 private static final long serialVersionUID = 1L;
\r
110 setLayout(new BorderLayout());
\r
115 public void reBurn() {
\r
118 public void run() {
\r
119 final JProgressBar bar = new JProgressBar(0,100);
\r
122 final Burn b = new Burn(motor, new Burn.BurnProgressListener(){
\r
124 public void setProgress(float f){
\r
125 bar.setValue((int)(f*100));
\r
129 final BurnPanel bp = new BurnPanel(b);
\r
130 SwingUtilities.invokeLater(new Thread() {
\r
131 public void run() {
\r
133 add(bp, BorderLayout.CENTER);
\r
138 } catch ( Exception e ){
\r
140 JTextArea t = new JTextArea(e.getMessage());
\r
141 t.setEditable(false);
\r
149 private class GrainEditor extends JSplitPane {
\r
150 private static final long serialVersionUID = 1L;
\r
152 public GrainEditor(final Grain g) {
\r
153 super(JSplitPane.HORIZONTAL_SPLIT);
\r
155 setRightComponent(new GrainPanel(g));
\r
156 if (g instanceof Grain.Composite) {
\r
157 final JPanel p = new JPanel();
\r
158 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
\r
159 p.add(new Editor(g));
\r
160 for (Grain gg : ((Grain.Composite) g).getGrains()) {
\r
161 final int grainEditorIndex = p.getComponentCount() + 1;
\r
162 p.add(new Chooser<Grain>(grainTypes) {
\r
163 private static final long serialVersionUID = 1L;
\r
166 protected void choiceMade(Grain ng) {
\r
167 if (g instanceof MultiGrain) {
\r
168 ((MultiGrain) g).setGrain(ng);
\r
169 p.remove(grainEditorIndex);
\r
170 p.add(new Editor(ng), grainEditorIndex);
\r
172 p.add(new Editor(g), 0);
\r
173 // System.out.println("Chose new grain");
\r
177 p.add(new Editor(gg));
\r
178 if (gg instanceof ChangeListening.Subject) {
\r
179 ((ChangeListening.Subject) gg)
\r
180 .addPropertyChangeListener(MotorEditor.this);
\r
183 setLeftComponent(p);
\r
185 setLeftComponent(new Editor(g));
\r
187 // setDividerLocation(.25);
\r
188 // setResizeWeight(.25);
\r
189 if (g instanceof ChangeListening.Subject) {
\r
190 ((ChangeListening.Subject) g).addPropertyChangeListener(MotorEditor.this);
\r
195 private class FuelEditor extends JSplitPane {
\r
196 private static final long serialVersionUID = 1L;
\r
198 public FuelEditor(Fuel f) {
\r
199 super(JSplitPane.HORIZONTAL_SPLIT);
\r
201 Chart<Pressure, Velocity> burnRate;
\r
203 burnRate = new Chart<Pressure, Velocity>(SI.MEGA(SI.PASCAL),
\r
204 SI.METERS_PER_SECOND, f, "burnRate");
\r
205 } catch (NoSuchMethodException e) {
\r
206 throw new Error(e);
\r
208 burnRate.setDomain(burnRate.new IntervalDomain(Amount.valueOf(0, SI
\r
209 .MEGA(SI.PASCAL)), Amount.valueOf(11, SI.MEGA(SI.PASCAL)),
\r
212 final JPanel p = new JPanel();
\r
213 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
\r
215 p.add(new Chooser<Fuel>(fuelTypes) {
\r
216 private static final long serialVersionUID = 1L;
\r
218 protected void choiceMade(Fuel o) {
\r
220 removeTabAt(FUEL_TAB);
\r
221 MotorEditor.this.add(new FuelEditor(motor.getFuel()),
\r
223 setSelectedIndex(FUEL_TAB);
\r
226 p.add(new Editor(f));
\r
228 p.add(new Editor(f.getCombustionProduct()));
\r
229 } catch (Exception e) {
\r
233 setLeftComponent(p);
\r
234 setRightComponent(burnRate);
\r
235 // setDividerLocation(.25);
\r
236 // setResizeWeight(.25);
\r
237 if (f instanceof ChangeListening.Subject) {
\r
238 ((ChangeListening.Subject) f).addPropertyChangeListener(MotorEditor.this);
\r
243 private class CaseEditor extends JSplitPane {
\r
244 private static final long serialVersionUID = 1L;
\r
246 public CaseEditor(Nozzle n, Chamber c) {
\r
247 super(JSplitPane.HORIZONTAL_SPLIT);
\r
249 JPanel parts = new JPanel();
\r
250 parts.setLayout(new BoxLayout(parts, BoxLayout.Y_AXIS));
\r
251 setLeftComponent(parts);
\r
252 setRightComponent(new NozzlePanel(n));
\r
254 parts.add(new JTextField(motor.getName()){
\r
255 private static final long serialVersionUID = 1L;
\r
257 final JTextField t = this;
\r
258 addFocusListener(new FocusListener() {
\r
261 public void focusLost(FocusEvent e) {
\r
262 String n = t.getText();
\r
263 if ( !"".equals(n) && !n.equals(motor.getName()) ){
\r
264 System.out.println("Name Changed");
\r
267 t.setText(motor.getName());
\r
272 public void focusGained(FocusEvent e) {
\r
280 parts.add(new Editor(c));
\r
281 parts.add(new Editor(n));
\r
283 if (n instanceof ChangeListening.Subject) {
\r
284 ((ChangeListening.Subject) n).addPropertyChangeListener(MotorEditor.this);
\r
286 if (c instanceof ChangeListening.Subject) {
\r
287 ((ChangeListening.Subject) c).addPropertyChangeListener(MotorEditor.this);
\r
293 text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
\r
297 public MotorEditor(Motor m) {
\r
298 super(JTabbedPane.BOTTOM);
\r
299 text.setName("XML");
\r
300 text.setEditable(false);
\r
301 add(text, XML_TAB);
\r
305 public Motor getMotor(){
\r
309 private void reText() {
\r
311 text.setText(MotorIO.writeMotor(motor));
\r
312 } catch (IOException e) {
\r
313 throw new Error(e);
\r
317 private void setMotor(Motor m, boolean retext) {
\r
318 if ( motor != null )
\r
319 motor.removePropertyChangeListener(this);
\r
321 motor.addPropertyChangeListener(this);
\r
324 if (grainEditor != null)
\r
325 remove(grainEditor);
\r
326 while (getTabCount() > 1)
\r
328 add(new CaseEditor(motor.getNozzle(), motor.getChamber()), CASING_TAB);
\r
329 add(new GrainEditor(motor.getGrain()), GRAIN_TAB);
\r
330 add(new FuelEditor(motor.getFuel()), FUEL_TAB);
\r
331 add(bt = new BurnTab(), BURN_TAB);
\r
335 public static Motor defaultMotor() {
\r
336 Motor m = new Motor();
\r
337 m.setName("Example Motor");
\r
338 m.setFuel(new KNSU());
\r
340 CylindricalChamber c = new CylindricalChamber();
\r
341 c.setLength(Amount.valueOf(200, SI.MILLIMETER));
\r
342 c.setID(Amount.valueOf(25, SI.MILLIMETER));
\r
345 CoredCylindricalGrain g = new CoredCylindricalGrain();
\r
347 g.setLength(Amount.valueOf(70, SI.MILLIMETER));
\r
348 g.setOD(Amount.valueOf(23.5, SI.MILLIMETER));
\r
349 g.setID(Amount.valueOf(7.9375, SI.MILLIMETER));
\r
350 } catch (PropertyVetoException v) {
\r
351 throw new Error(v);
\r
354 m.setGrain(new MultiGrain(g, 2));
\r
356 ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();
\r
357 n.setThroatDiameter(Amount.valueOf(7.962, SI.MILLIMETER));
\r
358 n.setExitDiameter(Amount.valueOf(13.79, SI.MILLIMETER));
\r
359 n.setEfficiency(.85);
\r
365 public void focusOnObject(Object o) {
\r
366 if (o instanceof Grain)
\r
367 setSelectedIndex(GRAIN_TAB);
\r
368 if (o instanceof Chamber || o instanceof Nozzle)
\r
369 setSelectedIndex(CASING_TAB);
\r
370 if (o instanceof Fuel || o instanceof Fuel.CombustionProduct)
\r
371 setSelectedIndex(FUEL_TAB);
\r
375 public void showAsWindow() {
\r
376 JFrame f = new JFrame();
\r
377 f.setSize(1024, 768);
\r
378 f.setContentPane(this);
\r
379 f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
\r
380 f.setVisible(true);
\r
383 public static void main(String args[]) throws Exception {
\r
385 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
\r
386 } catch (Exception e1) {
\r
387 e1.printStackTrace();
\r
389 new MotorEditor(defaultMotor()).showAsWindow();
\r
392 public void propertyChange(PropertyChangeEvent evt) {
\r
394 //Dont re-burn for a name change!
\r
395 if ( !evt.getPropertyName().equals("Name") )
\r