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.aerodynamics.ExtendedISAModel;
34 import net.sf.openrocket.document.Simulation;
35 import net.sf.openrocket.gui.SpinnerEditor;
36 import net.sf.openrocket.gui.adaptors.BooleanModel;
37 import net.sf.openrocket.gui.adaptors.DoubleModel;
38 import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
39 import net.sf.openrocket.gui.components.BasicSlider;
40 import net.sf.openrocket.gui.components.DescriptionArea;
41 import net.sf.openrocket.gui.components.SimulationExportPanel;
42 import net.sf.openrocket.gui.components.UnitSelector;
43 import net.sf.openrocket.gui.plot.Axis;
44 import net.sf.openrocket.gui.plot.PlotConfiguration;
45 import net.sf.openrocket.gui.plot.PlotPanel;
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.RK4Simulator;
50 import net.sf.openrocket.simulation.SimulationConditions;
51 import net.sf.openrocket.simulation.SimulationListener;
52 import net.sf.openrocket.simulation.listeners.CSVSaveListener;
53 import net.sf.openrocket.unit.Unit;
54 import net.sf.openrocket.unit.UnitGroup;
55 import net.sf.openrocket.util.GUIUtil;
56 import net.sf.openrocket.util.Icons;
57 import net.sf.openrocket.util.Prefs;
59 import org.jfree.chart.ChartFactory;
60 import org.jfree.chart.ChartPanel;
61 import org.jfree.chart.JFreeChart;
62 import org.jfree.chart.axis.NumberAxis;
63 import org.jfree.chart.plot.PlotOrientation;
64 import org.jfree.chart.plot.ValueMarker;
65 import org.jfree.chart.plot.XYPlot;
66 import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
67 import org.jfree.data.xy.XYSeries;
68 import org.jfree.data.xy.XYSeriesCollection;
71 public class SimulationEditDialog extends JDialog {
73 public static final int DEFAULT = -1;
74 public static final int EDIT = 1;
75 public static final int PLOT = 2;
78 private final Window parentWindow;
79 private final Simulation simulation;
80 private final SimulationConditions conditions;
81 private final Configuration configuration;
84 public SimulationEditDialog(Window parent, Simulation s) {
88 public SimulationEditDialog(Window parent, Simulation s, int tab) {
89 super(parent, "Edit simulation", JDialog.ModalityType.DOCUMENT_MODAL);
91 this.parentWindow = parent;
93 this.conditions = simulation.getConditions();
94 configuration = simulation.getConfiguration();
96 JPanel mainPanel = new JPanel(new MigLayout("fill","[grow, fill]"));
98 mainPanel.add(new JLabel("Simulation name: "), "span, split 2, shrink");
99 final JTextField field = new JTextField(simulation.getName());
100 field.getDocument().addDocumentListener(new DocumentListener() {
102 public void changedUpdate(DocumentEvent e) {
106 public void insertUpdate(DocumentEvent e) {
110 public void removeUpdate(DocumentEvent e) {
113 private void setText() {
114 String name = field.getText();
115 if (name == null || name.equals(""))
117 System.out.println("Setting name:"+name);
118 simulation.setName(name);
122 mainPanel.add(field, "shrinky, growx, wrap");
124 JTabbedPane tabbedPane = new JTabbedPane();
127 tabbedPane.addTab("Launch conditions", flightConditionsTab());
128 tabbedPane.addTab("Simulation options", simulationOptionsTab());
129 tabbedPane.addTab("Plot data", plotTab());
130 tabbedPane.addTab("Export data", exportTab());
132 // Select the initial tab
134 tabbedPane.setSelectedIndex(0);
135 } else if (tab == PLOT) {
136 tabbedPane.setSelectedIndex(2);
138 FlightData data = s.getSimulatedData();
139 if (data == null || data.getBranchCount() == 0)
140 tabbedPane.setSelectedIndex(0);
142 tabbedPane.setSelectedIndex(2);
145 mainPanel.add(tabbedPane, "spanx, grow, wrap");
149 mainPanel.add(new JPanel(), "spanx, split, growx");
152 button = new JButton("Run simulation");
153 button.addActionListener(new ActionListener() {
155 public void actionPerformed(ActionEvent e) {
156 SimulationEditDialog.this.dispose();
157 SimulationRunDialog.runSimulations(parentWindow, simulation);
160 mainPanel.add(button, "gapright para");
163 JButton close = new JButton("Close");
164 close.addActionListener(new ActionListener() {
166 public void actionPerformed(ActionEvent e) {
167 SimulationEditDialog.this.dispose();
170 mainPanel.add(close, "");
176 this.setLocationByPlatform(true);
178 GUIUtil.setDisposableDialogOptions(this, button);
185 private JPanel flightConditionsTab() {
186 JPanel panel = new JPanel(new MigLayout("fill"));
195 JLabel label = new JLabel("Motor configuration:");
196 label.setToolTipText("Select the motor configuration to use.");
197 panel.add(label, "shrinkx, spanx, split 2");
199 JComboBox combo = new JComboBox(new MotorConfigurationModel(configuration));
200 combo.setToolTipText("Select the motor configuration to use.");
201 combo.addActionListener(new ActionListener() {
203 public void actionPerformed(ActionEvent e) {
204 conditions.setMotorConfigurationID(configuration.getMotorConfigurationID());
207 panel.add(combo, "growx, wrap para");
210 //// Wind settings: Average wind speed, turbulence intensity, std. deviation
211 sub = new JPanel(new MigLayout("fill, gap rel unrel",
212 "[grow][65lp!][30lp!][75lp!]",""));
213 sub.setBorder(BorderFactory.createTitledBorder("Wind"));
214 panel.add(sub, "growx, split 2, aligny 0, flowy, gapright para");
218 label = new JLabel("Average windspeed:");
219 tip = "The average windspeed relative to the ground.";
220 label.setToolTipText(tip);
223 m = new DoubleModel(conditions,"WindSpeedAverage", UnitGroup.UNITS_VELOCITY,0);
225 spin = new JSpinner(m.getSpinnerModel());
226 spin.setEditor(new SpinnerEditor(spin));
227 spin.setToolTipText(tip);
228 sub.add(spin,"w 65lp!");
230 unit = new UnitSelector(m);
231 unit.setToolTipText(tip);
232 sub.add(unit,"growx");
233 slider = new BasicSlider(m.getSliderModel(0, 10.0));
234 slider.setToolTipText(tip);
235 sub.add(slider,"w 75lp, wrap");
239 // Wind std. deviation
240 label = new JLabel("Standard deviation:");
241 tip = "<html>The standard deviation of the windspeed.<br>" +
242 "The windspeed is within twice the standard deviation from the average for " +
244 label.setToolTipText(tip);
247 m = new DoubleModel(conditions,"WindSpeedDeviation", UnitGroup.UNITS_VELOCITY,0);
248 DoubleModel m2 = new DoubleModel(conditions,"WindSpeedAverage", 0.25,
249 UnitGroup.UNITS_COEFFICIENT,0);
251 spin = new JSpinner(m.getSpinnerModel());
252 spin.setEditor(new SpinnerEditor(spin));
253 spin.setToolTipText(tip);
254 sub.add(spin,"w 65lp!");
256 unit = new UnitSelector(m);
257 unit.setToolTipText(tip);
258 sub.add(unit,"growx");
259 slider = new BasicSlider(m.getSliderModel(new DoubleModel(0), m2));
260 slider.setToolTipText(tip);
261 sub.add(slider,"w 75lp, wrap");
264 // Wind turbulence intensity
265 label = new JLabel("Turbulence intensity:");
266 tip = "<html>The turbulence intensity is the standard deviation " +
267 "divided by the average windspeed.<br>" +
268 "Typical values range from "+
269 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.05) +
271 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.20) + ".";
272 label.setToolTipText(tip);
275 m = new DoubleModel(conditions,"WindTurbulenceIntensity", UnitGroup.UNITS_RELATIVE,0);
277 spin = new JSpinner(m.getSpinnerModel());
278 spin.setEditor(new SpinnerEditor(spin));
279 spin.setToolTipText(tip);
280 sub.add(spin,"w 65lp!");
282 unit = new UnitSelector(m);
283 unit.setToolTipText(tip);
284 sub.add(unit,"growx");
286 final JLabel intensityLabel = new JLabel(
287 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
288 intensityLabel.setToolTipText(tip);
289 sub.add(intensityLabel,"w 75lp, wrap");
290 m.addChangeListener(new ChangeListener() {
292 public void stateChanged(ChangeEvent e) {
293 intensityLabel.setText(
294 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
302 //// Temperature and pressure
303 sub = new JPanel(new MigLayout("fill, gap rel unrel",
304 "[grow][65lp!][30lp!][75lp!]",""));
305 sub.setBorder(BorderFactory.createTitledBorder("Atmospheric conditions"));
306 panel.add(sub, "growx, aligny 0, gapright para");
309 BooleanModel isa = new BooleanModel(conditions, "ISAAtmosphere");
310 JCheckBox check = new JCheckBox(isa);
311 check.setText("Use International Standard Atmosphere");
312 check.setToolTipText("<html>Select to use the International Standard Atmosphere model."+
313 "<br>This model has a temperature of " +
314 UnitGroup.UNITS_TEMPERATURE.toStringUnit(ExtendedISAModel.STANDARD_TEMPERATURE)+
315 " and a pressure of " +
316 UnitGroup.UNITS_PRESSURE.toStringUnit(ExtendedISAModel.STANDARD_PRESSURE) +
318 sub.add(check, "spanx, wrap unrel");
321 label = new JLabel("Temperature:");
322 tip = "The temperature at the launch site.";
323 label.setToolTipText(tip);
324 isa.addEnableComponent(label, false);
327 m = new DoubleModel(conditions,"LaunchTemperature", UnitGroup.UNITS_TEMPERATURE,0);
329 spin = new JSpinner(m.getSpinnerModel());
330 spin.setEditor(new SpinnerEditor(spin));
331 spin.setToolTipText(tip);
332 isa.addEnableComponent(spin, false);
333 sub.add(spin,"w 65lp!");
335 unit = new UnitSelector(m);
336 unit.setToolTipText(tip);
337 isa.addEnableComponent(unit, false);
338 sub.add(unit,"growx");
339 slider = new BasicSlider(m.getSliderModel(253.15, 308.15)); // -20 ... 35
340 slider.setToolTipText(tip);
341 isa.addEnableComponent(slider, false);
342 sub.add(slider,"w 75lp, wrap");
347 label = new JLabel("Pressure:");
348 tip = "The atmospheric pressure at the launch site.";
349 label.setToolTipText(tip);
350 isa.addEnableComponent(label, false);
353 m = new DoubleModel(conditions,"LaunchPressure", UnitGroup.UNITS_PRESSURE,0);
355 spin = new JSpinner(m.getSpinnerModel());
356 spin.setEditor(new SpinnerEditor(spin));
357 spin.setToolTipText(tip);
358 isa.addEnableComponent(spin, false);
359 sub.add(spin,"w 65lp!");
361 unit = new UnitSelector(m);
362 unit.setToolTipText(tip);
363 isa.addEnableComponent(unit, false);
364 sub.add(unit,"growx");
365 slider = new BasicSlider(m.getSliderModel(0.950e5, 1.050e5));
366 slider.setToolTipText(tip);
367 isa.addEnableComponent(slider, false);
368 sub.add(slider,"w 75lp, wrap");
374 //// Launch site conditions
375 sub = new JPanel(new MigLayout("fill, gap rel unrel",
376 "[grow][65lp!][30lp!][75lp!]",""));
377 sub.setBorder(BorderFactory.createTitledBorder("Launch site"));
378 panel.add(sub, "growx, split 2, aligny 0, flowy");
382 label = new JLabel("Latitude:");
383 tip = "<html>The launch site latitude affects the gravitational pull of Earth.<br>" +
384 "Positive values are on the Northern hemisphere, negative values on the " +
385 "Southern hemisphere.";
386 label.setToolTipText(tip);
389 m = new DoubleModel(conditions,"LaunchLatitude", UnitGroup.UNITS_NONE, -90, 90);
391 spin = new JSpinner(m.getSpinnerModel());
392 spin.setEditor(new SpinnerEditor(spin));
393 spin.setToolTipText(tip);
394 sub.add(spin,"w 65lp!");
396 label = new JLabel("\u00b0 N");
397 label.setToolTipText(tip);
398 sub.add(label,"growx");
399 slider = new BasicSlider(m.getSliderModel(-90, 90));
400 slider.setToolTipText(tip);
401 sub.add(slider,"w 75lp, wrap");
406 label = new JLabel("Altitude:");
407 tip = "<html>The launch altitude above mean sea level.<br>" +
408 "This affects the position of the rocket in the atmospheric model.";
409 label.setToolTipText(tip);
412 m = new DoubleModel(conditions,"LaunchAltitude", UnitGroup.UNITS_DISTANCE,0);
414 spin = new JSpinner(m.getSpinnerModel());
415 spin.setEditor(new SpinnerEditor(spin));
416 spin.setToolTipText(tip);
417 sub.add(spin,"w 65lp!");
419 unit = new UnitSelector(m);
420 unit.setToolTipText(tip);
421 sub.add(unit,"growx");
422 slider = new BasicSlider(m.getSliderModel(0, 250, 1000));
423 slider.setToolTipText(tip);
424 sub.add(slider,"w 75lp, wrap");
431 sub = new JPanel(new MigLayout("fill, gap rel unrel",
432 "[grow][65lp!][30lp!][75lp!]",""));
433 sub.setBorder(BorderFactory.createTitledBorder("Launch rod"));
434 panel.add(sub, "growx, aligny 0, wrap");
438 label = new JLabel("Length:");
439 tip = "The length of the launch rod.";
440 label.setToolTipText(tip);
443 m = new DoubleModel(conditions,"LaunchRodLength", UnitGroup.UNITS_LENGTH, 0);
445 spin = new JSpinner(m.getSpinnerModel());
446 spin.setEditor(new SpinnerEditor(spin));
447 spin.setToolTipText(tip);
448 sub.add(spin,"w 65lp!");
450 unit = new UnitSelector(m);
451 unit.setToolTipText(tip);
452 sub.add(unit,"growx");
453 slider = new BasicSlider(m.getSliderModel(0, 1, 5));
454 slider.setToolTipText(tip);
455 sub.add(slider,"w 75lp, wrap");
460 label = new JLabel("Angle:");
461 tip = "The angle of the launch rod from vertical.";
462 label.setToolTipText(tip);
465 m = new DoubleModel(conditions,"LaunchRodAngle", UnitGroup.UNITS_ANGLE,
466 0, SimulationConditions.MAX_LAUNCH_ROD_ANGLE);
468 spin = new JSpinner(m.getSpinnerModel());
469 spin.setEditor(new SpinnerEditor(spin));
470 spin.setToolTipText(tip);
471 sub.add(spin,"w 65lp!");
473 unit = new UnitSelector(m);
474 unit.setToolTipText(tip);
475 sub.add(unit,"growx");
476 slider = new BasicSlider(m.getSliderModel(0, Math.PI/9,
477 SimulationConditions.MAX_LAUNCH_ROD_ANGLE));
478 slider.setToolTipText(tip);
479 sub.add(slider,"w 75lp, wrap");
484 label = new JLabel("Direction:");
485 tip = "<html>Direction of the launch rod relative to the wind.<br>" +
486 UnitGroup.UNITS_ANGLE.toStringUnit(0) +
487 " = towards the wind, "+
488 UnitGroup.UNITS_ANGLE.toStringUnit(Math.PI) +
490 label.setToolTipText(tip);
493 m = new DoubleModel(conditions,"LaunchRodDirection", UnitGroup.UNITS_ANGLE,
496 spin = new JSpinner(m.getSpinnerModel());
497 spin.setEditor(new SpinnerEditor(spin));
498 spin.setToolTipText(tip);
499 sub.add(spin,"w 65lp!");
501 unit = new UnitSelector(m);
502 unit.setToolTipText(tip);
503 sub.add(unit,"growx");
504 slider = new BasicSlider(m.getSliderModel(-Math.PI, Math.PI));
505 slider.setToolTipText(tip);
506 sub.add(slider,"w 75lp, wrap");
512 private String getIntensityDescription(double i) {
530 private JPanel simulationOptionsTab() {
531 JPanel panel = new JPanel(new MigLayout("fill"));
541 //// Simulation options
542 sub = new JPanel(new MigLayout("fill, gap rel unrel",
543 "[grow][65lp!][30lp!][75lp!]",""));
544 sub.setBorder(BorderFactory.createTitledBorder("Simulator options"));
545 panel.add(sub, "w 330lp!, growy, aligny 0");
548 // Calculation method
550 "The Extended Barrowman method calculates aerodynamic forces according <br>" +
551 "to the Barrowman equations extended to accommodate more components.";
553 label = new JLabel("Calculation method:");
554 label.setToolTipText(tip);
555 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
557 label = new JLabel("Extended Barrowman");
558 label.setToolTipText(tip);
559 sub.add(label, "growx, wrap para");
564 "The six degree-of-freedom simulator allows the rocket total freedom during " +
566 "Integration is performed using a 4<sup>th</sup> order Runge-Kutta 4 " +
567 "numerical integration.";
569 label = new JLabel("Simulation method:");
570 label.setToolTipText(tip);
571 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
573 label = new JLabel("6-DOF Runge-Kutta 4");
574 label.setToolTipText(tip);
575 sub.add(label, "growx, wrap 35lp");
579 label = new JLabel("Time step:");
580 tip = "<html>The time between simulation steps.<br>" +
581 "A smaller time step results in a more accurate but slower simulation.<br>" +
582 "The 4<sup>th</sup> order simulation method is quite accurate with a time " +
584 UnitGroup.UNITS_TIME_STEP.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
586 label.setToolTipText(tip);
589 m = new DoubleModel(conditions,"TimeStep", UnitGroup.UNITS_TIME_STEP, 0, 1);
591 spin = new JSpinner(m.getSpinnerModel());
592 spin.setEditor(new SpinnerEditor(spin));
593 spin.setToolTipText(tip);
594 sub.add(spin,"w 65lp!");
596 unit = new UnitSelector(m);
597 unit.setToolTipText(tip);
598 sub.add(unit,"growx");
599 slider = new BasicSlider(m.getSliderModel(0, 0.2));
600 slider.setToolTipText(tip);
601 sub.add(slider,"w 75lp, wrap");
605 // Maximum angle step
607 label = new JLabel("Max. angle step:");
609 "This defines the maximum angle the rocket will turn during one time step.<br>"+
610 "Smaller values result in a more accurate but possibly slower simulation.<br>"+
611 "A recommended value is " +
612 UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".";
613 label.setToolTipText(tip);
616 m = new DoubleModel(conditions,"MaximumStepAngle", UnitGroup.UNITS_ANGLE,
617 1*Math.PI/180, Math.PI/9);
619 spin = new JSpinner(m.getSpinnerModel());
620 spin.setEditor(new SpinnerEditor(spin));
621 spin.setToolTipText(tip);
622 sub.add(spin,"w 65lp!");
624 unit = new UnitSelector(m);
625 unit.setToolTipText(tip);
626 sub.add(unit,"growx");
627 slider = new BasicSlider(m.getSliderModel(0, Math.toRadians(10)));
628 slider.setToolTipText(tip);
629 sub.add(slider,"w 75lp, wrap para");
632 JButton button = new JButton("Reset to default");
633 button.setToolTipText("Reset the time step to its default value (" +
634 UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
637 // button.setToolTipText("<html>Reset the step value to its default:<br>" +
639 // UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
640 // "; maximum angle step " +
641 // UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".");
642 sub.add(button, "spanx, tag right, wrap para");
647 //// Simulation listeners
648 sub = new JPanel(new MigLayout("fill, gap 0 0"));
649 sub.setBorder(BorderFactory.createTitledBorder("Simulator listeners"));
650 panel.add(sub, "growx, growy");
653 DescriptionArea desc = new DescriptionArea(5);
654 desc.setText("<html>" +
655 "<i>Simulation listeners</i> is an advanced feature that allows "+
656 "user-written code to listen to and interact with the simulation. " +
657 "For details on writing simulation listeners, see the OpenRocket " +
658 "technical documentation.");
659 sub.add(desc, "aligny 0, growx, wrap para");
662 label = new JLabel("Current listeners:");
663 sub.add(label, "spanx, wrap rel");
665 final ListenerListModel listenerModel = new ListenerListModel();
666 final JList list = new JList(listenerModel);
667 list.setCellRenderer(new ListenerCellRenderer());
668 JScrollPane scroll = new JScrollPane(list);
669 // scroll.setPreferredSize(new Dimension(1,1));
670 sub.add(scroll, "height 1px, grow, wrap rel");
673 button = new JButton("Add");
674 button.addActionListener(new ActionListener() {
676 public void actionPerformed(ActionEvent e) {
677 String previous = Prefs.NODE.get("previousListenerName", "");
678 String input = (String)JOptionPane.showInputDialog(SimulationEditDialog.this,
680 "Type the full Java class name of the simulation listener, for example:",
681 "<html><tt>" + CSVSaveListener.class.getName() + "</tt>" },
682 "Add simulation listener",
683 JOptionPane.QUESTION_MESSAGE,
687 if (input == null || input.equals(""))
690 Prefs.NODE.put("previousListenerName", input);
691 simulation.getSimulationListeners().add(input);
692 listenerModel.fireContentsChanged();
695 sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
697 button = new JButton("Remove");
698 button.addActionListener(new ActionListener() {
700 public void actionPerformed(ActionEvent e) {
701 int[] selected = list.getSelectedIndices();
702 Arrays.sort(selected);
703 for (int i=selected.length-1; i>=0; i--) {
704 simulation.getSimulationListeners().remove(selected[i]);
706 listenerModel.fireContentsChanged();
709 sub.add(button, "sizegroup buttons, alignx 50%");
716 private class ListenerListModel extends AbstractListModel {
718 public String getElementAt(int index) {
719 if (index < 0 || index >= getSize())
721 return simulation.getSimulationListeners().get(index);
724 public int getSize() {
725 return simulation.getSimulationListeners().size();
727 public void fireContentsChanged() {
728 super.fireContentsChanged(this, 0, getSize());
736 * A panel for plotting the previously calculated data.
738 private JPanel plotTab() {
740 // Check that data exists
741 if (simulation.getSimulatedData() == null ||
742 simulation.getSimulatedData().getBranchCount() == 0) {
743 return noDataPanel();
748 return new PlotPanel(simulation);
750 JPanel panel = new JPanel(new MigLayout("fill"));
755 JButton button = new JButton("test");
757 button.addActionListener(new ActionListener() {
759 public void actionPerformed(ActionEvent e) {
760 PlotConfiguration config = new PlotConfiguration();
761 config.addPlotDataType(FlightDataBranch.TYPE_ALTITUDE);
762 config.addPlotDataType(FlightDataBranch.TYPE_VELOCITY_Z);
763 config.addPlotDataType(FlightDataBranch.TYPE_ACCELERATION_Z);
764 config.addPlotDataType(FlightDataBranch.TYPE_ACCELERATION_TOTAL);
766 config.setDomainAxisType(FlightDataBranch.TYPE_TIME);
779 * A panel for exporting the data.
781 private JPanel exportTab() {
782 FlightData data = simulation.getSimulatedData();
784 // Check that data exists
785 if (data == null || data.getBranchCount() == 0 ||
786 data.getBranch(0).getTypes().length == 0) {
787 return noDataPanel();
790 return new SimulationExportPanel(simulation);
798 * Return a panel stating that there is no data available, and that the user
799 * should run the simulation first.
801 public static JPanel noDataPanel() {
802 JPanel panel = new JPanel(new MigLayout("fill"));
805 panel.add(new JLabel("No flight data available."),
806 "alignx 50%, aligny 100%, wrap para");
807 panel.add(new JLabel("Please run the simulation first."),
808 "alignx 50%, aligny 0%, wrap");
813 private void performPlot(PlotConfiguration config) {
815 // Fill the auto-selections
816 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
817 PlotConfiguration filled = config.fillAutoAxes(branch);
818 List<Axis> axes = filled.getAllAxes();
821 // Create the data series for both axes
822 XYSeriesCollection[] data = new XYSeriesCollection[2];
823 data[0] = new XYSeriesCollection();
824 data[1] = new XYSeriesCollection();
827 // Get the domain axis type
828 final FlightDataBranch.Type domainType = filled.getDomainAxisType();
829 final Unit domainUnit = filled.getDomainAxisUnit();
830 if (domainType == null) {
831 throw new IllegalArgumentException("Domain axis type not specified.");
833 List<Double> x = branch.get(domainType);
836 // Create the XYSeries objects from the flight data and store into the collections
837 int length = filled.getTypeCount();
838 String[] axisLabel = new String[2];
839 for (int i = 0; i < length; i++) {
841 FlightDataBranch.Type type = filled.getType(i);
842 Unit unit = filled.getUnit(i);
843 int axis = filled.getAxis(i);
844 String name = getLabel(type, unit);
846 // Store data in provided units
847 List<Double> y = branch.get(type);
848 XYSeries series = new XYSeries(name, false, true);
849 for (int j=0; j<x.size(); j++) {
850 series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j)));
852 data[axis].addSeries(series);
855 if (axisLabel[axis] == null)
856 axisLabel[axis] = type.getName();
858 axisLabel[axis] += "; " + type.getName();
862 // Create the chart using the factory to get all default settings
863 JFreeChart chart = ChartFactory.createXYLineChart(
868 PlotOrientation.VERTICAL,
875 // Add the data and formatting to the plot
876 XYPlot plot = chart.getXYPlot();
878 for (int i=0; i<2; i++) {
879 // Check whether axis has any data
880 if (data[i].getSeriesCount() > 0) {
881 // Create and set axis
882 double min = axes.get(i).getMinValue();
883 double max = axes.get(i).getMaxValue();
884 NumberAxis axis = new PresetNumberAxis(min, max);
885 axis.setLabel(axisLabel[i]);
886 // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue());
887 plot.setRangeAxis(axisno, axis);
889 // Add data and map to the axis
890 plot.setDataset(axisno, data[i]);
891 plot.setRenderer(axisno, new StandardXYItemRenderer());
892 plot.mapDatasetToRangeAxis(axisno, axisno);
897 plot.getDomainAxis().setLabel(getLabel(domainType,domainUnit));
898 plot.addDomainMarker(new ValueMarker(0));
899 plot.addRangeMarker(new ValueMarker(0));
903 final JDialog dialog = new JDialog(this, "Simulation results");
904 dialog.setModalityType(ModalityType.DOCUMENT_MODAL);
906 JPanel panel = new JPanel(new MigLayout("fill"));
909 ChartPanel chartPanel = new ChartPanel(chart,
915 chartPanel.setMouseWheelEnabled(true);
916 chartPanel.setEnforceFileExtensions(true);
917 chartPanel.setInitialDelay(500);
919 chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
921 panel.add(chartPanel, "grow, wrap 20lp");
923 JButton button = new JButton("Close");
924 button.addActionListener(new ActionListener() {
926 public void actionPerformed(ActionEvent e) {
927 dialog.setVisible(false);
930 panel.add(button, "right");
932 dialog.setLocationByPlatform(true);
935 GUIUtil.setDisposableDialogOptions(dialog, button);
937 dialog.setVisible(true);
941 private class PresetNumberAxis extends NumberAxis {
942 private final double min;
943 private final double max;
945 public PresetNumberAxis(double min, double max) {
952 protected void autoAdjustRange() {
953 this.setRange(min, max);
958 private String getLabel(FlightDataBranch.Type type, Unit unit) {
959 String name = type.getName();
960 if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
961 !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0)
962 name += " ("+unit.getUnit() + ")";
968 private class ListenerCellRenderer extends JLabel implements ListCellRenderer {
970 public Component getListCellRendererComponent(JList list, Object value,
971 int index, boolean isSelected, boolean cellHasFocus) {
972 String s = value.toString();
975 // Attempt instantiating, catch any exceptions
978 Class<?> c = Class.forName(s);
979 @SuppressWarnings("unused")
980 SimulationListener l = (SimulationListener)c.newInstance();
981 } catch (Exception e) {
986 setIcon(Icons.SIMULATION_LISTENER_OK);
987 setToolTipText("Listener instantiated successfully.");
989 setIcon(Icons.SIMULATION_LISTENER_ERROR);
990 setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
995 setBackground(list.getSelectionBackground());
996 setForeground(list.getSelectionForeground());
998 setBackground(list.getBackground());
999 setForeground(list.getForeground());