Rework simulation ui. Added support for default flight conditions.

Editing of flight conditions on multiple simulations.  Changed
exceptions generated in simulation to still produce data.
This commit is contained in:
kruland2607 2013-05-23 11:01:21 -05:00
parent b1b2bc5abe
commit f0ae25615e
21 changed files with 1011 additions and 443 deletions

View File

@ -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

View File

@ -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");
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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<String> stages = new ArrayList<String>();
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);
}
}

View File

@ -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;
/**

View File

@ -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;

View File

@ -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"));
//// <html>The launch site latitude affects the gravitational pull of Earth.<br>
@ -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"));
//// <html>The launch altitude above mean sea level.<br>
@ -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"));
//// <html>Direction of the launch rod relative to the wind.<br>
@ -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);
//// <html><i>Simulation listeners</i> 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<Axis> 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<Double> 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<Double> 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

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui.main;
package net.sf.openrocket.gui.simulation;
import java.awt.Dialog;

View File

@ -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<String> messages = new ArrayList<String>();
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);
}
}
}

View File

@ -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<FlightData, Simulatio
try {
simulation.simulate(listeners);
// FIXME - test for simulation warnings.
} catch (Throwable e) {
throwable = e;
return null;

View File

@ -42,9 +42,9 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private SimulationStepper landingStepper = new BasicLandingStepper();
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
// Constant holding 30 degress in radians. This is the AOA condition
// Constant holding 10 degress in radians. This is the AOA condition
// necessary to transistion to tumbling.
private final static double AOA_TUMBLE_CONDITION = Math.PI / 3.0;
private final static double AOA_TUMBLE_CONDITION = Math.PI / 9.0;
private SimulationStepper currentStepper;
@ -105,7 +105,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
return flightData;
}
private FlightDataBranch simulateLoop() throws SimulationException {
private FlightDataBranch simulateLoop() {
// Initialize the simulation
currentStepper = flightStepper;
@ -222,7 +222,9 @@ public class BasicEventSimulationEngine implements SimulationEngine {
} catch (SimulationException e) {
SimulationListenerHelper.fireEndSimulation(status, e);
throw e;
// Add FlightEvent for Abort.
status.getFlightData().addEvent(new FlightEvent(FlightEvent.Type.EXCEPTION, status.getSimulationTime(), status.getConfiguration().getRocket(), e.getLocalizedMessage()));
status.getWarnings().add(Warning.SIMULATION_EXCEPTION);
}
return status.getFlightData();

View File

@ -13,7 +13,7 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
private static final double RECOVERY_TIME_STEP = 0.5;
@Override
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
public SimulationStatus initialize(SimulationStatus status) {
return status;
}

View File

@ -12,7 +12,7 @@ public class BasicTumbleStepper extends AbstractSimulationStepper {
private static final double RECOVERY_TIME_STEP = 0.5;
@Override
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
public SimulationStatus initialize(SimulationStatus status) {
return new BasicTumbleStatus(status);
}

View File

@ -0,0 +1,73 @@
package net.sf.openrocket.simulation;
import net.sf.openrocket.startup.Preferences;
import com.google.inject.Inject;
public class DefaultSimulationOptionFactory {
@Inject
private Preferences prefs;
public static final String SIMCONDITION_WIND_SPEED = "SimConditionWindSpeed";
public static final String SIMCONDITION_WIND_STDDEV = "SimConditionWindStdDev";
public static final String SIMCONDITION_WIND_TURB = "SimConditionWindTurb";
public static final String SIMCONDITION_SITE_LAT = "SimConditionSiteLat";
public static final String SIMCONDITION_SITE_LON = "SimConditionSiteLon";
public static final String SIMCONDITION_SITE_ALT = "SimConditionSiteAlt";
public static final String SIMCONDITION_ATMOS_STD = "SimConditionsAtmosStd";
public static final String SIMCONDITION_ATMOS_TEMP = "SimConditionsAtmosTemp";
public static final String SIMCONDITION_ATMOS_PRESSURE = "SimConditionsAtmosPres";
public static final String SIMCONDITION_ROD_LENGTH = "SimConditionsRodLength";
public static final String SIMCONDITION_ROD_ANGLE = "SimConditionsRodAngle";
public static final String SIMCONDITION_ROD_DIRECTION = "SimConditionsRodDirection";
public DefaultSimulationOptionFactory(Preferences prefs) {
this.prefs = prefs;
}
public DefaultSimulationOptionFactory() {
prefs = null;
}
public SimulationOptions getDefault() {
SimulationOptions defaults = new SimulationOptions(null);
if (prefs != null) {
defaults.setWindSpeedAverage(prefs.getDouble(SIMCONDITION_WIND_SPEED, defaults.getWindSpeedAverage()));
defaults.setWindSpeedDeviation(prefs.getDouble(SIMCONDITION_WIND_STDDEV, defaults.getWindSpeedDeviation()));
defaults.setWindTurbulenceIntensity(prefs.getDouble(SIMCONDITION_WIND_TURB, defaults.getWindTurbulenceIntensity()));
defaults.setLaunchLatitude(prefs.getDouble(SIMCONDITION_SITE_LAT, defaults.getLaunchLatitude()));
defaults.setLaunchLongitude(prefs.getDouble(SIMCONDITION_SITE_LON, defaults.getLaunchLongitude()));
defaults.setLaunchAltitude(prefs.getDouble(SIMCONDITION_SITE_ALT, defaults.getLaunchAltitude()));
defaults.setISAAtmosphere(prefs.getBoolean(SIMCONDITION_ATMOS_STD, defaults.isISAAtmosphere()));
defaults.setLaunchTemperature(prefs.getDouble(SIMCONDITION_ATMOS_TEMP, defaults.getLaunchTemperature()));
defaults.setLaunchRodLength(prefs.getDouble(SIMCONDITION_ROD_LENGTH, defaults.getLaunchRodLength()));
defaults.setLaunchRodAngle(prefs.getDouble(SIMCONDITION_ROD_ANGLE, defaults.getLaunchRodAngle()));
defaults.setLaunchRodDirection(prefs.getDouble(SIMCONDITION_ROD_DIRECTION, defaults.getLaunchRodDirection()));
}
return defaults;
}
public void saveDefault(SimulationOptions newDefaults) {
prefs.putDouble(SIMCONDITION_WIND_SPEED, newDefaults.getWindSpeedAverage());
prefs.putDouble(SIMCONDITION_WIND_STDDEV, newDefaults.getWindSpeedDeviation());
prefs.putDouble(SIMCONDITION_WIND_TURB, newDefaults.getWindTurbulenceIntensity());
prefs.putDouble(SIMCONDITION_SITE_LAT, newDefaults.getLaunchLatitude());
prefs.putDouble(SIMCONDITION_SITE_LON, newDefaults.getLaunchLongitude());
prefs.putDouble(SIMCONDITION_SITE_ALT, newDefaults.getLaunchAltitude());
prefs.putBoolean(SIMCONDITION_ATMOS_STD, newDefaults.isISAAtmosphere());
prefs.putDouble(SIMCONDITION_ATMOS_TEMP, newDefaults.getLaunchTemperature());
prefs.putDouble(SIMCONDITION_ROD_LENGTH, newDefaults.getLaunchRodLength());
prefs.putDouble(SIMCONDITION_ROD_ANGLE, newDefaults.getLaunchRodAngle());
prefs.putDouble(SIMCONDITION_ROD_DIRECTION, newDefaults.getLaunchRodDirection());
}
}

View File

@ -77,7 +77,12 @@ public class FlightEvent implements Comparable<FlightEvent> {
/**
* 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;

View File

@ -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();
}
/**

View File

@ -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.