1 package net.sf.openrocket.gui.main;
5 import java.awt.Component;
6 import java.awt.Window;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.util.Arrays;
10 import java.util.List;
12 import javax.swing.AbstractListModel;
13 import javax.swing.BorderFactory;
14 import javax.swing.JButton;
15 import javax.swing.JCheckBox;
16 import javax.swing.JComboBox;
17 import javax.swing.JDialog;
18 import javax.swing.JLabel;
19 import javax.swing.JList;
20 import javax.swing.JOptionPane;
21 import javax.swing.JPanel;
22 import javax.swing.JScrollPane;
23 import javax.swing.JSpinner;
24 import javax.swing.JTabbedPane;
25 import javax.swing.JTextField;
26 import javax.swing.ListCellRenderer;
27 import javax.swing.event.ChangeEvent;
28 import javax.swing.event.ChangeListener;
29 import javax.swing.event.DocumentEvent;
30 import javax.swing.event.DocumentListener;
32 import net.miginfocom.swing.MigLayout;
33 import net.sf.openrocket.document.Simulation;
34 import net.sf.openrocket.gui.SpinnerEditor;
35 import net.sf.openrocket.gui.adaptors.BooleanModel;
36 import net.sf.openrocket.gui.adaptors.DoubleModel;
37 import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
38 import net.sf.openrocket.gui.components.BasicSlider;
39 import net.sf.openrocket.gui.components.DescriptionArea;
40 import net.sf.openrocket.gui.components.SimulationExportPanel;
41 import net.sf.openrocket.gui.components.UnitSelector;
42 import net.sf.openrocket.gui.plot.Axis;
43 import net.sf.openrocket.gui.plot.PlotConfiguration;
44 import net.sf.openrocket.gui.plot.SimulationPlotPanel;
45 import net.sf.openrocket.models.atmosphere.ExtendedISAModel;
46 import net.sf.openrocket.rocketcomponent.Configuration;
47 import net.sf.openrocket.simulation.FlightData;
48 import net.sf.openrocket.simulation.FlightDataBranch;
49 import net.sf.openrocket.simulation.RK4SimulationStepper;
50 import net.sf.openrocket.simulation.GUISimulationConditions;
51 import net.sf.openrocket.simulation.FlightDataType;
52 import net.sf.openrocket.simulation.listeners.SimulationListener;
53 import net.sf.openrocket.simulation.listeners.example.CSVSaveListener;
54 import net.sf.openrocket.unit.Unit;
55 import net.sf.openrocket.unit.UnitGroup;
56 import net.sf.openrocket.util.Chars;
57 import net.sf.openrocket.util.GUIUtil;
58 import net.sf.openrocket.util.Icons;
59 import net.sf.openrocket.util.Prefs;
61 import org.jfree.chart.ChartFactory;
62 import org.jfree.chart.ChartPanel;
63 import org.jfree.chart.JFreeChart;
64 import org.jfree.chart.axis.NumberAxis;
65 import org.jfree.chart.plot.PlotOrientation;
66 import org.jfree.chart.plot.ValueMarker;
67 import org.jfree.chart.plot.XYPlot;
68 import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
69 import org.jfree.data.xy.XYSeries;
70 import org.jfree.data.xy.XYSeriesCollection;
73 public class SimulationEditDialog extends JDialog {
75 public static final int DEFAULT = -1;
76 public static final int EDIT = 1;
77 public static final int PLOT = 2;
80 private final Window parentWindow;
81 private final Simulation simulation;
82 private final GUISimulationConditions conditions;
83 private final Configuration configuration;
86 public SimulationEditDialog(Window parent, Simulation s) {
90 public SimulationEditDialog(Window parent, Simulation s, int tab) {
91 super(parent, "Edit simulation", JDialog.ModalityType.DOCUMENT_MODAL);
93 this.parentWindow = parent;
95 this.conditions = simulation.getConditions();
96 configuration = simulation.getConfiguration();
98 JPanel mainPanel = new JPanel(new MigLayout("fill","[grow, fill]"));
100 mainPanel.add(new JLabel("Simulation name: "), "span, split 2, shrink");
101 final JTextField field = new JTextField(simulation.getName());
102 field.getDocument().addDocumentListener(new DocumentListener() {
104 public void changedUpdate(DocumentEvent e) {
108 public void insertUpdate(DocumentEvent e) {
112 public void removeUpdate(DocumentEvent e) {
115 private void setText() {
116 String name = field.getText();
117 if (name == null || name.equals(""))
119 System.out.println("Setting name:"+name);
120 simulation.setName(name);
124 mainPanel.add(field, "shrinky, growx, wrap");
126 JTabbedPane tabbedPane = new JTabbedPane();
129 tabbedPane.addTab("Launch conditions", flightConditionsTab());
130 tabbedPane.addTab("Simulation options", simulationOptionsTab());
131 tabbedPane.addTab("Plot data", plotTab());
132 tabbedPane.addTab("Export data", exportTab());
134 // Select the initial tab
136 tabbedPane.setSelectedIndex(0);
137 } else if (tab == PLOT) {
138 tabbedPane.setSelectedIndex(2);
140 FlightData data = s.getSimulatedData();
141 if (data == null || data.getBranchCount() == 0)
142 tabbedPane.setSelectedIndex(0);
144 tabbedPane.setSelectedIndex(2);
147 mainPanel.add(tabbedPane, "spanx, grow, wrap");
151 mainPanel.add(new JPanel(), "spanx, split, growx");
154 button = new JButton("Run simulation");
155 button.addActionListener(new ActionListener() {
157 public void actionPerformed(ActionEvent e) {
158 SimulationEditDialog.this.dispose();
159 SimulationRunDialog.runSimulations(parentWindow, simulation);
162 mainPanel.add(button, "gapright para");
165 JButton close = new JButton("Close");
166 close.addActionListener(new ActionListener() {
168 public void actionPerformed(ActionEvent e) {
169 SimulationEditDialog.this.dispose();
172 mainPanel.add(close, "");
178 this.setLocationByPlatform(true);
180 GUIUtil.setDisposableDialogOptions(this, button);
187 private JPanel flightConditionsTab() {
188 JPanel panel = new JPanel(new MigLayout("fill"));
197 JLabel label = new JLabel("Motor configuration:");
198 label.setToolTipText("Select the motor configuration to use.");
199 panel.add(label, "shrinkx, spanx, split 2");
201 JComboBox combo = new JComboBox(new MotorConfigurationModel(configuration));
202 combo.setToolTipText("Select the motor configuration to use.");
203 combo.addActionListener(new ActionListener() {
205 public void actionPerformed(ActionEvent e) {
206 conditions.setMotorConfigurationID(configuration.getMotorConfigurationID());
209 panel.add(combo, "growx, wrap para");
212 //// Wind settings: Average wind speed, turbulence intensity, std. deviation
213 sub = new JPanel(new MigLayout("fill, gap rel unrel",
214 "[grow][65lp!][30lp!][75lp!]",""));
215 sub.setBorder(BorderFactory.createTitledBorder("Wind"));
216 panel.add(sub, "growx, split 2, aligny 0, flowy, gapright para");
220 label = new JLabel("Average windspeed:");
221 tip = "The average windspeed relative to the ground.";
222 label.setToolTipText(tip);
225 m = new DoubleModel(conditions,"WindSpeedAverage", UnitGroup.UNITS_VELOCITY,0);
227 spin = new JSpinner(m.getSpinnerModel());
228 spin.setEditor(new SpinnerEditor(spin));
229 spin.setToolTipText(tip);
230 sub.add(spin,"w 65lp!");
232 unit = new UnitSelector(m);
233 unit.setToolTipText(tip);
234 sub.add(unit,"growx");
235 slider = new BasicSlider(m.getSliderModel(0, 10.0));
236 slider.setToolTipText(tip);
237 sub.add(slider,"w 75lp, wrap");
241 // Wind std. deviation
242 label = new JLabel("Standard deviation:");
243 tip = "<html>The standard deviation of the windspeed.<br>" +
244 "The windspeed is within twice the standard deviation from the average for " +
246 label.setToolTipText(tip);
249 m = new DoubleModel(conditions,"WindSpeedDeviation", UnitGroup.UNITS_VELOCITY,0);
250 DoubleModel m2 = new DoubleModel(conditions,"WindSpeedAverage", 0.25,
251 UnitGroup.UNITS_COEFFICIENT,0);
253 spin = new JSpinner(m.getSpinnerModel());
254 spin.setEditor(new SpinnerEditor(spin));
255 spin.setToolTipText(tip);
256 sub.add(spin,"w 65lp!");
258 unit = new UnitSelector(m);
259 unit.setToolTipText(tip);
260 sub.add(unit,"growx");
261 slider = new BasicSlider(m.getSliderModel(new DoubleModel(0), m2));
262 slider.setToolTipText(tip);
263 sub.add(slider,"w 75lp, wrap");
266 // Wind turbulence intensity
267 label = new JLabel("Turbulence intensity:");
268 tip = "<html>The turbulence intensity is the standard deviation " +
269 "divided by the average windspeed.<br>" +
270 "Typical values range from "+
271 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.05) +
273 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.20) + ".";
274 label.setToolTipText(tip);
277 m = new DoubleModel(conditions,"WindTurbulenceIntensity", UnitGroup.UNITS_RELATIVE,0);
279 spin = new JSpinner(m.getSpinnerModel());
280 spin.setEditor(new SpinnerEditor(spin));
281 spin.setToolTipText(tip);
282 sub.add(spin,"w 65lp!");
284 unit = new UnitSelector(m);
285 unit.setToolTipText(tip);
286 sub.add(unit,"growx");
288 final JLabel intensityLabel = new JLabel(
289 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
290 intensityLabel.setToolTipText(tip);
291 sub.add(intensityLabel,"w 75lp, wrap");
292 m.addChangeListener(new ChangeListener() {
294 public void stateChanged(ChangeEvent e) {
295 intensityLabel.setText(
296 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
304 //// Temperature and pressure
305 sub = new JPanel(new MigLayout("fill, gap rel unrel",
306 "[grow][65lp!][30lp!][75lp!]",""));
307 sub.setBorder(BorderFactory.createTitledBorder("Atmospheric conditions"));
308 panel.add(sub, "growx, aligny 0, gapright para");
311 BooleanModel isa = new BooleanModel(conditions, "ISAAtmosphere");
312 JCheckBox check = new JCheckBox(isa);
313 check.setText("Use International Standard Atmosphere");
314 check.setToolTipText("<html>Select to use the International Standard Atmosphere model."+
315 "<br>This model has a temperature of " +
316 UnitGroup.UNITS_TEMPERATURE.toStringUnit(ExtendedISAModel.STANDARD_TEMPERATURE)+
317 " and a pressure of " +
318 UnitGroup.UNITS_PRESSURE.toStringUnit(ExtendedISAModel.STANDARD_PRESSURE) +
320 sub.add(check, "spanx, wrap unrel");
323 label = new JLabel("Temperature:");
324 tip = "The temperature at the launch site.";
325 label.setToolTipText(tip);
326 isa.addEnableComponent(label, false);
329 m = new DoubleModel(conditions,"LaunchTemperature", UnitGroup.UNITS_TEMPERATURE,0);
331 spin = new JSpinner(m.getSpinnerModel());
332 spin.setEditor(new SpinnerEditor(spin));
333 spin.setToolTipText(tip);
334 isa.addEnableComponent(spin, false);
335 sub.add(spin,"w 65lp!");
337 unit = new UnitSelector(m);
338 unit.setToolTipText(tip);
339 isa.addEnableComponent(unit, false);
340 sub.add(unit,"growx");
341 slider = new BasicSlider(m.getSliderModel(253.15, 308.15)); // -20 ... 35
342 slider.setToolTipText(tip);
343 isa.addEnableComponent(slider, false);
344 sub.add(slider,"w 75lp, wrap");
349 label = new JLabel("Pressure:");
350 tip = "The atmospheric pressure at the launch site.";
351 label.setToolTipText(tip);
352 isa.addEnableComponent(label, false);
355 m = new DoubleModel(conditions,"LaunchPressure", UnitGroup.UNITS_PRESSURE,0);
357 spin = new JSpinner(m.getSpinnerModel());
358 spin.setEditor(new SpinnerEditor(spin));
359 spin.setToolTipText(tip);
360 isa.addEnableComponent(spin, false);
361 sub.add(spin,"w 65lp!");
363 unit = new UnitSelector(m);
364 unit.setToolTipText(tip);
365 isa.addEnableComponent(unit, false);
366 sub.add(unit,"growx");
367 slider = new BasicSlider(m.getSliderModel(0.950e5, 1.050e5));
368 slider.setToolTipText(tip);
369 isa.addEnableComponent(slider, false);
370 sub.add(slider,"w 75lp, wrap");
376 //// Launch site conditions
377 sub = new JPanel(new MigLayout("fill, gap rel unrel",
378 "[grow][65lp!][30lp!][75lp!]",""));
379 sub.setBorder(BorderFactory.createTitledBorder("Launch site"));
380 panel.add(sub, "growx, split 2, aligny 0, flowy");
384 label = new JLabel("Latitude:");
385 tip = "<html>The launch site latitude affects the gravitational pull of Earth.<br>" +
386 "Positive values are on the Northern hemisphere, negative values on the " +
387 "Southern hemisphere.";
388 label.setToolTipText(tip);
391 m = new DoubleModel(conditions,"LaunchLatitude", UnitGroup.UNITS_NONE, -90, 90);
393 spin = new JSpinner(m.getSpinnerModel());
394 spin.setEditor(new SpinnerEditor(spin));
395 spin.setToolTipText(tip);
396 sub.add(spin,"w 65lp!");
398 label = new JLabel(Chars.DEGREE + " N");
399 label.setToolTipText(tip);
400 sub.add(label,"growx");
401 slider = new BasicSlider(m.getSliderModel(-90, 90));
402 slider.setToolTipText(tip);
403 sub.add(slider,"w 75lp, wrap");
408 label = new JLabel("Altitude:");
409 tip = "<html>The launch altitude above mean sea level.<br>" +
410 "This affects the position of the rocket in the atmospheric model.";
411 label.setToolTipText(tip);
414 m = new DoubleModel(conditions,"LaunchAltitude", UnitGroup.UNITS_DISTANCE,0);
416 spin = new JSpinner(m.getSpinnerModel());
417 spin.setEditor(new SpinnerEditor(spin));
418 spin.setToolTipText(tip);
419 sub.add(spin,"w 65lp!");
421 unit = new UnitSelector(m);
422 unit.setToolTipText(tip);
423 sub.add(unit,"growx");
424 slider = new BasicSlider(m.getSliderModel(0, 250, 1000));
425 slider.setToolTipText(tip);
426 sub.add(slider,"w 75lp, wrap");
433 sub = new JPanel(new MigLayout("fill, gap rel unrel",
434 "[grow][65lp!][30lp!][75lp!]",""));
435 sub.setBorder(BorderFactory.createTitledBorder("Launch rod"));
436 panel.add(sub, "growx, aligny 0, wrap");
440 label = new JLabel("Length:");
441 tip = "The length of the launch rod.";
442 label.setToolTipText(tip);
445 m = new DoubleModel(conditions,"LaunchRodLength", UnitGroup.UNITS_LENGTH, 0);
447 spin = new JSpinner(m.getSpinnerModel());
448 spin.setEditor(new SpinnerEditor(spin));
449 spin.setToolTipText(tip);
450 sub.add(spin,"w 65lp!");
452 unit = new UnitSelector(m);
453 unit.setToolTipText(tip);
454 sub.add(unit,"growx");
455 slider = new BasicSlider(m.getSliderModel(0, 1, 5));
456 slider.setToolTipText(tip);
457 sub.add(slider,"w 75lp, wrap");
462 label = new JLabel("Angle:");
463 tip = "The angle of the launch rod from vertical.";
464 label.setToolTipText(tip);
467 m = new DoubleModel(conditions,"LaunchRodAngle", UnitGroup.UNITS_ANGLE,
468 0, GUISimulationConditions.MAX_LAUNCH_ROD_ANGLE);
470 spin = new JSpinner(m.getSpinnerModel());
471 spin.setEditor(new SpinnerEditor(spin));
472 spin.setToolTipText(tip);
473 sub.add(spin,"w 65lp!");
475 unit = new UnitSelector(m);
476 unit.setToolTipText(tip);
477 sub.add(unit,"growx");
478 slider = new BasicSlider(m.getSliderModel(0, Math.PI/9,
479 GUISimulationConditions.MAX_LAUNCH_ROD_ANGLE));
480 slider.setToolTipText(tip);
481 sub.add(slider,"w 75lp, wrap");
486 label = new JLabel("Direction:");
487 tip = "<html>Direction of the launch rod relative to the wind.<br>" +
488 UnitGroup.UNITS_ANGLE.toStringUnit(0) +
489 " = towards the wind, "+
490 UnitGroup.UNITS_ANGLE.toStringUnit(Math.PI) +
492 label.setToolTipText(tip);
495 m = new DoubleModel(conditions,"LaunchRodDirection", UnitGroup.UNITS_ANGLE,
498 spin = new JSpinner(m.getSpinnerModel());
499 spin.setEditor(new SpinnerEditor(spin));
500 spin.setToolTipText(tip);
501 sub.add(spin,"w 65lp!");
503 unit = new UnitSelector(m);
504 unit.setToolTipText(tip);
505 sub.add(unit,"growx");
506 slider = new BasicSlider(m.getSliderModel(-Math.PI, Math.PI));
507 slider.setToolTipText(tip);
508 sub.add(slider,"w 75lp, wrap");
514 private String getIntensityDescription(double i) {
532 private JPanel simulationOptionsTab() {
533 JPanel panel = new JPanel(new MigLayout("fill"));
543 //// Simulation options
544 sub = new JPanel(new MigLayout("fill, gap rel unrel",
545 "[grow][65lp!][30lp!][75lp!]",""));
546 sub.setBorder(BorderFactory.createTitledBorder("Simulator options"));
547 panel.add(sub, "w 330lp!, growy, aligny 0");
550 // Calculation method
552 "The Extended Barrowman method calculates aerodynamic forces according <br>" +
553 "to the Barrowman equations extended to accommodate more components.";
555 label = new JLabel("Calculation method:");
556 label.setToolTipText(tip);
557 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
559 label = new JLabel("Extended Barrowman");
560 label.setToolTipText(tip);
561 sub.add(label, "growx, wrap para");
566 "The six degree-of-freedom simulator allows the rocket total freedom during " +
568 "Integration is performed using a 4<sup>th</sup> order Runge-Kutta 4 " +
569 "numerical integration.";
571 label = new JLabel("Simulation method:");
572 label.setToolTipText(tip);
573 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
575 label = new JLabel("6-DOF Runge-Kutta 4");
576 label.setToolTipText(tip);
577 sub.add(label, "growx, wrap 35lp");
581 label = new JLabel("Time step:");
582 tip = "<html>The time between simulation steps.<br>" +
583 "A smaller time step results in a more accurate but slower simulation.<br>" +
584 "The 4<sup>th</sup> order simulation method is quite accurate with a time " +
586 UnitGroup.UNITS_TIME_STEP.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP) +
588 label.setToolTipText(tip);
591 m = new DoubleModel(conditions,"TimeStep", UnitGroup.UNITS_TIME_STEP, 0, 1);
593 spin = new JSpinner(m.getSpinnerModel());
594 spin.setEditor(new SpinnerEditor(spin));
595 spin.setToolTipText(tip);
596 sub.add(spin,"w 65lp!");
598 unit = new UnitSelector(m);
599 unit.setToolTipText(tip);
600 sub.add(unit,"growx");
601 slider = new BasicSlider(m.getSliderModel(0, 0.2));
602 slider.setToolTipText(tip);
603 sub.add(slider,"w 75lp, wrap");
607 // Maximum angle step
609 label = new JLabel("Max. angle step:");
611 "This defines the maximum angle the rocket will turn during one time step.<br>"+
612 "Smaller values result in a more accurate but possibly slower simulation.<br>"+
613 "A recommended value is " +
614 UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".";
615 label.setToolTipText(tip);
618 m = new DoubleModel(conditions,"MaximumStepAngle", UnitGroup.UNITS_ANGLE,
619 1*Math.PI/180, Math.PI/9);
621 spin = new JSpinner(m.getSpinnerModel());
622 spin.setEditor(new SpinnerEditor(spin));
623 spin.setToolTipText(tip);
624 sub.add(spin,"w 65lp!");
626 unit = new UnitSelector(m);
627 unit.setToolTipText(tip);
628 sub.add(unit,"growx");
629 slider = new BasicSlider(m.getSliderModel(0, Math.toRadians(10)));
630 slider.setToolTipText(tip);
631 sub.add(slider,"w 75lp, wrap para");
634 JButton button = new JButton("Reset to default");
635 button.setToolTipText("Reset the time step to its default value (" +
636 UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP) +
638 button.addActionListener(new ActionListener() {
640 public void actionPerformed(ActionEvent e) {
641 conditions.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP);
645 // button.setToolTipText("<html>Reset the step value to its default:<br>" +
647 // UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
648 // "; maximum angle step " +
649 // UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".");
650 sub.add(button, "spanx, tag right, wrap para");
655 //// Simulation listeners
656 sub = new JPanel(new MigLayout("fill, gap 0 0"));
657 sub.setBorder(BorderFactory.createTitledBorder("Simulator listeners"));
658 panel.add(sub, "growx, growy");
661 DescriptionArea desc = new DescriptionArea(5);
662 desc.setText("<html>" +
663 "<i>Simulation listeners</i> is an advanced feature that allows "+
664 "user-written code to listen to and interact with the simulation. " +
665 "For details on writing simulation listeners, see the OpenRocket " +
666 "technical documentation.");
667 sub.add(desc, "aligny 0, growx, wrap para");
670 label = new JLabel("Current listeners:");
671 sub.add(label, "spanx, wrap rel");
673 final ListenerListModel listenerModel = new ListenerListModel();
674 final JList list = new JList(listenerModel);
675 list.setCellRenderer(new ListenerCellRenderer());
676 JScrollPane scroll = new JScrollPane(list);
677 // scroll.setPreferredSize(new Dimension(1,1));
678 sub.add(scroll, "height 1px, grow, wrap rel");
681 button = new JButton("Add");
682 button.addActionListener(new ActionListener() {
684 public void actionPerformed(ActionEvent e) {
685 String previous = Prefs.NODE.get("previousListenerName", "");
686 String input = (String)JOptionPane.showInputDialog(SimulationEditDialog.this,
688 "Type the full Java class name of the simulation listener, for example:",
689 "<html><tt>" + CSVSaveListener.class.getName() + "</tt>" },
690 "Add simulation listener",
691 JOptionPane.QUESTION_MESSAGE,
695 if (input == null || input.equals(""))
698 Prefs.NODE.put("previousListenerName", input);
699 simulation.getSimulationListeners().add(input);
700 listenerModel.fireContentsChanged();
703 sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
705 button = new JButton("Remove");
706 button.addActionListener(new ActionListener() {
708 public void actionPerformed(ActionEvent e) {
709 int[] selected = list.getSelectedIndices();
710 Arrays.sort(selected);
711 for (int i=selected.length-1; i>=0; i--) {
712 simulation.getSimulationListeners().remove(selected[i]);
714 listenerModel.fireContentsChanged();
717 sub.add(button, "sizegroup buttons, alignx 50%");
724 private class ListenerListModel extends AbstractListModel {
726 public String getElementAt(int index) {
727 if (index < 0 || index >= getSize())
729 return simulation.getSimulationListeners().get(index);
732 public int getSize() {
733 return simulation.getSimulationListeners().size();
735 public void fireContentsChanged() {
736 super.fireContentsChanged(this, 0, getSize());
744 * A panel for plotting the previously calculated data.
746 private JPanel plotTab() {
748 // Check that data exists
749 if (simulation.getSimulatedData() == null ||
750 simulation.getSimulatedData().getBranchCount() == 0) {
751 return noDataPanel();
754 return new SimulationPlotPanel(simulation);
760 * A panel for exporting the data.
762 private JPanel exportTab() {
763 FlightData data = simulation.getSimulatedData();
765 // Check that data exists
766 if (data == null || data.getBranchCount() == 0 ||
767 data.getBranch(0).getTypes().length == 0) {
768 return noDataPanel();
771 return new SimulationExportPanel(simulation);
779 * Return a panel stating that there is no data available, and that the user
780 * should run the simulation first.
782 public static JPanel noDataPanel() {
783 JPanel panel = new JPanel(new MigLayout("fill"));
786 panel.add(new JLabel("No flight data available."),
787 "alignx 50%, aligny 100%, wrap para");
788 panel.add(new JLabel("Please run the simulation first."),
789 "alignx 50%, aligny 0%, wrap");
794 private void performPlot(PlotConfiguration config) {
796 // Fill the auto-selections
797 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
798 PlotConfiguration filled = config.fillAutoAxes(branch);
799 List<Axis> axes = filled.getAllAxes();
802 // Create the data series for both axes
803 XYSeriesCollection[] data = new XYSeriesCollection[2];
804 data[0] = new XYSeriesCollection();
805 data[1] = new XYSeriesCollection();
808 // Get the domain axis type
809 final FlightDataType domainType = filled.getDomainAxisType();
810 final Unit domainUnit = filled.getDomainAxisUnit();
811 if (domainType == null) {
812 throw new IllegalArgumentException("Domain axis type not specified.");
814 List<Double> x = branch.get(domainType);
817 // Create the XYSeries objects from the flight data and store into the collections
818 int length = filled.getTypeCount();
819 String[] axisLabel = new String[2];
820 for (int i = 0; i < length; i++) {
822 FlightDataType type = filled.getType(i);
823 Unit unit = filled.getUnit(i);
824 int axis = filled.getAxis(i);
825 String name = getLabel(type, unit);
827 // Store data in provided units
828 List<Double> y = branch.get(type);
829 XYSeries series = new XYSeries(name, false, true);
830 for (int j=0; j<x.size(); j++) {
831 series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j)));
833 data[axis].addSeries(series);
836 if (axisLabel[axis] == null)
837 axisLabel[axis] = type.getName();
839 axisLabel[axis] += "; " + type.getName();
843 // Create the chart using the factory to get all default settings
844 JFreeChart chart = ChartFactory.createXYLineChart(
849 PlotOrientation.VERTICAL,
856 // Add the data and formatting to the plot
857 XYPlot plot = chart.getXYPlot();
859 for (int i=0; i<2; i++) {
860 // Check whether axis has any data
861 if (data[i].getSeriesCount() > 0) {
862 // Create and set axis
863 double min = axes.get(i).getMinValue();
864 double max = axes.get(i).getMaxValue();
865 NumberAxis axis = new PresetNumberAxis(min, max);
866 axis.setLabel(axisLabel[i]);
867 // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue());
868 plot.setRangeAxis(axisno, axis);
870 // Add data and map to the axis
871 plot.setDataset(axisno, data[i]);
872 plot.setRenderer(axisno, new StandardXYItemRenderer());
873 plot.mapDatasetToRangeAxis(axisno, axisno);
878 plot.getDomainAxis().setLabel(getLabel(domainType,domainUnit));
879 plot.addDomainMarker(new ValueMarker(0));
880 plot.addRangeMarker(new ValueMarker(0));
884 final JDialog dialog = new JDialog(this, "Simulation results");
885 dialog.setModalityType(ModalityType.DOCUMENT_MODAL);
887 JPanel panel = new JPanel(new MigLayout("fill"));
890 ChartPanel chartPanel = new ChartPanel(chart,
896 chartPanel.setMouseWheelEnabled(true);
897 chartPanel.setEnforceFileExtensions(true);
898 chartPanel.setInitialDelay(500);
900 chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
902 panel.add(chartPanel, "grow, wrap 20lp");
904 JButton button = new JButton("Close");
905 button.addActionListener(new ActionListener() {
907 public void actionPerformed(ActionEvent e) {
908 dialog.setVisible(false);
911 panel.add(button, "right");
913 dialog.setLocationByPlatform(true);
916 GUIUtil.setDisposableDialogOptions(dialog, button);
918 dialog.setVisible(true);
922 private class PresetNumberAxis extends NumberAxis {
923 private final double min;
924 private final double max;
926 public PresetNumberAxis(double min, double max) {
933 protected void autoAdjustRange() {
934 this.setRange(min, max);
939 private String getLabel(FlightDataType type, Unit unit) {
940 String name = type.getName();
941 if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
942 !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0)
943 name += " ("+unit.getUnit() + ")";
949 private class ListenerCellRenderer extends JLabel implements ListCellRenderer {
951 public Component getListCellRendererComponent(JList list, Object value,
952 int index, boolean isSelected, boolean cellHasFocus) {
953 String s = value.toString();
956 // Attempt instantiating, catch any exceptions
959 Class<?> c = Class.forName(s);
960 @SuppressWarnings("unused")
961 SimulationListener l = (SimulationListener)c.newInstance();
962 } catch (Exception e) {
967 setIcon(Icons.SIMULATION_LISTENER_OK);
968 setToolTipText("Listener instantiated successfully.");
970 setIcon(Icons.SIMULATION_LISTENER_ERROR);
971 setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
976 setBackground(list.getSelectionBackground());
977 setForeground(list.getSelectionForeground());
979 setBackground(list.getBackground());
980 setForeground(list.getForeground());