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