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