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.UnitSelector;
42 import net.sf.openrocket.gui.plot.Axis;
43 import net.sf.openrocket.gui.plot.PlotConfiguration;
44 import net.sf.openrocket.gui.plot.PlotPanel;
45 import net.sf.openrocket.rocketcomponent.Configuration;
46 import net.sf.openrocket.simulation.FlightData;
47 import net.sf.openrocket.simulation.FlightDataBranch;
48 import net.sf.openrocket.simulation.RK4Simulator;
49 import net.sf.openrocket.simulation.SimulationConditions;
50 import net.sf.openrocket.simulation.SimulationListener;
51 import net.sf.openrocket.simulation.listeners.CSVSaveListener;
52 import net.sf.openrocket.unit.Unit;
53 import net.sf.openrocket.unit.UnitGroup;
54 import net.sf.openrocket.util.GUIUtil;
55 import net.sf.openrocket.util.Icons;
56 import net.sf.openrocket.util.Prefs;
58 import org.jfree.chart.ChartFactory;
59 import org.jfree.chart.ChartPanel;
60 import org.jfree.chart.JFreeChart;
61 import org.jfree.chart.axis.NumberAxis;
62 import org.jfree.chart.plot.PlotOrientation;
63 import org.jfree.chart.plot.ValueMarker;
64 import org.jfree.chart.plot.XYPlot;
65 import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
66 import org.jfree.data.xy.XYSeries;
67 import org.jfree.data.xy.XYSeriesCollection;
70 public class SimulationEditDialog extends JDialog {
72 public static final int DEFAULT = -1;
73 public static final int EDIT = 1;
74 public static final int PLOT = 2;
77 private final Window parentWindow;
78 private final Simulation simulation;
79 private final SimulationConditions conditions;
80 private final Configuration configuration;
83 public SimulationEditDialog(Window parent, Simulation s) {
87 public SimulationEditDialog(Window parent, Simulation s, int tab) {
88 super(parent, "Edit simulation", JDialog.ModalityType.DOCUMENT_MODAL);
90 this.parentWindow = parent;
92 this.conditions = simulation.getConditions();
93 configuration = simulation.getConfiguration();
95 JPanel mainPanel = new JPanel(new MigLayout("fill","[grow, fill]"));
97 mainPanel.add(new JLabel("Simulation name: "), "span, split 2, shrink");
98 final JTextField field = new JTextField(simulation.getName());
99 field.getDocument().addDocumentListener(new DocumentListener() {
101 public void changedUpdate(DocumentEvent e) {
105 public void insertUpdate(DocumentEvent e) {
109 public void removeUpdate(DocumentEvent e) {
112 private void setText() {
113 String name = field.getText();
114 if (name == null || name.equals(""))
116 System.out.println("Setting name:"+name);
117 simulation.setName(name);
121 mainPanel.add(field, "shrinky, growx, wrap");
123 JTabbedPane tabbedPane = new JTabbedPane();
126 tabbedPane.addTab("Launch conditions", flightConditionsTab());
127 tabbedPane.addTab("Simulation options", simulationOptionsTab());
128 tabbedPane.addTab("Plot data", plotTab());
129 // tabbedPane.addTab("Export data", exportTab());
131 // Select the initial tab
133 tabbedPane.setSelectedIndex(0);
134 } else if (tab == PLOT) {
135 tabbedPane.setSelectedIndex(2);
137 FlightData data = s.getSimulatedData();
138 if (data == null || data.getBranchCount() == 0)
139 tabbedPane.setSelectedIndex(0);
141 tabbedPane.setSelectedIndex(2);
144 mainPanel.add(tabbedPane, "spanx, grow, wrap");
148 mainPanel.add(new JPanel(), "spanx, split, growx");
151 button = new JButton("Run simulation");
152 button.addActionListener(new ActionListener() {
154 public void actionPerformed(ActionEvent e) {
155 SimulationEditDialog.this.dispose();
156 SimulationRunDialog.runSimulations(parentWindow, simulation);
159 mainPanel.add(button, "gapright para");
162 JButton close = new JButton("Close");
163 close.addActionListener(new ActionListener() {
165 public void actionPerformed(ActionEvent e) {
166 SimulationEditDialog.this.dispose();
169 mainPanel.add(close, "");
175 this.setLocationByPlatform(true);
176 GUIUtil.setDefaultButton(button);
177 GUIUtil.installEscapeCloseOperation(this);
184 private JPanel flightConditionsTab() {
185 JPanel panel = new JPanel(new MigLayout("fill"));
194 JLabel label = new JLabel("Motor configuration:");
195 label.setToolTipText("Select the motor configuration to use.");
196 panel.add(label, "shrinkx, spanx, split 2");
198 JComboBox combo = new JComboBox(new MotorConfigurationModel(configuration));
199 combo.setToolTipText("Select the motor configuration to use.");
200 combo.addActionListener(new ActionListener() {
202 public void actionPerformed(ActionEvent e) {
203 conditions.setMotorConfigurationID(configuration.getMotorConfigurationID());
206 panel.add(combo, "growx, wrap para");
209 //// Wind settings: Average wind speed, turbulence intensity, std. deviation
210 sub = new JPanel(new MigLayout("fill, gap rel unrel",
211 "[grow][65lp!][30lp!][75lp!]",""));
212 sub.setBorder(BorderFactory.createTitledBorder("Wind"));
213 panel.add(sub, "growx, split 2, aligny 0, flowy, gapright para");
217 label = new JLabel("Average windspeed:");
218 tip = "The average windspeed relative to the ground.";
219 label.setToolTipText(tip);
222 m = new DoubleModel(conditions,"WindSpeedAverage", UnitGroup.UNITS_VELOCITY,0);
224 spin = new JSpinner(m.getSpinnerModel());
225 spin.setEditor(new SpinnerEditor(spin));
226 spin.setToolTipText(tip);
227 sub.add(spin,"w 65lp!");
229 unit = new UnitSelector(m);
230 unit.setToolTipText(tip);
231 sub.add(unit,"growx");
232 slider = new BasicSlider(m.getSliderModel(0, 10.0));
233 slider.setToolTipText(tip);
234 sub.add(slider,"w 75lp, wrap");
238 // Wind std. deviation
239 label = new JLabel("Standard deviation:");
240 tip = "<html>The standard deviation of the windspeed.<br>" +
241 "The windspeed is within twice the standard deviation from the average for " +
243 label.setToolTipText(tip);
246 m = new DoubleModel(conditions,"WindSpeedDeviation", UnitGroup.UNITS_VELOCITY,0);
247 DoubleModel m2 = new DoubleModel(conditions,"WindSpeedAverage", 0.25,
248 UnitGroup.UNITS_COEFFICIENT,0);
250 spin = new JSpinner(m.getSpinnerModel());
251 spin.setEditor(new SpinnerEditor(spin));
252 spin.setToolTipText(tip);
253 sub.add(spin,"w 65lp!");
255 unit = new UnitSelector(m);
256 unit.setToolTipText(tip);
257 sub.add(unit,"growx");
258 slider = new BasicSlider(m.getSliderModel(new DoubleModel(0), m2));
259 slider.setToolTipText(tip);
260 sub.add(slider,"w 75lp, wrap");
263 // Wind turbulence intensity
264 label = new JLabel("Turbulence intensity:");
265 tip = "<html>The turbulence intensity is the standard deviation " +
266 "divided by the average windspeed.<br>" +
267 "Typical values range from "+
268 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.05) +
270 UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.20) + ".";
271 label.setToolTipText(tip);
274 m = new DoubleModel(conditions,"WindTurbulenceIntensity", UnitGroup.UNITS_RELATIVE,0);
276 spin = new JSpinner(m.getSpinnerModel());
277 spin.setEditor(new SpinnerEditor(spin));
278 spin.setToolTipText(tip);
279 sub.add(spin,"w 65lp!");
281 unit = new UnitSelector(m);
282 unit.setToolTipText(tip);
283 sub.add(unit,"growx");
285 final JLabel intensityLabel = new JLabel(
286 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
287 intensityLabel.setToolTipText(tip);
288 sub.add(intensityLabel,"w 75lp, wrap");
289 m.addChangeListener(new ChangeListener() {
291 public void stateChanged(ChangeEvent e) {
292 intensityLabel.setText(
293 getIntensityDescription(conditions.getWindTurbulenceIntensity()));
301 //// Temperature and pressure
302 sub = new JPanel(new MigLayout("fill, gap rel unrel",
303 "[grow][65lp!][30lp!][75lp!]",""));
304 sub.setBorder(BorderFactory.createTitledBorder("Atmospheric conditions"));
305 panel.add(sub, "growx, aligny 0, gapright para");
308 BooleanModel isa = new BooleanModel(conditions, "ISAAtmosphere");
309 JCheckBox check = new JCheckBox(isa);
310 check.setText("Use International Standard Atmosphere");
311 check.setToolTipText("<html>Select to use the International Standard Atmosphere model."+
312 "<br>This model has a temperature of " +
313 UnitGroup.UNITS_TEMPERATURE.toStringUnit(ExtendedISAModel.STANDARD_TEMPERATURE)+
314 " and a pressure of " +
315 UnitGroup.UNITS_PRESSURE.toStringUnit(ExtendedISAModel.STANDARD_PRESSURE) +
317 sub.add(check, "spanx, wrap unrel");
320 label = new JLabel("Temperature:");
321 tip = "The temperature at the launch site.";
322 label.setToolTipText(tip);
323 isa.addEnableComponent(label, false);
326 m = new DoubleModel(conditions,"LaunchTemperature", UnitGroup.UNITS_TEMPERATURE,0);
328 spin = new JSpinner(m.getSpinnerModel());
329 spin.setEditor(new SpinnerEditor(spin));
330 spin.setToolTipText(tip);
331 isa.addEnableComponent(spin, false);
332 sub.add(spin,"w 65lp!");
334 unit = new UnitSelector(m);
335 unit.setToolTipText(tip);
336 isa.addEnableComponent(unit, false);
337 sub.add(unit,"growx");
338 slider = new BasicSlider(m.getSliderModel(253.15, 308.15)); // -20 ... 35
339 slider.setToolTipText(tip);
340 isa.addEnableComponent(slider, false);
341 sub.add(slider,"w 75lp, wrap");
346 label = new JLabel("Pressure:");
347 tip = "The atmospheric pressure at the launch site.";
348 label.setToolTipText(tip);
349 isa.addEnableComponent(label, false);
352 m = new DoubleModel(conditions,"LaunchPressure", UnitGroup.UNITS_PRESSURE,0);
354 spin = new JSpinner(m.getSpinnerModel());
355 spin.setEditor(new SpinnerEditor(spin));
356 spin.setToolTipText(tip);
357 isa.addEnableComponent(spin, false);
358 sub.add(spin,"w 65lp!");
360 unit = new UnitSelector(m);
361 unit.setToolTipText(tip);
362 isa.addEnableComponent(unit, false);
363 sub.add(unit,"growx");
364 slider = new BasicSlider(m.getSliderModel(0.950e5, 1.050e5));
365 slider.setToolTipText(tip);
366 isa.addEnableComponent(slider, false);
367 sub.add(slider,"w 75lp, wrap");
373 //// Launch site conditions
374 sub = new JPanel(new MigLayout("fill, gap rel unrel",
375 "[grow][65lp!][30lp!][75lp!]",""));
376 sub.setBorder(BorderFactory.createTitledBorder("Launch site"));
377 panel.add(sub, "growx, split 2, aligny 0, flowy");
381 label = new JLabel("Latitude:");
382 tip = "<html>The launch site latitude affects the gravitational pull of Earth.<br>" +
383 "Positive values are on the Northern hemisphere, negative values on the " +
384 "Southern hemisphere.";
385 label.setToolTipText(tip);
388 m = new DoubleModel(conditions,"LaunchLatitude", UnitGroup.UNITS_NONE, -90, 90);
390 spin = new JSpinner(m.getSpinnerModel());
391 spin.setEditor(new SpinnerEditor(spin));
392 spin.setToolTipText(tip);
393 sub.add(spin,"w 65lp!");
395 label = new JLabel("\u00b0 N");
396 label.setToolTipText(tip);
397 sub.add(label,"growx");
398 slider = new BasicSlider(m.getSliderModel(-90, 90));
399 slider.setToolTipText(tip);
400 sub.add(slider,"w 75lp, wrap");
405 label = new JLabel("Altitude:");
406 tip = "<html>The launch altitude above mean sea level.<br>" +
407 "This affects the position of the rocket in the atmospheric model.";
408 label.setToolTipText(tip);
411 m = new DoubleModel(conditions,"LaunchAltitude", UnitGroup.UNITS_DISTANCE,0);
413 spin = new JSpinner(m.getSpinnerModel());
414 spin.setEditor(new SpinnerEditor(spin));
415 spin.setToolTipText(tip);
416 sub.add(spin,"w 65lp!");
418 unit = new UnitSelector(m);
419 unit.setToolTipText(tip);
420 sub.add(unit,"growx");
421 slider = new BasicSlider(m.getSliderModel(0, 250, 1000));
422 slider.setToolTipText(tip);
423 sub.add(slider,"w 75lp, wrap");
430 sub = new JPanel(new MigLayout("fill, gap rel unrel",
431 "[grow][65lp!][30lp!][75lp!]",""));
432 sub.setBorder(BorderFactory.createTitledBorder("Launch rod"));
433 panel.add(sub, "growx, aligny 0, wrap");
437 label = new JLabel("Length:");
438 tip = "The length of the launch rod.";
439 label.setToolTipText(tip);
442 m = new DoubleModel(conditions,"LaunchRodLength", UnitGroup.UNITS_LENGTH, 0);
444 spin = new JSpinner(m.getSpinnerModel());
445 spin.setEditor(new SpinnerEditor(spin));
446 spin.setToolTipText(tip);
447 sub.add(spin,"w 65lp!");
449 unit = new UnitSelector(m);
450 unit.setToolTipText(tip);
451 sub.add(unit,"growx");
452 slider = new BasicSlider(m.getSliderModel(0, 1, 5));
453 slider.setToolTipText(tip);
454 sub.add(slider,"w 75lp, wrap");
459 label = new JLabel("Angle:");
460 tip = "The angle of the launch rod from vertical.";
461 label.setToolTipText(tip);
464 m = new DoubleModel(conditions,"LaunchRodAngle", UnitGroup.UNITS_ANGLE,
465 0, SimulationConditions.MAX_LAUNCH_ROD_ANGLE);
467 spin = new JSpinner(m.getSpinnerModel());
468 spin.setEditor(new SpinnerEditor(spin));
469 spin.setToolTipText(tip);
470 sub.add(spin,"w 65lp!");
472 unit = new UnitSelector(m);
473 unit.setToolTipText(tip);
474 sub.add(unit,"growx");
475 slider = new BasicSlider(m.getSliderModel(0, Math.PI/9,
476 SimulationConditions.MAX_LAUNCH_ROD_ANGLE));
477 slider.setToolTipText(tip);
478 sub.add(slider,"w 75lp, wrap");
483 label = new JLabel("Direction:");
484 tip = "<html>Direction of the launch rod relative to the wind.<br>" +
485 UnitGroup.UNITS_ANGLE.toStringUnit(0) +
486 " = towards the wind, "+
487 UnitGroup.UNITS_ANGLE.toStringUnit(Math.PI) +
489 label.setToolTipText(tip);
492 m = new DoubleModel(conditions,"LaunchRodDirection", UnitGroup.UNITS_ANGLE,
495 spin = new JSpinner(m.getSpinnerModel());
496 spin.setEditor(new SpinnerEditor(spin));
497 spin.setToolTipText(tip);
498 sub.add(spin,"w 65lp!");
500 unit = new UnitSelector(m);
501 unit.setToolTipText(tip);
502 sub.add(unit,"growx");
503 slider = new BasicSlider(m.getSliderModel(-Math.PI, Math.PI));
504 slider.setToolTipText(tip);
505 sub.add(slider,"w 75lp, wrap");
511 private String getIntensityDescription(double i) {
529 private JPanel simulationOptionsTab() {
530 JPanel panel = new JPanel(new MigLayout("fill"));
540 //// Simulation options
541 sub = new JPanel(new MigLayout("fill, gap rel unrel",
542 "[grow][65lp!][30lp!][75lp!]",""));
543 sub.setBorder(BorderFactory.createTitledBorder("Simulator options"));
544 panel.add(sub, "w 330lp!, growy, aligny 0");
547 // Calculation method
549 "The Extended Barrowman method calculates aerodynamic forces according <br>" +
550 "to the Barrowman equations extended to accommodate more components.";
552 label = new JLabel("Calculation method:");
553 label.setToolTipText(tip);
554 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
556 label = new JLabel("Extended Barrowman");
557 label.setToolTipText(tip);
558 sub.add(label, "growx, wrap para");
563 "The six degree-of-freedom simulator allows the rocket total freedom during " +
565 "Integration is performed using a 4<sup>th</sup> order Runge-Kutta 4 " +
566 "numerical integration.";
568 label = new JLabel("Simulation method:");
569 label.setToolTipText(tip);
570 sub.add(label, "gaptop unrel, gapright para, spanx, split 2, w 150lp!");
572 label = new JLabel("6-DOF Runge-Kutta 4");
573 label.setToolTipText(tip);
574 sub.add(label, "growx, wrap 35lp");
578 label = new JLabel("Time step:");
579 tip = "<html>The time between simulation steps.<br>" +
580 "A smaller time step results in a more accurate but slower simulation.<br>" +
581 "The 4<sup>th</sup> order simulation method is quite accurate with a time " +
583 UnitGroup.UNITS_TIME_STEP.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
585 label.setToolTipText(tip);
588 m = new DoubleModel(conditions,"TimeStep", UnitGroup.UNITS_TIME_STEP, 0, 1);
590 spin = new JSpinner(m.getSpinnerModel());
591 spin.setEditor(new SpinnerEditor(spin));
592 spin.setToolTipText(tip);
593 sub.add(spin,"w 65lp!");
595 unit = new UnitSelector(m);
596 unit.setToolTipText(tip);
597 sub.add(unit,"growx");
598 slider = new BasicSlider(m.getSliderModel(0, 0.2));
599 slider.setToolTipText(tip);
600 sub.add(slider,"w 75lp, wrap");
604 // Maximum angle step
606 label = new JLabel("Max. angle step:");
608 "This defines the maximum angle the rocket will turn during one time step.<br>"+
609 "Smaller values result in a more accurate but possibly slower simulation.<br>"+
610 "A recommended value is " +
611 UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".";
612 label.setToolTipText(tip);
615 m = new DoubleModel(conditions,"MaximumStepAngle", UnitGroup.UNITS_ANGLE,
616 1*Math.PI/180, Math.PI/9);
618 spin = new JSpinner(m.getSpinnerModel());
619 spin.setEditor(new SpinnerEditor(spin));
620 spin.setToolTipText(tip);
621 sub.add(spin,"w 65lp!");
623 unit = new UnitSelector(m);
624 unit.setToolTipText(tip);
625 sub.add(unit,"growx");
626 slider = new BasicSlider(m.getSliderModel(0, Math.toRadians(10)));
627 slider.setToolTipText(tip);
628 sub.add(slider,"w 75lp, wrap para");
631 JButton button = new JButton("Reset to default");
632 button.setToolTipText("Reset the time step to its default value (" +
633 UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
636 // button.setToolTipText("<html>Reset the step value to its default:<br>" +
638 // UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4Simulator.RECOMMENDED_TIME_STEP) +
639 // "; maximum angle step " +
640 // UnitGroup.UNITS_ANGLE.toStringUnit(RK4Simulator.RECOMMENDED_ANGLE_STEP) + ".");
641 sub.add(button, "spanx, tag right, wrap para");
646 //// Simulation listeners
647 sub = new JPanel(new MigLayout("fill, gap 0 0"));
648 sub.setBorder(BorderFactory.createTitledBorder("Simulator listeners"));
649 panel.add(sub, "growx, growy");
652 DescriptionArea desc = new DescriptionArea(5, -1);
653 desc.setText("<html><p>" +
654 "<i>Simulation listeners</i> is an advanced feature that allows "+
655 "user-written code to listen to and interact with the simulation. " +
656 "For details on writing simulation listeners, see the OpenRocket " +
657 "technical documentation.</p>");
658 sub.add(desc, "aligny 0, growx, wrap para");
661 label = new JLabel("Current listeners:");
662 sub.add(label, "spanx, wrap rel");
664 final ListenerListModel listenerModel = new ListenerListModel();
665 final JList list = new JList(listenerModel);
666 list.setCellRenderer(new ListenerCellRenderer());
667 JScrollPane scroll = new JScrollPane(list);
668 // scroll.setPreferredSize(new Dimension(1,1));
669 sub.add(scroll, "height 1px, grow, wrap rel");
672 button = new JButton("Add");
673 button.addActionListener(new ActionListener() {
675 public void actionPerformed(ActionEvent e) {
676 String previous = Prefs.NODE.get("previousListenerName", "");
677 String input = (String)JOptionPane.showInputDialog(SimulationEditDialog.this,
679 "Type the full Java class name of the simulation listener, for example:",
680 "<html><tt>" + CSVSaveListener.class.getName() + "</tt>" },
681 "Add simulation listener",
682 JOptionPane.QUESTION_MESSAGE,
686 if (input == null || input.equals(""))
689 Prefs.NODE.put("previousListenerName", input);
690 simulation.getSimulationListeners().add(input);
691 listenerModel.fireContentsChanged();
694 sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
696 button = new JButton("Remove");
697 button.addActionListener(new ActionListener() {
699 public void actionPerformed(ActionEvent e) {
700 int[] selected = list.getSelectedIndices();
701 Arrays.sort(selected);
702 for (int i=selected.length-1; i>=0; i--) {
703 simulation.getSimulationListeners().remove(selected[i]);
705 listenerModel.fireContentsChanged();
708 sub.add(button, "sizegroup buttons, alignx 50%");
715 private class ListenerListModel extends AbstractListModel {
717 public String getElementAt(int index) {
718 if (index < 0 || index >= getSize())
720 return simulation.getSimulationListeners().get(index);
723 public int getSize() {
724 return simulation.getSimulationListeners().size();
726 public void fireContentsChanged() {
727 super.fireContentsChanged(this, 0, getSize());
735 * A panel for plotting the previously calculated data.
737 private JPanel plotTab() {
739 // Check that data exists
740 if (simulation.getSimulatedData() == null ||
741 simulation.getSimulatedData().getBranchCount() == 0) {
742 return noDataPanel();
747 return new PlotPanel(simulation);
749 JPanel panel = new JPanel(new MigLayout("fill"));
754 JButton button = new JButton("test");
756 button.addActionListener(new ActionListener() {
758 public void actionPerformed(ActionEvent e) {
759 PlotConfiguration config = new PlotConfiguration();
760 config.addPlotDataType(FlightDataBranch.TYPE_ALTITUDE);
761 config.addPlotDataType(FlightDataBranch.TYPE_VELOCITY_Z);
762 config.addPlotDataType(FlightDataBranch.TYPE_ACCELERATION_Z);
763 config.addPlotDataType(FlightDataBranch.TYPE_ACCELERATION_TOTAL);
765 config.setDomainAxisType(FlightDataBranch.TYPE_TIME);
778 * A panel for exporting the data.
780 private JPanel exportTab() {
782 // Check that data exists
783 if (simulation.getSimulatedData() == null ||
784 simulation.getSimulatedData().getBranchCount() == 0) {
785 return noDataPanel();
789 JPanel panel = new JPanel(new MigLayout("fill"));
791 panel.add(new JLabel("Not implemented yet.")); // TODO: HIGH: Implement export
801 * Return a panel stating that there is no data available, and that the user
802 * should run the simulation first.
804 private JPanel noDataPanel() {
805 JPanel panel = new JPanel(new MigLayout("fill"));
808 panel.add(new JLabel("No flight data available."),
809 "alignx 50%, aligny 100%, wrap para");
810 panel.add(new JLabel("Please run the simulation first."),
811 "alignx 50%, aligny 0%, wrap");
816 private void performPlot(PlotConfiguration config) {
818 // Fill the auto-selections
819 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
820 PlotConfiguration filled = config.fillAutoAxes(branch);
821 List<Axis> axes = filled.getAllAxes();
824 // Create the data series for both axes
825 XYSeriesCollection[] data = new XYSeriesCollection[2];
826 data[0] = new XYSeriesCollection();
827 data[1] = new XYSeriesCollection();
830 // Get the domain axis type
831 final FlightDataBranch.Type domainType = filled.getDomainAxisType();
832 final Unit domainUnit = filled.getDomainAxisUnit();
833 if (domainType == null) {
834 throw new IllegalArgumentException("Domain axis type not specified.");
836 List<Double> x = branch.get(domainType);
839 // Create the XYSeries objects from the flight data and store into the collections
840 int length = filled.getTypeCount();
841 String[] axisLabel = new String[2];
842 for (int i = 0; i < length; i++) {
844 FlightDataBranch.Type type = filled.getType(i);
845 Unit unit = filled.getUnit(i);
846 int axis = filled.getAxis(i);
847 String name = getLabel(type, unit);
849 // Store data in provided units
850 List<Double> y = branch.get(type);
851 XYSeries series = new XYSeries(name, false, true);
852 for (int j=0; j<x.size(); j++) {
853 series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j)));
855 data[axis].addSeries(series);
858 if (axisLabel[axis] == null)
859 axisLabel[axis] = type.getName();
861 axisLabel[axis] += "; " + type.getName();
865 // Create the chart using the factory to get all default settings
866 JFreeChart chart = ChartFactory.createXYLineChart(
871 PlotOrientation.VERTICAL,
878 // Add the data and formatting to the plot
879 XYPlot plot = chart.getXYPlot();
881 for (int i=0; i<2; i++) {
882 // Check whether axis has any data
883 if (data[i].getSeriesCount() > 0) {
884 // Create and set axis
885 double min = axes.get(i).getMinValue();
886 double max = axes.get(i).getMaxValue();
887 NumberAxis axis = new PresetNumberAxis(min, max);
888 axis.setLabel(axisLabel[i]);
889 // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue());
890 plot.setRangeAxis(axisno, axis);
892 // Add data and map to the axis
893 plot.setDataset(axisno, data[i]);
894 plot.setRenderer(axisno, new StandardXYItemRenderer());
895 plot.mapDatasetToRangeAxis(axisno, axisno);
900 plot.getDomainAxis().setLabel(getLabel(domainType,domainUnit));
901 plot.addDomainMarker(new ValueMarker(0));
902 plot.addRangeMarker(new ValueMarker(0));
906 final JDialog dialog = new JDialog(this, "Simulation results");
907 dialog.setModalityType(ModalityType.DOCUMENT_MODAL);
909 JPanel panel = new JPanel(new MigLayout("fill"));
912 ChartPanel chartPanel = new ChartPanel(chart,
918 chartPanel.setMouseWheelEnabled(true);
919 chartPanel.setEnforceFileExtensions(true);
920 chartPanel.setInitialDelay(500);
922 chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
924 panel.add(chartPanel, "grow, wrap 20lp");
926 JButton button = new JButton("Close");
927 button.addActionListener(new ActionListener() {
929 public void actionPerformed(ActionEvent e) {
930 dialog.setVisible(false);
933 panel.add(button, "right");
935 dialog.setLocationByPlatform(true);
937 GUIUtil.installEscapeCloseOperation(dialog);
938 GUIUtil.setDefaultButton(button);
940 dialog.setVisible(true);
944 private class PresetNumberAxis extends NumberAxis {
945 private final double min;
946 private final double max;
948 public PresetNumberAxis(double min, double max) {
955 protected void autoAdjustRange() {
956 this.setRange(min, max);
961 private String getLabel(FlightDataBranch.Type type, Unit unit) {
962 String name = type.getName();
963 if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
964 !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0)
965 name += " ("+unit.getUnit() + ")";
971 private class ListenerCellRenderer extends JLabel implements ListCellRenderer {
973 public Component getListCellRendererComponent(JList list, Object value,
974 int index, boolean isSelected, boolean cellHasFocus) {
975 String s = value.toString();
978 // Attempt instantiating, catch any exceptions
981 Class<?> c = Class.forName(s);
982 @SuppressWarnings("unused")
983 SimulationListener l = (SimulationListener)c.newInstance();
984 } catch (Exception e) {
989 setIcon(Icons.SIMULATION_LISTENER_OK);
990 setToolTipText("Listener instantiated successfully.");
992 setIcon(Icons.SIMULATION_LISTENER_ERROR);
993 setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
998 setBackground(list.getSelectionBackground());
999 setForeground(list.getSelectionForeground());
1001 setBackground(list.getBackground());
1002 setForeground(list.getForeground());