Square grain
[sw/motorsim] / gui / com / billkuker / rocketry / motorsim / visual / workbench / MotorEditor.java
1 package com.billkuker.rocketry.motorsim.visual.workbench;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Color;\r
5 import java.awt.Dimension;\r
6 import java.awt.event.ActionEvent;\r
7 import java.awt.event.ActionListener;\r
8 import java.awt.event.ComponentEvent;\r
9 import java.awt.event.ComponentListener;\r
10 import java.awt.event.FocusEvent;\r
11 import java.awt.event.FocusListener;\r
12 import java.beans.PropertyChangeEvent;\r
13 import java.beans.PropertyChangeListener;\r
14 import java.beans.PropertyVetoException;\r
15 import java.net.URI;\r
16 import java.util.List;\r
17 import java.util.Vector;\r
18 \r
19 import javax.measure.quantity.Duration;\r
20 import javax.measure.unit.SI;\r
21 import javax.swing.Box;\r
22 import javax.swing.BoxLayout;\r
23 import javax.swing.DefaultComboBoxModel;\r
24 import javax.swing.JComboBox;\r
25 import javax.swing.JFrame;\r
26 import javax.swing.JLabel;\r
27 import javax.swing.JPanel;\r
28 import javax.swing.JSplitPane;\r
29 import javax.swing.JTabbedPane;\r
30 import javax.swing.JTextArea;\r
31 import javax.swing.JTextField;\r
32 import javax.swing.SwingUtilities;\r
33 import javax.swing.UIManager;\r
34 import javax.swing.WindowConstants;\r
35 \r
36 import org.apache.log4j.Logger;\r
37 import org.jscience.physics.amount.Amount;\r
38 \r
39 import com.billkuker.rocketry.motorsim.Burn;\r
40 import com.billkuker.rocketry.motorsim.Chamber;\r
41 import com.billkuker.rocketry.motorsim.ChangeListening;\r
42 import com.billkuker.rocketry.motorsim.Colors;\r
43 import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;\r
44 import com.billkuker.rocketry.motorsim.CylindricalChamber;\r
45 import com.billkuker.rocketry.motorsim.Fuel;\r
46 import com.billkuker.rocketry.motorsim.Grain;\r
47 import com.billkuker.rocketry.motorsim.Motor;\r
48 import com.billkuker.rocketry.motorsim.Nozzle;\r
49 import com.billkuker.rocketry.motorsim.cases.Schedule40;\r
50 import com.billkuker.rocketry.motorsim.cases.Schedule80;\r
51 import com.billkuker.rocketry.motorsim.fuel.FuelResolver;\r
52 import com.billkuker.rocketry.motorsim.fuel.KNSU;\r
53 import com.billkuker.rocketry.motorsim.grain.CSlot;\r
54 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
55 import com.billkuker.rocketry.motorsim.grain.EndBurner;\r
56 import com.billkuker.rocketry.motorsim.grain.Finocyl;\r
57 import com.billkuker.rocketry.motorsim.grain.Moonburner;\r
58 import com.billkuker.rocketry.motorsim.grain.MultiGrain;\r
59 import com.billkuker.rocketry.motorsim.grain.MultiPort;\r
60 import com.billkuker.rocketry.motorsim.grain.RodAndTubeGrain;\r
61 import com.billkuker.rocketry.motorsim.grain.Square;\r
62 import com.billkuker.rocketry.motorsim.grain.Star;\r
63 import com.billkuker.rocketry.motorsim.visual.BurnPanel;\r
64 import com.billkuker.rocketry.motorsim.visual.ClassChooser;\r
65 import com.billkuker.rocketry.motorsim.visual.Editor;\r
66 import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
67 import com.billkuker.rocketry.motorsim.visual.HardwarePanel;\r
68 import com.billkuker.rocketry.motorsim.visual.SummaryPanel;\r
69 \r
70 public class MotorEditor extends JPanel implements PropertyChangeListener, FuelResolver.FuelsChangeListener{\r
71         private static final long serialVersionUID = 1L;\r
72         private static Logger log = Logger.getLogger(MotorEditor.class);\r
73         Motor motor;\r
74         GrainEditor grainEditor;\r
75         BurnTab bt;\r
76         Burn burn;\r
77         SummaryPanel sp;\r
78         JTextArea error;\r
79         JTabbedPane tabs;\r
80 \r
81         private Vector<BurnWatcher> burnWatchers = new Vector<BurnWatcher>();\r
82         private DefaultComboBoxModel availableFuels = new DefaultComboBoxModel();\r
83         \r
84         public MotorEditor(Motor m) {\r
85                 setLayout( new BorderLayout());\r
86                 tabs = new JTabbedPane(JTabbedPane.TOP);\r
87                 add(tabs, BorderLayout.CENTER);\r
88                 availableFuels.addElement(m.getFuel());\r
89                 availableFuels.setSelectedItem(m.getFuel());\r
90                 FuelResolver.addFuelsChangeListener(this);\r
91                 fuelsChanged();\r
92                 setMotor(m);\r
93                 \r
94                 Burn.getBurnSettings().addPropertyChangeListener(this);\r
95         }\r
96 \r
97         @Override\r
98         public void fuelsChanged() {\r
99                 while ( availableFuels.getSize() > 0 && availableFuels.getIndexOf(availableFuels.getSelectedItem()) != 0 )\r
100                         availableFuels.removeElementAt(0);\r
101                 while ( availableFuels.getSize() > 1 )\r
102                         availableFuels.removeElementAt(1);\r
103                 for ( Fuel f : FuelResolver.getFuelMap().values() ){\r
104                         if ( f != availableFuels.getSelectedItem() )\r
105                                 availableFuels.addElement(f);\r
106                 }\r
107         }\r
108 \r
109         //private static final int XML_TAB = 0;\r
110         private static final int CASING_TAB = 0;\r
111         private static final int GRAIN_TAB = 1;\r
112         private static final int BURN_TAB = 2;\r
113 \r
114         private List<Class<? extends Grain>> grainTypes = new Vector<Class<? extends Grain>>();\r
115         {\r
116                 grainTypes.add(CoredCylindricalGrain.class);\r
117                 grainTypes.add(Finocyl.class);\r
118                 grainTypes.add(Star.class);\r
119                 grainTypes.add(Moonburner.class);\r
120                 grainTypes.add(RodAndTubeGrain.class);\r
121                 grainTypes.add(CSlot.class);\r
122                 grainTypes.add(EndBurner.class);\r
123                 grainTypes.add(MultiPort.class);\r
124                 grainTypes.add(Square.class);\r
125         }\r
126         \r
127         private List<Class<? extends Chamber>> chamberTypes = new Vector<Class<? extends Chamber>>();\r
128         {\r
129                 chamberTypes.add(CylindricalChamber.class);\r
130                 chamberTypes.add(Schedule40.class);\r
131                 chamberTypes.add(Schedule80.class);\r
132         }\r
133 \r
134         private class BurnTab extends JPanel {\r
135                 private static final long serialVersionUID = 1L;\r
136                 private Thread currentThread;\r
137                 \r
138                 public BurnTab() {\r
139                         setLayout(new BorderLayout());\r
140                         setName("Simulation Results");\r
141                         reBurn();\r
142                 }\r
143                 \r
144                 private class BurnCanceled extends RuntimeException{\r
145                         private static final long serialVersionUID = 1L;\r
146                 };\r
147 \r
148                 public void reBurn() {\r
149                         removeAll();\r
150                         if ( error != null ){\r
151                                 MotorEditor.this.remove(error);\r
152                                 error = null;\r
153                         }\r
154                         if ( sp != null ){\r
155                                 MotorEditor.this.remove(sp);\r
156                                 sp = null;\r
157                         }\r
158                         currentThread = new Thread() {\r
159                                 {\r
160                                         setName("Burn " + motor.getName());\r
161                                         setDaemon(true);\r
162                                 }\r
163                                 public void run() {\r
164                                         final Thread me = this;\r
165                                         try {                                           \r
166                                                 final Burn b = new Burn(motor);\r
167                                                 b.addBurnProgressListener(\r
168                                                                 new Burn.BurnProgressListener() {\r
169                                                                         @Override\r
170                                                                         public void burnComplete(){};\r
171                                                                         @Override\r
172                                                                         public void setProgress(float f) {\r
173                                                                                 if ( currentThread != me ){\r
174                                                                                         throw new BurnCanceled();\r
175                                                                                 }\r
176                                                                         }\r
177                                                                 });\r
178 \r
179                                                 MotorEditor.this.add(sp = new SummaryPanel(b), BorderLayout.NORTH);\r
180                                                 revalidate();\r
181                                                 b.burn();\r
182 \r
183                                                 final BurnPanel bp = new BurnPanel(b);\r
184                                                 SwingUtilities.invokeLater(new Thread() {\r
185                                                         public void run() {\r
186                                                                 add(bp, BorderLayout.CENTER);\r
187                                                                 for (BurnWatcher bw : burnWatchers)\r
188                                                                         bw.replace(burn, b);\r
189                                                                 burn = b;\r
190                                                                 revalidate();\r
191                                                         }\r
192                                                 });\r
193                                         } catch (BurnCanceled c){\r
194                                                 log.info("Burn Canceled!");\r
195                                         } catch (final Exception e) {\r
196                                                 SwingUtilities.invokeLater(new Thread() {\r
197                                                         public void run() {\r
198                                                                 if ( sp != null )\r
199                                                                         MotorEditor.this.remove(sp);\r
200                                                                 error = new JTextArea(e.getMessage());\r
201                                                                 error.setBackground(Colors.RED);\r
202                                                                 error.setForeground(Color.WHITE);\r
203                                                                 error.setEditable(false);\r
204                                                                 MotorEditor.this.add(error, BorderLayout.NORTH);\r
205                                                                 revalidate();\r
206                                                         }\r
207                                                 });\r
208                                         }\r
209                                 }\r
210                         };\r
211                         currentThread.start();\r
212                 }\r
213         }\r
214 \r
215         private class GrainEditor extends JSplitPane {\r
216                 private static final long serialVersionUID = 1L;\r
217 \r
218                 public GrainEditor(final Grain g) {\r
219                         super(JSplitPane.HORIZONTAL_SPLIT);\r
220                         setName("Grain Geometry");\r
221                         setRightComponent(new GrainPanel(g));\r
222                         if (g instanceof Grain.Composite) {\r
223                                 final JPanel p = new JPanel();\r
224                                 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
225                                 \r
226                                 Editor grainEditor = new Editor(g);\r
227                                 grainEditor.setAlignmentX(LEFT_ALIGNMENT);\r
228                                 p.add(grainEditor);\r
229                                 \r
230                                 for (Grain gg : ((Grain.Composite) g).getGrains()) {\r
231                                         final int grainEditorIndex = p.getComponentCount() + 2;\r
232                                         \r
233                                         JLabel l = new JLabel("Grain Type:");\r
234                                         l.setAlignmentX(LEFT_ALIGNMENT);\r
235                                         p.add(l);\r
236                                         \r
237                                         p.add(new ClassChooser<Grain>(grainTypes, gg) {\r
238                                                 private static final long serialVersionUID = 1L;\r
239                                                 {setAlignmentX(LEFT_ALIGNMENT);}\r
240                                                 @Override\r
241                                                 protected Grain classSelected(\r
242                                                                 Class<? extends Grain> clazz, Grain ng) {\r
243                                                         if ( ng == null ){\r
244                                                                 try {\r
245                                                                         ng = clazz.newInstance();\r
246                                                                 } catch (InstantiationException e) {\r
247                                                                         e.printStackTrace();\r
248                                                                 } catch (IllegalAccessException e) {\r
249                                                                         e.printStackTrace();\r
250                                                                 }\r
251                                                         }\r
252                                                         if (g instanceof MultiGrain) {\r
253                                                                 ((MultiGrain) g).setGrain(ng);\r
254                                                                 p.remove(grainEditorIndex);\r
255                                                                 p.add(new Editor(ng), grainEditorIndex);\r
256                                                                 p.remove(0);\r
257                                                                 p.add(new Editor(g), 0);\r
258                                                         }\r
259                                                         return ng;\r
260 \r
261                                                 }\r
262                                         });\r
263                                         \r
264                                         Editor ggEditor = new Editor(gg);\r
265                                         ggEditor.setAlignmentX(LEFT_ALIGNMENT);\r
266                                         p.add(ggEditor);\r
267                                         \r
268                                         if (gg instanceof ChangeListening.Subject) {\r
269                                                 ((ChangeListening.Subject) gg)\r
270                                                                 .addPropertyChangeListener(MotorEditor.this);\r
271                                         }\r
272                                 }\r
273                                 setLeftComponent(p);\r
274                         } else {\r
275                                 setLeftComponent(new Editor(g));\r
276                         }\r
277                         // setDividerLocation(.25);\r
278                         // setResizeWeight(.25);\r
279                         if (g instanceof ChangeListening.Subject) {\r
280                                 ((ChangeListening.Subject) g)\r
281                                                 .addPropertyChangeListener(MotorEditor.this);\r
282                         }\r
283                 }\r
284         }\r
285 \r
286         private class CaseEditor extends JSplitPane implements ComponentListener {\r
287                 private static final long serialVersionUID = 1L;\r
288                 \r
289                 private HardwarePanel hp;\r
290                 private JPanel casing;\r
291                 private JPanel nozzle;\r
292                 private Editor casingEditor;\r
293                 private Editor nozzleEditor;\r
294                 \r
295                 private void setup() {\r
296                         if (casingEditor != null)\r
297                                 casing.remove(casingEditor);\r
298                         casingEditor = new Editor(motor.getChamber());\r
299                         casingEditor.setAlignmentX(LEFT_ALIGNMENT);\r
300                         casing.add(casingEditor);\r
301                         \r
302                         if (nozzleEditor != null)\r
303                                 nozzle.remove(nozzleEditor);\r
304                         nozzleEditor = new Editor(motor.getNozzle());\r
305                         nozzleEditor.setAlignmentX(LEFT_ALIGNMENT);\r
306                         nozzle.add(nozzleEditor);\r
307                         \r
308                         if (hp != null)\r
309                                 remove(hp);\r
310                         setBottomComponent(hp = new HardwarePanel(motor));\r
311                         if (motor.getNozzle() instanceof ChangeListening.Subject) {\r
312                                 ((ChangeListening.Subject) motor.getNozzle())\r
313                                                 .addPropertyChangeListener(MotorEditor.this);\r
314                         }\r
315                         if (motor.getChamber() instanceof ChangeListening.Subject) {\r
316                                 ((ChangeListening.Subject) motor.getChamber())\r
317                                                 .addPropertyChangeListener(MotorEditor.this);\r
318                         }\r
319                         if (motor.getFuel() instanceof ChangeListening.Subject ){\r
320                                 ((ChangeListening.Subject) motor.getFuel())\r
321                                                 .addPropertyChangeListener(MotorEditor.this);\r
322                         }\r
323                 }\r
324 \r
325                 public CaseEditor() {\r
326                         super(JSplitPane.VERTICAL_SPLIT);\r
327                         setName("General Parameters");\r
328                         this.addComponentListener(this);\r
329                         \r
330                         JPanel parts = new JPanel();\r
331                         parts.setLayout(new BoxLayout(parts, BoxLayout.X_AXIS));\r
332                         setTopComponent(parts);\r
333                         \r
334                         JPanel nameAndFuel = new JPanel();\r
335                         nameAndFuel.setLayout(new BoxLayout(nameAndFuel, BoxLayout.Y_AXIS));\r
336 \r
337                         JLabel l = new JLabel("Name:");\r
338                         l.setAlignmentX(LEFT_ALIGNMENT);\r
339                         nameAndFuel.add(l);\r
340                         nameAndFuel.add(new JTextField(motor.getName()) {\r
341                                 private static final long serialVersionUID = 1L;\r
342                                 {\r
343                                         setAlignmentX(LEFT_ALIGNMENT);\r
344                                         setMinimumSize(new Dimension(200, 20));\r
345                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
346                                         final JTextField t = this;\r
347                                         addFocusListener(new FocusListener() {\r
348 \r
349                                                 @Override\r
350                                                 public void focusLost(FocusEvent e) {\r
351                                                         String n = t.getText();\r
352                                                         if (!"".equals(n) && !n.equals(motor.getName())) {\r
353                                                                 motor.setName(n);\r
354                                                         } else {\r
355                                                                 t.setText(motor.getName());\r
356                                                         }\r
357                                                 }\r
358 \r
359                                                 @Override\r
360                                                 public void focusGained(FocusEvent e) {\r
361 \r
362                                                 }\r
363                                         });\r
364 \r
365                                 }\r
366                         });\r
367                         \r
368                         l = new JLabel("Fuel:");\r
369                         l.setAlignmentX(LEFT_ALIGNMENT);\r
370                         nameAndFuel.add(l);\r
371                         \r
372                         nameAndFuel.add( new JComboBox(availableFuels){\r
373                                 private static final long serialVersionUID = 1L;\r
374                                 {\r
375                                         setAlignmentX(LEFT_ALIGNMENT);\r
376                                         this.setSelectedItem(motor.getFuel());\r
377                                         setMinimumSize(new Dimension(200, 20));\r
378                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
379                                         addActionListener(new ActionListener(){\r
380                                                 @Override\r
381                                                 public void actionPerformed(ActionEvent e) {\r
382                                                         motor.setFuel((Fuel)getSelectedItem());\r
383                                                         System.out.println("FUEL CHANGED");\r
384                                                 }});\r
385                                 }\r
386                         });\r
387                         \r
388                         l = new JLabel("Casing:");\r
389                         l.setAlignmentX(LEFT_ALIGNMENT);\r
390                         nameAndFuel.add(l);\r
391                         \r
392                         nameAndFuel.add(new ClassChooser<Chamber>(chamberTypes, motor.getChamber()) {\r
393                                 private static final long serialVersionUID = 1L;\r
394                                 {\r
395                                         setAlignmentX(LEFT_ALIGNMENT);\r
396                                         setMinimumSize(new Dimension(200, 20));\r
397                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
398                                 }\r
399                                 @Override\r
400                                 protected Chamber classSelected(Class<? extends Chamber> clazz, Chamber c) {\r
401                                         try {\r
402                                                 if ( c != null ){\r
403                                                         motor.setChamber(c);\r
404                                                 } else {\r
405                                                         motor.setChamber(clazz.newInstance());\r
406                                                 }\r
407                                                 return motor.getChamber();\r
408                                         } catch (InstantiationException e) {\r
409                                                 log.error(e);\r
410                                         } catch (IllegalAccessException e) {\r
411                                                 log.error(e);\r
412                                         }\r
413                                         return null;\r
414                                 }\r
415                         });\r
416                         \r
417                         \r
418                         \r
419                         \r
420                         l = new JLabel("Delay:");\r
421                         l.setAlignmentX(LEFT_ALIGNMENT);\r
422                         nameAndFuel.add(l);\r
423                         nameAndFuel.add(new JTextField(Double.toString(motor.getEjectionDelay().doubleValue(SI.SECOND))) {\r
424                                 private static final long serialVersionUID = 1L;\r
425                                 {\r
426                                         setAlignmentX(LEFT_ALIGNMENT);\r
427                                         setMinimumSize(new Dimension(200, 20));\r
428                                         setMaximumSize(new Dimension(Short.MAX_VALUE, 20));\r
429                                         final JTextField t = this;\r
430                                         addFocusListener(new FocusListener() {\r
431 \r
432                                                 @Override\r
433                                                 public void focusLost(FocusEvent e) {\r
434                                                         try {\r
435                                                                 String n = t.getText();\r
436                                                                 double d = Double.parseDouble(n);\r
437                                                                 Amount<Duration> delay = Amount.valueOf(d, SI.SECOND);\r
438                                                                 if ( delay != motor.getEjectionDelay() ){\r
439                                                                         motor.setEjectionDelay(delay);\r
440                                                                 }\r
441                                                         } catch ( Exception ex ){\r
442                                                                 log.warn(e);\r
443                                                                 setText(Double.toString(motor.getEjectionDelay().doubleValue(SI.SECOND)));\r
444                                                         }\r
445                                                 }\r
446 \r
447                                                 @Override\r
448                                                 public void focusGained(FocusEvent e) {\r
449 \r
450                                                 }\r
451                                         });\r
452 \r
453                                 }\r
454                         });\r
455                         \r
456                         \r
457                         \r
458                         nameAndFuel.add(Box.createVerticalGlue());\r
459                         parts.add(nameAndFuel);\r
460                         \r
461                         casing = new JPanel();\r
462                         casing.setLayout(new BoxLayout(casing, BoxLayout.Y_AXIS));\r
463                         l = new JLabel("Casing:");\r
464                         l.setAlignmentX(LEFT_ALIGNMENT);\r
465                         casing.add(l);\r
466                         parts.add(casing);\r
467                         \r
468                         nozzle = new JPanel();\r
469                         nozzle.setLayout(new BoxLayout(nozzle, BoxLayout.Y_AXIS));\r
470                         l = new JLabel("Nozzle:");\r
471                         l.setAlignmentX(LEFT_ALIGNMENT);\r
472                         nozzle.add(l);\r
473                         parts.add(nozzle);\r
474                         \r
475                         motor.addPropertyChangeListener(new PropertyChangeListener() {\r
476                                 @Override\r
477                                 public void propertyChange(PropertyChangeEvent arg0) {\r
478                                         setup();\r
479                                         setResizeWeight(.5);\r
480                                         setDividerLocation(.5);\r
481                                 }\r
482                         });\r
483                         \r
484                         setup();\r
485                 }\r
486 \r
487                 @Override\r
488                 public void componentHidden(ComponentEvent arg0) {\r
489 \r
490                 }\r
491 \r
492                 @Override\r
493                 public void componentMoved(ComponentEvent arg0) {\r
494 \r
495                 }\r
496 \r
497                 @Override\r
498                 public void componentResized(ComponentEvent arg0) {\r
499                         setResizeWeight(.5);\r
500                         setDividerLocation(.5);\r
501                 }\r
502 \r
503                 @Override\r
504                 public void componentShown(ComponentEvent arg0) {\r
505 \r
506                 }\r
507         }\r
508 \r
509 \r
510 \r
511 \r
512         public Motor getMotor() {\r
513                 return motor;\r
514         }\r
515 \r
516 \r
517         private void setMotor(Motor m) {\r
518                 if (motor != null)\r
519                         motor.removePropertyChangeListener(this);\r
520                 motor = m;\r
521                 motor.addPropertyChangeListener(this);\r
522                 if (grainEditor != null)\r
523                         remove(grainEditor);\r
524                 while (tabs.getTabCount() > 1)\r
525                         tabs.removeTabAt(1);\r
526                 tabs.add(new CaseEditor(), CASING_TAB);\r
527                 tabs.add(new GrainEditor(motor.getGrain()), GRAIN_TAB);\r
528                 tabs.add(bt = new BurnTab(), BURN_TAB);\r
529         }\r
530 \r
531         private static int idx;\r
532         public static Motor defaultMotor() {\r
533                 Motor m = new Motor();\r
534                 m.setName("New Motor " + ++idx);\r
535                 try {\r
536                         m.setFuel(FuelResolver.getFuel(new URI("motorsim:KNDX")));\r
537                 } catch (Exception e) {\r
538                         throw new Error(e);\r
539                 }\r
540 \r
541                 CylindricalChamber c = new CylindricalChamber();\r
542                 c.setLength(Amount.valueOf(420, SI.MILLIMETER));\r
543                 c.setID(Amount.valueOf(70, SI.MILLIMETER));\r
544                 c.setOD(Amount.valueOf(72, SI.MILLIMETER));\r
545                 m.setChamber(c);\r
546 \r
547                 CoredCylindricalGrain g = new CoredCylindricalGrain();\r
548                 try {\r
549                         g.setLength(Amount.valueOf(100, SI.MILLIMETER));\r
550                         g.setOD(Amount.valueOf(62, SI.MILLIMETER));\r
551                         g.setID(Amount.valueOf(20, SI.MILLIMETER));\r
552                 } catch (PropertyVetoException v) {\r
553                         throw new Error(v);\r
554                 }\r
555                 \r
556                 MultiGrain mg = new MultiGrain(g, 4);\r
557                 mg.setSpacing(Amount.valueOf(6, SI.MILLIMETER));\r
558                 m.setGrain(mg);\r
559 \r
560                 ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();\r
561                 n.setThroatDiameter(Amount.valueOf(14.089, SI.MILLIMETER));\r
562                 n.setExitDiameter(Amount.valueOf(44.55, SI.MILLIMETER));\r
563                 n.setEfficiency(.85);\r
564                 m.setNozzle(n);\r
565 \r
566                 return m;\r
567         }\r
568 \r
569         public void focusOnObject(Object o) {\r
570                 if (o instanceof Grain)\r
571                         tabs.setSelectedIndex(GRAIN_TAB);\r
572                 if (o instanceof Chamber || o instanceof Nozzle)\r
573                         tabs.setSelectedIndex(CASING_TAB);\r
574         }\r
575 \r
576         public void addBurnWatcher(BurnWatcher bw) {\r
577                 burnWatchers.add(bw);\r
578         }\r
579 \r
580         @Deprecated\r
581         public void showAsWindow() {\r
582                 JFrame f = new JFrame();\r
583                 f.setSize(1024, 768);\r
584                 f.setContentPane(this);\r
585                 f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\r
586                 f.setVisible(true);\r
587         }\r
588 \r
589         public static void main(String args[]) throws Exception {\r
590                 try {\r
591                         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\r
592                 } catch (Exception e1) {\r
593                         e1.printStackTrace();\r
594                 }\r
595                 Vector<Fuel> ff = new Vector<Fuel>();\r
596                 ff.add(new KNSU());\r
597                 //new MotorEditor(defaultMotor(), ff).showAsWindow();\r
598         }\r
599 \r
600         public void propertyChange(PropertyChangeEvent evt) {\r
601                 // Dont re-burn for a name change!\r
602                 if (!evt.getPropertyName().equals("Name")){\r
603                         bt.reBurn();\r
604                 } else {\r
605                         for (BurnWatcher bw : burnWatchers)\r
606                                 bw.replace(burn, burn);\r
607                 }\r
608         }\r
609 \r
610 \r
611 }\r