diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index b48b42d5f..153afa599 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -306,6 +306,7 @@ PreferencesDialog.lbl.languageEffect = The language will change the next time yo ! Simulation edit dialog simedtdlg.but.runsimulation = Run simulation simedtdlg.but.resettodefault = Reset to default +simedtdlg.but.savedefault = Save as default simedtdlg.but.add = Add simedtdlg.but.remove = Remove simedtdlg.title.Editsim = Edit simulation @@ -438,12 +439,6 @@ SimuRunDlg.lbl.Altitude = Altitude: SimuRunDlg.lbl.Velocity = Velocity: SimuRunDlg.msg.Unabletosim = Unable to simulate: SimuRunDlg.msg.errorOccurred = An error occurred during the simulation: -SimuRunDlg.msg.AnException1 = An exception occurred during the simulation: -SimuRunDlg.msg.AnException2 = Please report this as a bug along with the details below. -SimuRunDlg.msg.AssertionError1 = A computation error occurred during the simulation. -SimuRunDlg.msg.AssertionError2 = Please report this as a bug along with the details below. -SimuRunDlg.msg.unknownerror1 = An unknown error was encountered during the simulation. -SimuRunDlg.msg.unknownerror2 = The program may be unstable, you should save all your designs and restart OpenRocket now! RK4SimulationStepper.error.valuesTooLarge = Simulation values exceeded limits. Try selecting a shorter time step. @@ -468,7 +463,6 @@ SimExpPan.checkbox.Incflightevents = Include flight events SimExpPan.checkbox.ttip.Incflightevents = Include a comment line for every flight event. SimExpPan.lbl.Commentchar = Comment character: SimExpPan.lbl.ttip.Commentchar = The character(s) that mark a comment line. -SimExpPan.but.Exporttofile = Export to file... SimExpPan.Fileexists.desc1 = File \" SimExpPan.Fileexists.desc2 = \" exists. Overwrite? SimExpPan.Fileexists.title = File exists @@ -581,7 +575,6 @@ simplotpanel.lbl.Flightevents = Flight events: simplotpanel.but.All = All simplotpanel.but.None = None simplotpanel.but.NewYaxisplottype = New Y axis plot type -simplotpanel.but.Plotflight = Plot flight simplotpanel.lbl.Axis = Axis: simplotpanel.but.ttip.Removethisplot = Remove this plot simplotpanel.Desc = The data will be plotted in time order even if the X axis type is not time. @@ -1394,6 +1387,7 @@ FlightEvent.Type.GROUND_HIT = Ground hit FlightEvent.Type.SIMULATION_END = Simulation end FlightEvent.Type.ALTITUDE = Altitude change FlightEvent.Type.TUMBLE = Tumbling +FlightEvent.Type.EXCEPTION = Exception ! ThrustCurveMotorColumns TCurveMotorCol.MANUFACTURER = Manufacturer diff --git a/core/src/net/sf/openrocket/aerodynamics/Warning.java b/core/src/net/sf/openrocket/aerodynamics/Warning.java index 363606d72..b06876c22 100644 --- a/core/src/net/sf/openrocket/aerodynamics/Warning.java +++ b/core/src/net/sf/openrocket/aerodynamics/Warning.java @@ -325,4 +325,7 @@ public abstract class Warning { public static final Warning RECOVERY_LAUNCH_ROD = new Other(trans.get("Warning.RECOVERY_LAUNCH_ROD")); + + public static final Warning SIMULATION_EXCEPTION = + new Other("Exception during simulation"); } diff --git a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java index 37df70c91..aa27ebc3a 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -23,9 +23,6 @@ import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableCellRenderer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.miginfocom.swing.MigLayout; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; @@ -38,6 +35,10 @@ import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.adaptors.Column; import net.sf.openrocket.gui.adaptors.ColumnTableModel; import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.simulation.SimulationEditDialog; +import net.sf.openrocket.gui.simulation.SimulationPlotExportDialog; +import net.sf.openrocket.gui.simulation.SimulationRunDialog; +import net.sf.openrocket.gui.simulation.SimulationWarningDialog; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -48,6 +49,9 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.unit.UnitGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class SimulationPanel extends JPanel { private static final Logger log = LoggerFactory.getLogger(SimulationPanel.class); private static final Translator trans = Application.getTranslator(); @@ -68,13 +72,14 @@ public class SimulationPanel extends JPanel { private final ColumnTableModel simulationTableModel; private final JTable simulationTable; + private final JButton editButton; + private final JButton runButton; + private final JButton deleteButton; + private final JButton plotButton; public SimulationPanel(OpenRocketDocument doc) { super(new MigLayout("fill", "[grow][][][][][][grow]")); - JButton button; - - this.document = doc; @@ -82,51 +87,54 @@ public class SimulationPanel extends JPanel { //////// The simulation action buttons //// New simulation button - button = new JButton(trans.get("simpanel.but.newsimulation")); - //// Add a new simulation - button.setToolTipText(trans.get("simpanel.but.ttip.newsimulation")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Simulation sim = new Simulation(document.getRocket()); - sim.setName(document.getNextSimulationName()); - - int n = document.getSimulationCount(); - document.addSimulation(sim); - simulationTableModel.fireTableDataChanged(); - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(n, n); - - openDialog(sim, SimulationEditDialog.EDIT); - } - }); - this.add(button, "skip 1, gapright para"); + { + JButton button = new JButton(trans.get("simpanel.but.newsimulation")); + //// Add a new simulation + button.setToolTipText(trans.get("simpanel.but.ttip.newsimulation")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Simulation sim = new Simulation(document.getRocket()); + sim.setName(document.getNextSimulationName()); + + int n = document.getSimulationCount(); + document.addSimulation(sim); + simulationTableModel.fireTableDataChanged(); + simulationTable.clearSelection(); + simulationTable.addRowSelectionInterval(n, n); + + openDialog(sim); + } + }); + this.add(button, "skip 1, gapright para"); + } //// Edit simulation button - button = new JButton(trans.get("simpanel.but.editsimulation")); + editButton = new JButton(trans.get("simpanel.but.editsimulation")); //// Edit the selected simulation - button.setToolTipText(trans.get("simpanel.but.ttip.editsim")); - button.addActionListener(new ActionListener() { + editButton.setToolTipText(trans.get("simpanel.but.ttip.editsim")); + editButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int selected = simulationTable.getSelectedRow(); - if (selected < 0) - return; // TODO: MEDIUM: "None selected" dialog + int[] selection = simulationTable.getSelectedRows(); + if (selection.length == 0) + return; // TODO: LOW: "None selected" dialog - selected = simulationTable.convertRowIndexToModel(selected); - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(selected, selected); - - openDialog(document.getSimulations().get(selected), SimulationEditDialog.EDIT); + Simulation[] sims = new Simulation[selection.length]; + for (int i = 0; i < selection.length; i++) { + selection[i] = simulationTable.convertRowIndexToModel(selection[i]); + sims[i] = document.getSimulation(selection[i]); + } + openDialog(sims); } }); - this.add(button, "gapright para"); + this.add(editButton, "gapright para"); //// Run simulations - button = new JButton(trans.get("simpanel.but.runsimulations")); + runButton = new JButton(trans.get("simpanel.but.runsimulations")); //// Re-run the selected simulations - button.setToolTipText(trans.get("simpanel.but.ttip.runsimu")); - button.addActionListener(new ActionListener() { + runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu")); + runButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int[] selection = simulationTable.getSelectedRows(); @@ -146,13 +154,13 @@ public class SimulationPanel extends JPanel { fireMaintainSelection(); } }); - this.add(button, "gapright para"); + this.add(runButton, "gapright para"); //// Delete simulations button - button = new JButton(trans.get("simpanel.but.deletesimulations")); + deleteButton = new JButton(trans.get("simpanel.but.deletesimulations")); //// Delete the selected simulations - button.setToolTipText(trans.get("simpanel.but.ttip.deletesim")); - button.addActionListener(new ActionListener() { + deleteButton.setToolTipText(trans.get("simpanel.but.ttip.deletesim")); + deleteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int[] selection = simulationTable.getSelectedRows(); @@ -201,12 +209,12 @@ public class SimulationPanel extends JPanel { simulationTableModel.fireTableDataChanged(); } }); - this.add(button, "gapright para"); + this.add(deleteButton, "gapright para"); //// Plot / export button - button = new JButton(trans.get("simpanel.but.plotexport")); + plotButton = new JButton(trans.get("simpanel.but.plotexport")); // button = new JButton("Plot flight"); - button.addActionListener(new ActionListener() { + plotButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int selected = simulationTable.getSelectedRow(); @@ -217,10 +225,21 @@ public class SimulationPanel extends JPanel { simulationTable.clearSelection(); simulationTable.addRowSelectionInterval(selected, selected); - openDialog(document.getSimulations().get(selected), SimulationEditDialog.PLOT); + + Simulation sim = document.getSimulations().get(selected); + + if (sim.getSimulatedData() == null || sim.getSimulatedData().getBranchCount() == 0) { + new SimulationRunDialog(SwingUtilities.getWindowAncestor( + SimulationPanel.this), document, sim).setVisible(true); + } + + new SimulationPlotExportDialog(SwingUtilities.getWindowAncestor(SimulationPanel.this), document, sim) + .setVisible(true); + fireMaintainSelection(); + } }); - this.add(button, "wrap para"); + this.add(plotButton, "wrap para"); @@ -474,13 +493,20 @@ public class SimulationPanel extends JPanel { int selected = simulationTable.getSelectedRow(); if (selected < 0) return; - selected = simulationTable.convertRowIndexToModel(selected); - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(selected, selected); - openDialog(document.getSimulations().get(selected), - SimulationEditDialog.DEFAULT); + int column = simulationTable.columnAtPoint(e.getPoint()); + if (column == 0) { + SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected)); + } else { + simulationTable.clearSelection(); + simulationTable.addRowSelectionInterval(selected, selected); + + // FIXME - do we want to check to open plot dialog? + openDialog(document.getSimulations().get(selected)); + } + } else { + updateButtonStates(); } } }); @@ -509,17 +535,35 @@ public class SimulationPanel extends JPanel { JScrollPane scrollpane = new JScrollPane(simulationTable); this.add(scrollpane, "spanx, grow, wrap rel"); - + updateButtonStates(); } + private void updateButtonStates() { + int[] selection = simulationTable.getSelectedRows(); + if (selection.length == 0) { + editButton.setEnabled(false); + runButton.setEnabled(false); + deleteButton.setEnabled(false); + plotButton.setEnabled(false); + } else { + if (selection.length > 1) { + plotButton.setEnabled(false); + } else { + plotButton.setEnabled(true); + } + editButton.setEnabled(true); + runButton.setEnabled(true); + deleteButton.setEnabled(true); + } + + } public ListSelectionModel getSimulationListSelectionModel() { return simulationTable.getSelectionModel(); } - private void openDialog(final Simulation sim, int position) { - new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sim, position) - .setVisible(true); + private void openDialog(final Simulation... sims) { + new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sims).setVisible(true); fireMaintainSelection(); } diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java index a254275c1..05d2de63e 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.List; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.simulation.SimulationPlotPanel; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightEvent; diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index 1bb158549..95d18726b 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -7,8 +7,6 @@ import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -40,7 +38,7 @@ public class SimulationPlotDialog extends JDialog { private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) { //// Flight data plot super(parent, simulation.getName()); - this.setModalityType(ModalityType.DOCUMENT_MODAL); + this.setModalityType(ModalityType.MODELESS); final boolean initialShowPoints = Application.getPreferences().getBoolean(Preferences.PLOT_SHOW_POINTS, false); @@ -113,8 +111,8 @@ public class SimulationPlotDialog extends JDialog { //// Add series selection box ArrayList stages = new ArrayList(); stages.add("All"); - stages.addAll( Util.generateSeriesLabels(simulation)); - + stages.addAll(Util.generateSeriesLabels(simulation)); + final JComboBox stageSelection = new JComboBox(stages.toArray(new String[0])); stageSelection.addItemListener(new ItemListener() { @@ -159,8 +157,8 @@ public class SimulationPlotDialog extends JDialog { * @param simulation the simulation to plot. * @param config the configuration of the plot. */ - public static void showPlot(Window parent, Simulation simulation, PlotConfiguration config) { - new SimulationPlotDialog(parent, simulation, config).setVisible(true); + public static SimulationPlotDialog getPlot(Window parent, Simulation simulation, PlotConfiguration config) { + return new SimulationPlotDialog(parent, simulation, config); } } diff --git a/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java b/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java index 53a710c80..be4fbe6c9 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java +++ b/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java @@ -4,7 +4,7 @@ package net.sf.openrocket.gui.print; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.gui.main.SimulationWorker; +import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.simulation.FlightData; /** diff --git a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 70e9928e8..5ae1d1581 100644 --- a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -54,8 +54,8 @@ import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CPCaret; import net.sf.openrocket.gui.figureelements.Caret; import net.sf.openrocket.gui.figureelements.RocketInfo; -import net.sf.openrocket.gui.main.SimulationWorker; import net.sf.openrocket.gui.main.componenttree.ComponentTreeModel; +import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.masscalc.BasicMassCalculator; diff --git a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java similarity index 73% rename from core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java index 62175e4de..ab05b176a 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java @@ -1,13 +1,11 @@ -package net.sf.openrocket.gui.main; +package net.sf.openrocket.gui.simulation; -import java.awt.Color; import java.awt.Component; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; -import java.util.List; import javax.swing.AbstractListModel; import javax.swing.BorderFactory; @@ -40,139 +38,126 @@ import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.DescriptionArea; -import net.sf.openrocket.gui.components.SimulationExportPanel; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog; -import net.sf.openrocket.gui.plot.Axis; -import net.sf.openrocket.gui.plot.PlotConfiguration; -import net.sf.openrocket.gui.plot.SimulationPlotPanel; -import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.models.atmosphere.ExtendedISAModel; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.simulation.FlightData; -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.DefaultSimulationOptionFactory; import net.sf.openrocket.simulation.RK4SimulationStepper; import net.sf.openrocket.simulation.SimulationOptions; import net.sf.openrocket.simulation.listeners.SimulationListener; import net.sf.openrocket.simulation.listeners.example.CSVSaveListener; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Chars; import net.sf.openrocket.util.GeodeticComputationStrategy; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.ValueMarker; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.StandardXYItemRenderer; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; - public class SimulationEditDialog extends JDialog { - public static final int DEFAULT = -1; - public static final int EDIT = 1; - public static final int PLOT = 2; - - private final Window parentWindow; - private final Simulation simulation; + private final Simulation[] simulation; private final OpenRocketDocument document; private final SimulationOptions conditions; private final Configuration configuration; private static final Translator trans = Application.getTranslator(); - public SimulationEditDialog(Window parent, OpenRocketDocument document, Simulation s) { - this(parent, document, s, 0); - } - - public SimulationEditDialog(Window parent, OpenRocketDocument document, Simulation s, int tab) { + public SimulationEditDialog(Window parent, OpenRocketDocument document, Simulation... sims) { //// Edit simulation super(parent, trans.get("simedtdlg.title.Editsim"), JDialog.ModalityType.DOCUMENT_MODAL); this.document = document; this.parentWindow = parent; - this.simulation = s; - this.conditions = simulation.getOptions(); - configuration = simulation.getConfiguration(); + this.simulation = sims; + this.conditions = simulation[0].getOptions(); + configuration = simulation[0].getConfiguration(); - JPanel mainPanel = new JPanel(new MigLayout("fill", "[grow, fill]")); + JPanel mainPanel = new JPanel(new MigLayout("")); - //// Simulation name: - mainPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "span, split 2, shrink"); - final JTextField field = new JTextField(simulation.getName()); - field.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - setText(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - setText(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - setText(); - } - - private void setText() { - String name = field.getText(); - if (name == null || name.equals("")) - return; - //System.out.println("Setting name:" + name); - simulation.setName(name); + if (sims.length == 1) { + //// Simulation name: + mainPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "shrink"); + final JTextField field = new JTextField(simulation[0].getName()); + field.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + setText(); + } - } - }); - mainPanel.add(field, "shrinky, growx, wrap"); - + @Override + public void insertUpdate(DocumentEvent e) { + setText(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + setText(); + } + + private void setText() { + String name = field.getText(); + if (name == null || name.equals("")) + return; + //System.out.println("Setting name:" + name); + simulation[0].setName(name); + + } + }); + mainPanel.add(field, "shrinky, growx, wrap"); + + //// Flight selector + //// Flight configuration: + JLabel label = new JLabel(trans.get("simedtdlg.lbl.Flightcfg")); + //// Select the motor configuration to use. + label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg")); + mainPanel.add(label, "shrink"); + + JComboBox combo = new JComboBox(new FlightConfigurationModel(configuration)); + //// Select the motor configuration to use. + combo.setToolTipText(trans.get("simedtdlg.combo.ttip.Flightcfg")); + combo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + conditions.setMotorConfigurationID(configuration.getFlightConfigurationID()); + } + }); + mainPanel.add(combo, "split 2, shrink"); + + //// Edit button + JButton button = new JButton(trans.get("simedtdlg.but.FlightcfgEdit")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog configDialog = new FlightConfigurationDialog(SimulationEditDialog.this.document.getRocket(), SwingUtilities.windowForComponent(SimulationEditDialog.this)); + configDialog.setVisible(true); + } + }); + mainPanel.add(button, "shrink, wrap"); + } JTabbedPane tabbedPane = new JTabbedPane(); //// Launch conditions tabbedPane.addTab(trans.get("simedtdlg.tab.Launchcond"), flightConditionsTab()); //// Simulation options tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), simulationOptionsTab()); - //// Plot data - tabbedPane.addTab(trans.get("simedtdlg.tab.Plotdata"), plotTab()); - //// Export data - tabbedPane.addTab(trans.get("simedtdlg.tab.Exportdata"), exportTab()); - // Select the initial tab - if (tab == EDIT) { - tabbedPane.setSelectedIndex(0); - } else if (tab == PLOT) { - tabbedPane.setSelectedIndex(2); - } else { - FlightData data = s.getSimulatedData(); - if (data == null || data.getBranchCount() == 0) - tabbedPane.setSelectedIndex(0); - else - tabbedPane.setSelectedIndex(2); - } + tabbedPane.setSelectedIndex(0); mainPanel.add(tabbedPane, "spanx, grow, wrap"); - + // Buttons mainPanel.add(new JPanel(), "spanx, split, growx"); - JButton button; //// Run simulation button - button = new JButton(trans.get("simedtdlg.but.runsimulation")); + JButton button = new JButton(trans.get("simedtdlg.but.runsimulation")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + copyChangesToAllSims(); SimulationEditDialog.this.dispose(); SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulation); } @@ -184,12 +169,13 @@ public class SimulationEditDialog extends JDialog { close.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + copyChangesToAllSims(); SimulationEditDialog.this.dispose(); } }); mainPanel.add(close, ""); - + this.add(mainPanel); this.validate(); this.pack(); @@ -198,10 +184,19 @@ public class SimulationEditDialog extends JDialog { GUIUtil.setDisposableDialogOptions(this, button); } + private void copyChangesToAllSims() { + if (simulation.length > 1) { + for (int i = 1; i < simulation.length; i++) { + simulation[i].getOptions().copyConditionsFrom(simulation[0].getOptions()); + simulation[i].getSimulationListeners().clear(); + simulation[i].getSimulationListeners().addAll(simulation[0].getSimulationListeners()); + } + } + } + + + - - - private JPanel flightConditionsTab() { JPanel panel = new JPanel(new MigLayout("fill")); JPanel sub; @@ -211,35 +206,6 @@ public class SimulationEditDialog extends JDialog { DoubleModel m; JSpinner spin; - //// Flight selector - //// Flight configuration: - JLabel label = new JLabel(trans.get("simedtdlg.lbl.Flightcfg")); - //// Select the motor configuration to use. - label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg")); - panel.add(label, "shrinkx, spanx, split 2"); - - JComboBox combo = new JComboBox(new FlightConfigurationModel(configuration)); - //// Select the motor configuration to use. - combo.setToolTipText(trans.get("simedtdlg.combo.ttip.Flightcfg")); - combo.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - conditions.setMotorConfigurationID(configuration.getFlightConfigurationID()); - } - }); - panel.add(combo, ""); - - //// Edit button - JButton button = new JButton(trans.get("simedtdlg.but.FlightcfgEdit")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JDialog configDialog = new FlightConfigurationDialog(document.getRocket(),SwingUtilities.windowForComponent(SimulationEditDialog.this)); - configDialog.show(); - } - }); - panel.add(button, "wrap"); - //// Wind settings: Average wind speed, turbulence intensity, std. deviation sub = new JPanel(new MigLayout("fill, gap rel unrel", "[grow][65lp!][30lp!][75lp!]", "")); @@ -247,10 +213,10 @@ public class SimulationEditDialog extends JDialog { sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.lbl.Wind"))); panel.add(sub, "growx, split 2, aligny 0, flowy, gapright para"); - + // Wind average //// Average windspeed: - label = new JLabel(trans.get("simedtdlg.lbl.Averwindspeed")); + JLabel label = new JLabel(trans.get("simedtdlg.lbl.Averwindspeed")); //// The average windspeed relative to the ground. tip = trans.get("simedtdlg.lbl.ttip.Averwindspeed"); label.setToolTipText(tip); @@ -270,8 +236,8 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - - + + // Wind std. deviation //// Standard deviation: label = new JLabel(trans.get("simedtdlg.lbl.Stddeviation")); @@ -297,7 +263,7 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - + // Wind turbulence intensity //// Turbulence intensity: label = new JLabel(trans.get("simedtdlg.lbl.Turbulenceintensity")); @@ -335,10 +301,10 @@ public class SimulationEditDialog extends JDialog { } }); - - - - + + + + //// Temperature and pressure sub = new JPanel(new MigLayout("fill, gap rel unrel", "[grow][65lp!][30lp!][75lp!]", "")); @@ -346,7 +312,7 @@ public class SimulationEditDialog extends JDialog { sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Atmoscond"))); panel.add(sub, "growx, aligny 0, gapright para"); - + BooleanModel isa = new BooleanModel(conditions, "ISAAtmosphere"); JCheckBox check = new JCheckBox(isa); //// Use International Standard Atmosphere @@ -387,8 +353,8 @@ public class SimulationEditDialog extends JDialog { isa.addEnableComponent(slider, false); sub.add(slider, "w 75lp, wrap"); - - + + // Pressure: label = new JLabel(trans.get("simedtdlg.lbl.Pressure")); //// The atmospheric pressure at the launch site. @@ -414,10 +380,10 @@ public class SimulationEditDialog extends JDialog { isa.addEnableComponent(slider, false); sub.add(slider, "w 75lp, wrap"); - - - - + + + + //// Launch site conditions sub = new JPanel(new MigLayout("fill, gap rel unrel", "[grow][65lp!][30lp!][75lp!]", "")); @@ -425,7 +391,7 @@ public class SimulationEditDialog extends JDialog { sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.lbl.Launchsite"))); panel.add(sub, "growx, split 2, aligny 0, flowy"); - + // Latitude: label = new JLabel(trans.get("simedtdlg.lbl.Latitude")); //// The launch site latitude affects the gravitational pull of Earth.
@@ -448,7 +414,7 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - + // Longitude: label = new JLabel(trans.get("simedtdlg.lbl.Longitude")); tip = trans.get("simedtdlg.lbl.ttip.Longitude"); @@ -469,7 +435,7 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - + // Altitude: label = new JLabel(trans.get("simedtdlg.lbl.Altitude")); //// The launch altitude above mean sea level.
@@ -492,10 +458,10 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - - - - + + + + //// Launch rod sub = new JPanel(new MigLayout("fill, gap rel unrel", "[grow][65lp!][30lp!][75lp!]", "")); @@ -503,7 +469,7 @@ public class SimulationEditDialog extends JDialog { sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Launchrod"))); panel.add(sub, "growx, aligny 0, wrap"); - + // Length: label = new JLabel(trans.get("simedtdlg.lbl.Length")); //// The length of the launch rod. @@ -525,8 +491,8 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - - + + // Angle: label = new JLabel(trans.get("simedtdlg.lbl.Angle")); //// The angle of the launch rod from vertical. @@ -550,8 +516,8 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); - - + + // Direction: label = new JLabel(trans.get("simedtdlg.lbl.Direction")); //// Direction of the launch rod relative to the wind.
@@ -580,6 +546,35 @@ public class SimulationEditDialog extends JDialog { slider.setToolTipText(tip); sub.add(slider, "w 75lp, wrap"); + JButton restoreDefaults = new JButton(trans.get("simedtdlg.but.resettodefault")); + restoreDefaults.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + DefaultSimulationOptionFactory f = Application.getInjector().getInstance(DefaultSimulationOptionFactory.class); + SimulationOptions defaults = f.getDefault(); + conditions.copyConditionsFrom(defaults); + + } + + }); + panel.add(restoreDefaults, "span, split 3, skip, gapbottom para, gapright para, right"); + + JButton saveDefaults = new JButton(trans.get("simedtdlg.but.savedefault")); + saveDefaults.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + + DefaultSimulationOptionFactory f = Application.getInjector().getInstance(DefaultSimulationOptionFactory.class); + f.saveDefault(conditions); + + } + + }); + + panel.add(saveDefaults, "gapbottom para, gapright para, right"); return panel; } @@ -608,7 +603,7 @@ public class SimulationEditDialog extends JDialog { } - + private JPanel simulationOptionsTab() { JPanel panel = new JPanel(new MigLayout("fill")); JPanel sub, subsub; @@ -619,7 +614,7 @@ public class SimulationEditDialog extends JDialog { UnitSelector unit; BasicSlider slider; - + //// Simulation options sub = new JPanel(new MigLayout("fill, gap rel unrel", "[grow][65lp!][30lp!][75lp!]", "")); @@ -627,11 +622,11 @@ public class SimulationEditDialog extends JDialog { sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Simopt"))); panel.add(sub, "growx, growy, aligny 0"); - + // Separate panel for computation methods, as they use a different layout subsub = new JPanel(new MigLayout("insets 0, fill")); - + //// Calculation method: tip = trans.get("simedtdlg.lbl.ttip.Calcmethod"); label = new JLabel(trans.get("simedtdlg.lbl.Calcmethod")); @@ -643,7 +638,7 @@ public class SimulationEditDialog extends JDialog { label.setToolTipText(tip); subsub.add(label, "growx, wrap para"); - + // Simulation method tip = trans.get("simedtdlg.lbl.ttip.Simmethod1") + trans.get("simedtdlg.lbl.ttip.Simmethod2"); @@ -655,7 +650,7 @@ public class SimulationEditDialog extends JDialog { label.setToolTipText(tip); subsub.add(label, "growx, wrap para"); - + //// Geodetic calculation method: label = new JLabel(trans.get("simedtdlg.lbl.GeodeticMethod")); label.setToolTipText(trans.get("simedtdlg.lbl.ttip.GeodeticMethodTip")); @@ -676,7 +671,7 @@ public class SimulationEditDialog extends JDialog { sub.add(subsub, "spanx, wrap para"); - + //// Time step: label = new JLabel(trans.get("simedtdlg.lbl.Timestep")); tip = trans.get("simedtdlg.lbl.ttip.Timestep1") + @@ -703,9 +698,9 @@ public class SimulationEditDialog extends JDialog { sub.add(slider, "w 75lp, wrap"); //sub.add(slider,"wrap"); - - - + + + //// Reset to default button JButton button = new JButton(trans.get("simedtdlg.but.resettodefault")); //// Reset the time step to its default value ( @@ -722,16 +717,16 @@ public class SimulationEditDialog extends JDialog { sub.add(button, "align left"); - - - + + + //// Simulation listeners sub = new JPanel(new MigLayout("fill, gap 0 0")); //// Simulator listeners sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Simlist"))); panel.add(sub, "growx, growy"); - + DescriptionArea desc = new DescriptionArea(5); //// Simulation listeners is an advanced feature that allows user-written code to listen to and interact with the simulation. //// For details on writing simulation listeners, see the OpenRocket technical documentation. @@ -771,7 +766,7 @@ public class SimulationEditDialog extends JDialog { return; Application.getPreferences().putString("previousListenerName", input); - simulation.getSimulationListeners().add(input); + simulation[0].getSimulationListeners().add(input); listenerModel.fireContentsChanged(); } }); @@ -785,14 +780,14 @@ public class SimulationEditDialog extends JDialog { int[] selected = list.getSelectedIndices(); Arrays.sort(selected); for (int i = selected.length - 1; i >= 0; i--) { - simulation.getSimulationListeners().remove(selected[i]); + simulation[0].getSimulationListeners().remove(selected[i]); } listenerModel.fireContentsChanged(); } }); sub.add(button, "sizegroup buttons, alignx 50%"); - + return panel; } @@ -802,12 +797,12 @@ public class SimulationEditDialog extends JDialog { public String getElementAt(int index) { if (index < 0 || index >= getSize()) return null; - return simulation.getSimulationListeners().get(index); + return simulation[0].getSimulationListeners().get(index); } @Override public int getSize() { - return simulation.getSimulationListeners().size(); + return simulation[0].getSimulationListeners().size(); } public void fireContentsChanged() { @@ -816,40 +811,9 @@ public class SimulationEditDialog extends JDialog { } - - - /** - * A panel for plotting the previously calculated data. - */ - private JPanel plotTab() { - - // Check that data exists - if (simulation.getSimulatedData() == null || - simulation.getSimulatedData().getBranchCount() == 0) { - return noDataPanel(); - } - - return new SimulationPlotPanel(simulation); - } - - /** - * A panel for exporting the data. - */ - private JPanel exportTab() { - FlightData data = simulation.getSimulatedData(); - - // Check that data exists - if (data == null || data.getBranchCount() == 0 || - data.getBranch(0).getTypes().length == 0) { - return noDataPanel(); - } - - return new SimulationExportPanel(simulation); - } - /** * Return a panel stating that there is no data available, and that the user * should run the simulation first. @@ -868,165 +832,7 @@ public class SimulationEditDialog extends JDialog { } - @SuppressWarnings("unused") - private void performPlot(PlotConfiguration config) { - - // Fill the auto-selections - FlightDataBranch branch = simulation.getSimulatedData().getBranch(0); - PlotConfiguration filled = config.fillAutoAxes(branch); - List axes = filled.getAllAxes(); - - - // Create the data series for both axes - XYSeriesCollection[] data = new XYSeriesCollection[2]; - data[0] = new XYSeriesCollection(); - data[1] = new XYSeriesCollection(); - - - // Get the domain axis type - final FlightDataType domainType = filled.getDomainAxisType(); - final Unit domainUnit = filled.getDomainAxisUnit(); - if (domainType == null) { - throw new IllegalArgumentException("Domain axis type not specified."); - } - List x = branch.get(domainType); - - - // Create the XYSeries objects from the flight data and store into the collections - int length = filled.getTypeCount(); - String[] axisLabel = new String[2]; - for (int i = 0; i < length; i++) { - // Get info - FlightDataType type = filled.getType(i); - Unit unit = filled.getUnit(i); - int axis = filled.getAxis(i); - String name = getLabel(type, unit); - - // Store data in provided units - List y = branch.get(type); - XYSeries series = new XYSeries(name, false, true); - for (int j = 0; j < x.size(); j++) { - series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j))); - } - data[axis].addSeries(series); - - // Update axis label - if (axisLabel[axis] == null) - axisLabel[axis] = type.getName(); - else - axisLabel[axis] += "; " + type.getName(); - } - - - // Create the chart using the factory to get all default settings - JFreeChart chart = ChartFactory.createXYLineChart( - //// Simulated flight - trans.get("simedtdlg.chart.Simflight"), - null, - null, - null, - PlotOrientation.VERTICAL, - true, - true, - false - ); - - - // Add the data and formatting to the plot - XYPlot plot = chart.getXYPlot(); - int axisno = 0; - for (int i = 0; i < 2; i++) { - // Check whether axis has any data - if (data[i].getSeriesCount() > 0) { - // Create and set axis - double min = axes.get(i).getMinValue(); - double max = axes.get(i).getMaxValue(); - NumberAxis axis = new PresetNumberAxis(min, max); - axis.setLabel(axisLabel[i]); - // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue()); - plot.setRangeAxis(axisno, axis); - - // Add data and map to the axis - plot.setDataset(axisno, data[i]); - plot.setRenderer(axisno, new StandardXYItemRenderer()); - plot.mapDatasetToRangeAxis(axisno, axisno); - axisno++; - } - } - - plot.getDomainAxis().setLabel(getLabel(domainType, domainUnit)); - plot.addDomainMarker(new ValueMarker(0)); - plot.addRangeMarker(new ValueMarker(0)); - - - // Create the dialog - //// Simulation results - final JDialog dialog = new JDialog(this, trans.get("simedtdlg.dlg.Simres")); - dialog.setModalityType(ModalityType.DOCUMENT_MODAL); - - JPanel panel = new JPanel(new MigLayout("fill")); - dialog.add(panel); - - ChartPanel chartPanel = new ChartPanel(chart, - false, // properties - true, // save - false, // print - true, // zoom - true); // tooltips - chartPanel.setMouseWheelEnabled(true); - chartPanel.setEnforceFileExtensions(true); - chartPanel.setInitialDelay(500); - - chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); - - panel.add(chartPanel, "grow, wrap 20lp"); - - //// Close button - JButton button = new JButton(trans.get("dlg.but.close")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dialog.setVisible(false); - } - }); - panel.add(button, "right"); - - dialog.setLocationByPlatform(true); - dialog.pack(); - - GUIUtil.setDisposableDialogOptions(dialog, button); - - dialog.setVisible(true); - } - - private class PresetNumberAxis extends NumberAxis { - private final double min; - private final double max; - - public PresetNumberAxis(double min, double max) { - this.min = min; - this.max = max; - autoAdjustRange(); - } - - @Override - protected void autoAdjustRange() { - this.setRange(min, max); - } - } - - - private String getLabel(FlightDataType type, Unit unit) { - String name = type.getName(); - if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) && - !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0) - name += " (" + unit.getUnit() + ")"; - return name; - } - - - private class ListenerCellRenderer extends JLabel implements ListCellRenderer { @Override diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java b/core/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java new file mode 100644 index 000000000..08a7b8678 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java @@ -0,0 +1,436 @@ +package net.sf.openrocket.gui.simulation; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.Arrays; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.components.CsvOptionPanel; +import net.sf.openrocket.gui.components.UnitCellEditor; +import net.sf.openrocket.gui.util.FileHelper; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.SaveCSVWorker; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.Unit; +import net.sf.openrocket.unit.UnitGroup; + +public class SimulationExportPanel extends JPanel { + + private static final String SPACE = "SPACE"; + private static final String TAB = "TAB"; + private static final Translator trans = Application.getTranslator(); + + private static final int OPTION_SIMULATION_COMMENTS = 0; + private static final int OPTION_FIELD_DESCRIPTIONS = 1; + private static final int OPTION_FLIGHT_EVENTS = 2; + + private final JTable table; + private final SelectionTableModel tableModel; + private final JLabel selectedCountLabel; + + private final Simulation simulation; + private final FlightDataBranch branch; + + private final boolean[] selected; + private final FlightDataType[] types; + private final Unit[] units; + + private final CsvOptionPanel csvOptions; + + + public SimulationExportPanel(Simulation sim) { + super(new MigLayout("fill, flowy")); + + JPanel panel; + JButton button; + + + this.simulation = sim; + + // TODO: MEDIUM: Only exports primary branch + + final FlightData data = simulation.getSimulatedData(); + + // Check that data exists + if (data == null || data.getBranchCount() == 0 || + data.getBranch(0).getTypes().length == 0) { + throw new IllegalArgumentException("No data for panel"); + } + + + // Create the data model + branch = data.getBranch(0); + + types = branch.getTypes(); + Arrays.sort(types); + + selected = new boolean[types.length]; + units = new Unit[types.length]; + for (int i = 0; i < types.length; i++) { + selected[i] = ((SwingPreferences) Application.getPreferences()).isExportSelected(types[i]); + units[i] = types[i].getUnitGroup().getDefaultUnit(); + } + + + //// Create the panel + + + // Set up the variable selection table + tableModel = new SelectionTableModel(); + table = new JTable(tableModel); + table.setDefaultRenderer(Object.class, + new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Object.class))); + table.setDefaultRenderer(Boolean.class, + new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Boolean.class))); + table.setRowSelectionAllowed(false); + table.setColumnSelectionAllowed(false); + + table.setDefaultEditor(Unit.class, new UnitCellEditor() { + @Override + protected UnitGroup getUnitGroup(Unit value, int row, int column) { + return types[row].getUnitGroup(); + } + }); + + // Set column widths + TableColumnModel columnModel = table.getColumnModel(); + TableColumn col = columnModel.getColumn(0); + int w = table.getRowHeight(); + col.setMinWidth(w); + col.setPreferredWidth(w); + col.setMaxWidth(w); + + col = columnModel.getColumn(1); + col.setPreferredWidth(200); + + col = columnModel.getColumn(2); + col.setPreferredWidth(100); + + table.addMouseListener(new GUIUtil.BooleanTableClickListener(table)); + + // Add table + panel = new JPanel(new MigLayout("fill")); + panel.setBorder(BorderFactory.createTitledBorder(trans.get("SimExpPan.border.Vartoexport"))); + + panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap"); + + // Select all/none buttons + button = new JButton(trans.get("SimExpPan.but.Selectall")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tableModel.selectAll(); + } + }); + panel.add(button, "split 2, growx 1, sizegroup selectbutton"); + + button = new JButton(trans.get("SimExpPan.but.Selectnone")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tableModel.selectNone(); + } + }); + panel.add(button, "growx 1, sizegroup selectbutton, wrap"); + + + selectedCountLabel = new JLabel(); + updateSelectedCount(); + panel.add(selectedCountLabel); + + this.add(panel, "grow 100, wrap"); + + + // These need to be in the order of the OPTIONS_XXX indices + csvOptions = new CsvOptionPanel(SimulationExportPanel.class, + trans.get("SimExpPan.checkbox.Includesimudesc"), + trans.get("SimExpPan.checkbox.ttip.Includesimudesc"), + trans.get("SimExpPan.checkbox.Includefielddesc"), + trans.get("SimExpPan.checkbox.ttip.Includefielddesc"), + trans.get("SimExpPan.checkbox.Incflightevents"), + trans.get("SimExpPan.checkbox.ttip.Incflightevents")); + + this.add(csvOptions, "spany, split, growx 1"); + + + // Space-filling panel + panel = new JPanel(); + this.add(panel, "width 1, height 1, grow 1"); + + /* + // Export button + button = new JButton(trans.get("SimExpPan.but.Exporttofile")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doExport(); + } + }); + this.add(button, "gapbottom para, gapright para, right"); + */ + } + + + public boolean doExport() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(FileHelper.CSV_FILE_FILTER); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + + if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) + return false; + + File file = chooser.getSelectedFile(); + if (file == null) + return false; + + file = FileHelper.ensureExtension(file, "csv"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } + + + String commentChar = csvOptions.getCommentCharacter(); + String fieldSep = csvOptions.getFieldSeparator(); + boolean simulationComment = csvOptions.getSelectionOption(OPTION_SIMULATION_COMMENTS); + boolean fieldComment = csvOptions.getSelectionOption(OPTION_FIELD_DESCRIPTIONS); + boolean eventComment = csvOptions.getSelectionOption(OPTION_FLIGHT_EVENTS); + csvOptions.storePreferences(); + + // Store preferences and export + int n = 0; + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); + for (int i = 0; i < selected.length; i++) { + ((SwingPreferences) Application.getPreferences()).setExportSelected(types[i], selected[i]); + if (selected[i]) + n++; + } + + + FlightDataType[] fieldTypes = new FlightDataType[n]; + Unit[] fieldUnits = new Unit[n]; + int pos = 0; + for (int i = 0; i < selected.length; i++) { + if (selected[i]) { + fieldTypes[pos] = types[i]; + fieldUnits[pos] = units[i]; + pos++; + } + } + + if (fieldSep.equals(SPACE)) { + fieldSep = " "; + } else if (fieldSep.equals(TAB)) { + fieldSep = "\t"; + } + + + SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, + commentChar, simulationComment, fieldComment, eventComment, + SwingUtilities.getWindowAncestor(this)); + + return true; + } + + + private void updateSelectedCount() { + int total = selected.length; + int n = 0; + String str; + + for (int i = 0; i < selected.length; i++) { + if (selected[i]) + n++; + } + + if (n == 1) { + //// Exporting 1 variable out of + str = trans.get("SimExpPan.ExportingVar.desc1") + " " + total + "."; + } else { + //// Exporting + //// variables out of + str = trans.get("SimExpPan.ExportingVar.desc2") + " " + n + " " + + trans.get("SimExpPan.ExportingVar.desc3") + " " + total + "."; + } + + selectedCountLabel.setText(str); + } + + + + /** + * A table cell renderer that uses another renderer and sets the background and + * foreground of the returned component based on the selection of the variable. + */ + private class SelectionBackgroundCellRenderer implements TableCellRenderer { + + private final TableCellRenderer renderer; + + public SelectionBackgroundCellRenderer(TableCellRenderer renderer) { + this.renderer = renderer; + } + + @Override + public Component getTableCellRendererComponent(JTable myTable, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + + Component component = renderer.getTableCellRendererComponent(myTable, + value, isSelected, hasFocus, row, column); + + if (selected[row]) { + component.setBackground(myTable.getSelectionBackground()); + component.setForeground(myTable.getSelectionForeground()); + } else { + component.setBackground(myTable.getBackground()); + component.setForeground(myTable.getForeground()); + } + + return component; + } + + } + + + /** + * The table model for the variable selection. + */ + private class SelectionTableModel extends AbstractTableModel { + private static final int SELECTED = 0; + private static final int NAME = 1; + private static final int UNIT = 2; + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public int getRowCount() { + return types.length; + } + + @Override + public String getColumnName(int column) { + switch (column) { + case SELECTED: + return ""; + case NAME: + //// Variable + return trans.get("SimExpPan.Col.Variable"); + case UNIT: + //// Unit + return trans.get("SimExpPan.Col.Unit"); + default: + throw new IndexOutOfBoundsException("column=" + column); + } + + } + + @Override + public Class getColumnClass(int column) { + switch (column) { + case SELECTED: + return Boolean.class; + case NAME: + return FlightDataType.class; + case UNIT: + return Unit.class; + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + @Override + public Object getValueAt(int row, int column) { + + switch (column) { + case SELECTED: + return selected[row]; + + case NAME: + return types[row]; + + case UNIT: + return units[row]; + + default: + throw new IndexOutOfBoundsException("column=" + column); + } + + } + + @Override + public void setValueAt(Object value, int row, int column) { + + switch (column) { + case SELECTED: + selected[row] = (Boolean) value; + this.fireTableRowsUpdated(row, row); + updateSelectedCount(); + break; + + case NAME: + break; + + case UNIT: + units[row] = (Unit) value; + break; + + default: + throw new IndexOutOfBoundsException("column=" + column); + } + + } + + @Override + public boolean isCellEditable(int row, int column) { + switch (column) { + case SELECTED: + return true; + + case NAME: + return false; + + case UNIT: + return types[row].getUnitGroup().getUnitCount() > 1; + + default: + throw new IndexOutOfBoundsException("column=" + column); + } + } + + public void selectAll() { + Arrays.fill(selected, true); + updateSelectedCount(); + this.fireTableDataChanged(); + } + + public void selectNone() { + Arrays.fill(selected, false); + updateSelectedCount(); + this.fireTableDataChanged(); + } + + } + +} diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationPlotExportDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotExportDialog.java new file mode 100644 index 000000000..e145dc2b7 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotExportDialog.java @@ -0,0 +1,137 @@ +package net.sf.openrocket.gui.simulation; + +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.SimulationOptions; +import net.sf.openrocket.startup.Application; + +public class SimulationPlotExportDialog extends JDialog { + + private final Window parentWindow; + private final Simulation simulation; + private final OpenRocketDocument document; + private final SimulationOptions conditions; + private final Configuration configuration; + private static final Translator trans = Application.getTranslator(); + + public SimulationPlotExportDialog(Window parent, OpenRocketDocument document, Simulation s) { + //// Plot/Export simulation + super(parent, trans.get("simedtdlg.title.Editsim"), JDialog.ModalityType.DOCUMENT_MODAL); + this.document = document; + this.parentWindow = parent; + this.simulation = s; + this.conditions = simulation.getOptions(); + configuration = simulation.getConfiguration(); + + JPanel mainPanel = new JPanel(new MigLayout("fill", "[grow]")); + + //// Simulation name: + mainPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "span, split 2, shrink"); + final JTextField field = new JTextField(simulation.getName()); + field.setEditable(false); + mainPanel.add(field, "shrinky, growx, wrap"); + + final JTabbedPane tabbedPane = new JTabbedPane(); + + //// Plot data + final SimulationPlotPanel plotTab = plotTab(); + tabbedPane.addTab(trans.get("simedtdlg.tab.Plotdata"), plotTab); + //// Export data + final SimulationExportPanel exportTab = exportTab(); + tabbedPane.addTab(trans.get("simedtdlg.tab.Exportdata"), exportTab); + + mainPanel.add(tabbedPane, "grow, wrap"); + + JButton ok = new JButton(trans.get("dlg.but.ok")); + ok.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + + if (tabbedPane.getSelectedIndex() == 0) { + JDialog plot = plotTab.doPlot(); + if (plot != null) { + SimulationPlotExportDialog.this.dispose(); + plot.setVisible(true); + } + } else { + if (exportTab.doExport()) { + SimulationPlotExportDialog.this.dispose(); + } + } + } + }); + mainPanel.add(ok, "tag ok, split 2"); + + //// Close button + JButton close = new JButton(trans.get("dlg.but.close")); + close.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + SimulationPlotExportDialog.this.dispose(); + } + }); + mainPanel.add(close, "tag cancel"); + + + this.add(mainPanel); + this.validate(); + this.pack(); + this.setLocationByPlatform(true); + + GUIUtil.setDisposableDialogOptions(this, close); + + } + + + /** + * A panel for plotting the previously calculated data. + */ + private SimulationPlotPanel plotTab() { + + // Check that data exists + // FIXME - + /* + if (simulation.getSimulatedData() == null || + simulation.getSimulatedData().getBranchCount() == 0) { + return noDataPanel(); + } + */ + return new SimulationPlotPanel(simulation); + } + + /** + * A panel for exporting the data. + */ + private SimulationExportPanel exportTab() { + FlightData data = simulation.getSimulatedData(); + + // Check that data exists + // FIXME - + /* + if (data == null || data.getBranchCount() == 0 || + data.getBranch(0).getTypes().length == 0) { + return noDataPanel(); + } + */ + return new SimulationExportPanel(simulation); + } + + + +} diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java similarity index 95% rename from core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java index 5a8787b28..dfac15d55 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java @@ -1,5 +1,6 @@ -package net.sf.openrocket.gui.plot; +package net.sf.openrocket.gui.simulation; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -10,6 +11,7 @@ import java.util.EnumSet; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComboBox; +import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -24,6 +26,8 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.plot.PlotConfiguration; +import net.sf.openrocket.gui.plot.SimulationPlotDialog; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; @@ -213,7 +217,7 @@ public class SimulationPlotPanel extends JPanel { col0.setPreferredWidth(w); col0.setMaxWidth(w); table.addMouseListener(new GUIUtil.BooleanTableClickListener(table)); - this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel"); + this.add(new JScrollPane(table), "height 200px, width 200lp, grow 1, wrap rel"); //// All + None buttons @@ -294,6 +298,7 @@ public class SimulationPlotPanel extends JPanel { this.add(new JPanel(), "growx"); + /* //// Plot flight button = new JButton(trans.get("simplotpanel.but.Plotflight")); button.addActionListener(new ActionListener() { @@ -312,10 +317,22 @@ public class SimulationPlotPanel extends JPanel { } }); this.add(button, "right"); - + */ updatePlots(); } + public JDialog doPlot() { + if (configuration.getTypeCount() == 0) { + JOptionPane.showMessageDialog(SimulationPlotPanel.this, + trans.get("error.noPlotSelected"), + trans.get("error.noPlotSelected.title"), + JOptionPane.ERROR_MESSAGE); + return null; + } + defaultConfiguration = configuration.clone(); + return SimulationPlotDialog.getPlot((Window) SwingUtilities.getRoot(SimulationPlotPanel.this), + simulation, configuration); + } private void setConfiguration(PlotConfiguration conf) { diff --git a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java similarity index 99% rename from core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java index 25bb169c1..ca8b49b0f 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui.main; +package net.sf.openrocket.gui.simulation; import java.awt.Dialog; diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java new file mode 100644 index 000000000..45a9bfa80 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java @@ -0,0 +1,32 @@ +package net.sf.openrocket.gui.simulation; + +import java.awt.Component; +import java.util.ArrayList; + +import javax.swing.JOptionPane; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.dialogs.DetailDialog; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class SimulationWarningDialog { + + private static final Translator trans = Application.getTranslator(); + + public static void showWarningDialog(Component parent, Simulation simulation) { + + if (simulation.getSimulatedWarnings().size() > 0) { + ArrayList messages = new ArrayList(); + messages.add(trans.get("SimuRunDlg.msg.errorOccurred")); + for (Warning m : simulation.getSimulatedWarnings()) { + messages.add(m.toString()); + } + DetailDialog.showDetailedMessageDialog(parent, + messages.toArray(), + null, simulation.getName(), JOptionPane.ERROR_MESSAGE); + } + + } +} diff --git a/core/src/net/sf/openrocket/gui/main/SimulationWorker.java b/core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java similarity index 97% rename from core/src/net/sf/openrocket/gui/main/SimulationWorker.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java index f348fbcdb..659b6b429 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationWorker.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui.main; +package net.sf.openrocket.gui.simulation; import java.util.Arrays; @@ -53,6 +53,7 @@ public abstract class SimulationWorker extends SwingWorker { /** * The rocket begins to tumble. */ - TUMBLE(trans.get("FlightEvent.Type.TUMBLE")); + TUMBLE(trans.get("FlightEvent.Type.TUMBLE")), + + /** + * Simulation aborted + */ + EXCEPTION(trans.get("FlightEvent.Type.EXCEPTION")); private final String name; diff --git a/core/src/net/sf/openrocket/simulation/SimulationOptions.java b/core/src/net/sf/openrocket/simulation/SimulationOptions.java index d47389b0d..dbbfbd19c 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/core/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -452,6 +452,25 @@ public class SimulationOptions implements ChangeSource, Cloneable { fireChangeEvent(); } + public void copyConditionsFrom(SimulationOptions src) { + + this.launchAltitude = src.launchAltitude; + this.launchLatitude = src.launchLatitude; + this.launchLongitude = src.launchLongitude; + this.launchPressure = src.launchPressure; + this.launchRodAngle = src.launchRodAngle; + this.launchRodDirection = src.launchRodDirection; + this.launchRodLength = src.launchRodLength; + this.launchTemperature = src.launchTemperature; + this.maximumAngle = src.maximumAngle; + this.timeStep = src.timeStep; + this.windAverage = src.windAverage; + this.windTurbulence = src.windTurbulence; + this.calculateExtras = src.calculateExtras; + this.randomSeed = src.randomSeed; + + fireChangeEvent(); + } /** diff --git a/core/src/net/sf/openrocket/simulation/SimulationStepper.java b/core/src/net/sf/openrocket/simulation/SimulationStepper.java index 90855c3cb..f218f387e 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/SimulationStepper.java @@ -11,7 +11,7 @@ public interface SimulationStepper { * @param status the current simulation status. * @return a SimulationStatus suitable for simulating with this simulation stepper. */ - public SimulationStatus initialize(SimulationStatus status) throws SimulationException; + public SimulationStatus initialize(SimulationStatus status); /** * Perform one simulation time step.