Merge remote-tracking branch 'kruland/sims'

This commit is contained in:
Sampo Niskanen 2013-06-13 15:42:45 +03:00
commit 92ae04f85a
27 changed files with 1960 additions and 1278 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

@ -8,7 +8,7 @@ import net.sf.openrocket.unit.UnitGroup;
public abstract class Warning {
private static final Translator trans = Application.getTranslator();
/**
* Return a Warning with the specific text.
*/
@ -36,8 +36,8 @@ public abstract class Warning {
*/
@Override
public boolean equals(Object o) {
return o != null && (o.getClass() == this.getClass());
}
return o != null && (o.getClass() == this.getClass());
}
/**
* A <code>hashCode</code> method compatible with the <code>equals</code> method.
@ -79,21 +79,21 @@ public abstract class Warning {
return (trans.get("Warning.LargeAOA.str2") +
UnitGroup.UNITS_ANGLE.getDefaultUnit().toString(aoa) + ").");
}
@Override
public boolean replaceBy(Warning other) {
if (!(other instanceof LargeAOA))
return false;
LargeAOA o = (LargeAOA)other;
if (Double.isNaN(this.aoa)) // If this has value NaN then replace
LargeAOA o = (LargeAOA) other;
if (Double.isNaN(this.aoa)) // If this has value NaN then replace
return true;
return (o.aoa > this.aoa);
}
}
public static class MissingMotor extends Warning {
private Motor.Type type = null;
private String manufacturer = null;
private String designation = null;
@ -101,7 +101,7 @@ public abstract class Warning {
private double diameter = Double.NaN;
private double length = Double.NaN;
private double delay = Double.NaN;
@Override
public String toString() {
String str = "No motor with designation '" + designation + "'";
@ -110,82 +110,82 @@ public abstract class Warning {
str += " found.";
return str;
}
public Motor.Type getType() {
return type;
}
public void setType(Motor.Type type) {
this.type = type;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public String getDigest() {
return digest;
}
public void setDigest(String digest) {
this.digest = digest;
}
public double getDiameter() {
return diameter;
}
public void setDiameter(double diameter) {
this.diameter = diameter;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getDelay() {
return delay;
}
public void setDelay(double delay) {
this.delay = delay;
}
@Override
public boolean replaceBy(Warning other) {
return false;
}
@Override
public int hashCode() {
final int prime = 31;
@ -206,7 +206,7 @@ public abstract class Warning {
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
@ -272,7 +272,7 @@ public abstract class Warning {
if (!(other instanceof Other))
return false;
Other o = (Other)other;
Other o = (Other) other;
return (o.description.equals(this.description));
}
@ -280,7 +280,7 @@ public abstract class Warning {
public int hashCode() {
return description.hashCode();
}
@Override
public boolean replaceBy(Warning other) {
return false;
@ -289,40 +289,40 @@ public abstract class Warning {
/** A <code>Warning</code> that the body diameter is discontinuous. */
////Discontinuity in rocket body diameter.
public static final Warning DISCONTINUITY =
new Other(trans.get("Warning.DISCONTINUITY"));
////Discontinuity in rocket body diameter.
public static final Warning DISCONTINUITY =
new Other(trans.get("Warning.DISCONTINUITY"));
/** A <code>Warning</code> that the fins are thick compared to the rocket body. */
////Thick fins may not be modeled accurately.
////Thick fins may not be modeled accurately.
public static final Warning THICK_FIN =
new Other(trans.get("Warning.THICK_FIN"));
new Other(trans.get("Warning.THICK_FIN"));
/** A <code>Warning</code> that the fins have jagged edges. */
////Jagged-edged fin predictions may be inaccurate.
////Jagged-edged fin predictions may be inaccurate.
public static final Warning JAGGED_EDGED_FIN =
new Other(trans.get("Warning.JAGGED_EDGED_FIN"));
new Other(trans.get("Warning.JAGGED_EDGED_FIN"));
/** A <code>Warning</code> that simulation listeners have affected the simulation */
////Listeners modified the flight simulation
////Listeners modified the flight simulation
public static final Warning LISTENERS_AFFECTED =
new Other(trans.get("Warning.LISTENERS_AFFECTED"));
new Other(trans.get("Warning.LISTENERS_AFFECTED"));
////Recovery device opened while motor still burning.
////Recovery device opened while motor still burning.
public static final Warning RECOVERY_DEPLOYMENT_WHILE_BURNING =
new Other(trans.get("Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING"));
new Other(trans.get("Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING"));
//// Invalid parameter encountered, ignoring.
public static final Warning FILE_INVALID_PARAMETER =
new Other(trans.get("Warning.FILE_INVALID_PARAMETER"));
new Other(trans.get("Warning.FILE_INVALID_PARAMETER"));
public static final Warning PARALLEL_FINS =
new Other(trans.get("Warning.PARALLEL_FINS"));
new Other(trans.get("Warning.PARALLEL_FINS"));
public static final Warning SUPERSONIC =
new Other(trans.get("Warning.SUPERSONIC"));
new Other(trans.get("Warning.SUPERSONIC"));
public static final Warning RECOVERY_LAUNCH_ROD =
new Other(trans.get("Warning.RECOVERY_LAUNCH_ROD"));
new Other(trans.get("Warning.RECOVERY_LAUNCH_ROD"));
}

View File

@ -13,6 +13,7 @@ import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.simulation.BasicEventSimulationEngine;
import net.sf.openrocket.simulation.DefaultSimulationOptionFactory;
import net.sf.openrocket.simulation.FlightData;
import net.sf.openrocket.simulation.RK4SimulationStepper;
import net.sf.openrocket.simulation.SimulationConditions;
@ -105,8 +106,11 @@ public class Simulation implements ChangeSource, Cloneable {
this.status = Status.NOT_SIMULATED;
options = new SimulationOptions(rocket);
options.setMotorConfigurationID(
rocket.getDefaultConfiguration().getFlightConfigurationID());
DefaultSimulationOptionFactory f = Application.getInjector().getInstance(DefaultSimulationOptionFactory.class);
options.copyConditionsFrom(f.getDefault());
options.setMotorConfigurationID(rocket.getDefaultConfiguration().getFlightConfigurationID());
options.addChangeListener(new ConditionListener());
}
@ -374,7 +378,21 @@ public class Simulation implements ChangeSource, Cloneable {
return simulatedData;
}
/**
* Return true if this simulation contains plotable flight data.
*
* @return
*/
public boolean hasSimulationData() {
FlightData data = getSimulatedData();
if (data == null) {
return false;
}
if (data.getBranchCount() == 0) {
return false;
}
return true;
}
/**
* Returns a copy of this simulation suitable for cut/copy/paste operations.

View File

@ -15,6 +15,7 @@ import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.adaptors.FlightConfigurationModel;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.util.GUIUtil;
@ -170,6 +171,10 @@ public class FlightConfigurationDialog extends JDialog {
private void addConfiguration() {
String newId = rocket.newFlightConfigurationID();
rocket.getDefaultConfiguration().setFlightConfigurationID(newId);
// Create a new simulation for this configuration.
createSimulationForNewConfiguration();
configurationChanged();
}
@ -188,9 +193,22 @@ public class FlightConfigurationDialog extends JDialog {
rocket.setFlightConfigurationName(currentId, oldName);
rocket.getDefaultConfiguration().setFlightConfigurationID(newConfigId);
// Create a new simulation for this configuration.
createSimulationForNewConfiguration();
configurationChanged();
}
/**
* prereq - assumes that the new configuration has been set as the default configuration.
*/
private void createSimulationForNewConfiguration() {
Simulation newSim = new Simulation(rocket);
OpenRocketDocument doc = BasicFrame.findDocument(rocket);
newSim.setName(doc.getNextSimulationName());
doc.addSimulation(newSim);
}
private void renameConfiguration() {
new RenameConfigDialog(this, rocket).setVisible(true);
}

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,9 @@ 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.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;
@ -68,13 +71,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,59 +86,60 @@ 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(false, 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() {
@Override
public void actionPerformed(ActionEvent e) {
int selected = simulationTable.getSelectedRow();
if (selected < 0)
return; // TODO: MEDIUM: "None selected" dialog
selected = simulationTable.convertRowIndexToModel(selected);
simulationTable.clearSelection();
simulationTable.addRowSelectionInterval(selected, selected);
openDialog(document.getSimulations().get(selected), SimulationEditDialog.EDIT);
}
});
this.add(button, "gapright para");
//// Run simulations
button = new JButton(trans.get("simpanel.but.runsimulations"));
//// Re-run the selected simulations
button.setToolTipText(trans.get("simpanel.but.ttip.runsimu"));
button.addActionListener(new ActionListener() {
editButton.setToolTipText(trans.get("simpanel.but.ttip.editsim"));
editButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] selection = simulationTable.getSelectedRows();
if (selection.length == 0) {
JOptionPane.showMessageDialog(simulationTable, "No simulations selected.");
return;
if (selection.length == 0)
return; // TODO: LOW: "None selected" dialog
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(false, sims);
}
});
this.add(editButton, "gapright para");
//// Run simulations
runButton = new JButton(trans.get("simpanel.but.runsimulations"));
//// Re-run the selected simulations
runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu"));
runButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] selection = simulationTable.getSelectedRows();
if (selection.length == 0)
return; // TODO: LOW: "None selected" dialog
Simulation[] sims = new Simulation[selection.length];
for (int i = 0; i < selection.length; i++) {
selection[i] = simulationTable.convertRowIndexToModel(selection[i]);
@ -148,13 +153,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();
@ -203,12 +208,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();
@ -219,10 +224,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.hasSimulationData()) {
new SimulationRunDialog(SwingUtilities.getWindowAncestor(
SimulationPanel.this), document, sim).setVisible(true);
}
fireMaintainSelection();
openDialog(true, sim);
}
});
this.add(button, "wrap para");
this.add(plotButton, "wrap para");
@ -476,13 +492,19 @@ 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);
openDialog(document.getSimulations().get(selected));
}
} else {
updateButtonStates();
}
}
});
@ -511,6 +533,26 @@ 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);
}
}
@ -518,12 +560,23 @@ public class SimulationPanel extends JPanel {
return simulationTable.getSelectionModel();
}
private void openDialog(final Simulation sim, int position) {
new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sim, position)
.setVisible(true);
private void openDialog(boolean plotMode, final Simulation... sims) {
SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sims);
if (plotMode) {
d.setPlotMode();
}
d.setVisible(true);
fireMaintainSelection();
}
private void openDialog(final Simulation sim) {
boolean plotMode = false;
if (sim.hasSimulationData()) {
plotMode = true;
}
openDialog(plotMode, sim);
}
private void fireMaintainSelection() {
int[] selection = simulationTable.getSelectedRows();
simulationTableModel.fireTableDataChanged();

View File

@ -37,6 +37,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Total motion vs. time
@ -51,6 +52,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Flight side profile
@ -63,6 +65,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Stability vs. time
@ -77,6 +80,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Drag coefficients vs. Mach number
@ -86,6 +90,7 @@ public class PlotConfiguration implements Cloneable {
config.addPlotDataType(FlightDataType.TYPE_FRICTION_DRAG_COEFF, 0);
config.addPlotDataType(FlightDataType.TYPE_BASE_DRAG_COEFF, 0);
config.addPlotDataType(FlightDataType.TYPE_PRESSURE_DRAG_COEFF, 0);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Roll characteristics
@ -102,6 +107,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Angle of attack and orientation vs. time
@ -116,6 +122,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
//// Simulation time step and computation time
@ -129,6 +136,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
config.setEvent(FlightEvent.Type.EXCEPTION, true);
configs.add(config);
DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]);

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;
@ -152,7 +153,6 @@ public class SimulationPlot {
Unit unit = filled.getUnit(i);
int axis = filled.getAxis(i);
String name = getLabel(type, unit);
this.legendItems.lineLabels.add(name);
List<String> seriesNames = Util.generateSeriesLabels(simulation);
@ -166,7 +166,7 @@ public class SimulationPlot {
List<Double> plotx = thisBranch.get(domainType);
List<Double> ploty = thisBranch.get(type);
XYSeries series = new XYSeries(seriesNames.get(branchIndex) + ": " + name, false, true);
series.setDescription(thisBranch.getBranchName() + ": " + name);
series.setDescription(name);
int pointCount = plotx.size();
for (int j = 0; j < pointCount; j++) {
series.add(domainUnit.toUnit(plotx.get(j)), unit.toUnit(ploty.get(j)));
@ -249,6 +249,8 @@ public class SimulationPlot {
}
// Now we pull the colors for the legend.
for (int j = 0; j < data[i].getSeriesCount(); j += branchCount) {
String name = data[i].getSeries(j).getDescription();
this.legendItems.lineLabels.add(name);
Paint linePaint = r.lookupSeriesPaint(j);
this.legendItems.linePaints.add(linePaint);
Shape itemShape = r.lookupSeriesShape(j);

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

@ -0,0 +1,441 @@
package net.sf.openrocket.gui.simulation;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.models.atmosphere.ExtendedISAModel;
import net.sf.openrocket.simulation.DefaultSimulationOptionFactory;
import net.sf.openrocket.simulation.SimulationOptions;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.Chars;
public class SimulationConditionsPanel extends JPanel {
private static final Translator trans = Application.getTranslator();
SimulationConditionsPanel(final Simulation simulation) {
super(new MigLayout("fill"));
final SimulationOptions conditions = simulation.getOptions();
JPanel sub;
String tip;
UnitSelector unit;
BasicSlider slider;
DoubleModel m;
JSpinner spin;
//// Wind settings: Average wind speed, turbulence intensity, std. deviation
sub = new JPanel(new MigLayout("fill, gap rel unrel",
"[grow][65lp!][30lp!][75lp!]", ""));
//// Wind
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.lbl.Wind")));
this.add(sub, "growx, split 2, aligny 0, flowy, gapright para");
// Wind average
//// Average windspeed:
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);
sub.add(label);
m = new DoubleModel(conditions, "WindSpeedAverage", UnitGroup.UNITS_WINDSPEED, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(0, 10.0));
slider.setToolTipText(tip);
sub.add(slider, "w 75lp, wrap");
// Wind std. deviation
//// Standard deviation:
label = new JLabel(trans.get("simedtdlg.lbl.Stddeviation"));
//// <html>The standard deviation of the windspeed.<br>
//// The windspeed is within twice the standard deviation from the average for 95% of the time.
tip = trans.get("simedtdlg.lbl.ttip.Stddeviation");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "WindSpeedDeviation", UnitGroup.UNITS_WINDSPEED, 0);
DoubleModel m2 = new DoubleModel(conditions, "WindSpeedAverage", 0.25,
UnitGroup.UNITS_COEFFICIENT, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(new DoubleModel(0), m2));
slider.setToolTipText(tip);
sub.add(slider, "w 75lp, wrap");
// Wind turbulence intensity
//// Turbulence intensity:
label = new JLabel(trans.get("simedtdlg.lbl.Turbulenceintensity"));
//// <html>The turbulence intensity is the standard deviation divided by the average windspeed.<br>
//// Typical values range from
//// to
tip = trans.get("simedtdlg.lbl.ttip.Turbulenceintensity1") +
trans.get("simedtdlg.lbl.ttip.Turbulenceintensity2") + " " +
UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.05) +
" " + trans.get("simedtdlg.lbl.ttip.Turbulenceintensity3") + " " +
UnitGroup.UNITS_RELATIVE.getDefaultUnit().toStringUnit(0.20) + ".";
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "WindTurbulenceIntensity", UnitGroup.UNITS_RELATIVE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
final JLabel intensityLabel = new JLabel(
getIntensityDescription(conditions.getWindTurbulenceIntensity()));
intensityLabel.setToolTipText(tip);
sub.add(intensityLabel, "w 75lp, wrap");
m.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
intensityLabel.setText(
getIntensityDescription(conditions.getWindTurbulenceIntensity()));
}
});
//// Temperature and pressure
sub = new JPanel(new MigLayout("fill, gap rel unrel",
"[grow][65lp!][30lp!][75lp!]", ""));
//// Atmospheric conditions
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Atmoscond")));
this.add(sub, "growx, aligny 0, gapright para");
BooleanModel isa = new BooleanModel(conditions, "ISAAtmosphere");
JCheckBox check = new JCheckBox(isa);
//// Use International Standard Atmosphere
check.setText(trans.get("simedtdlg.checkbox.InterStdAtmosphere"));
//// <html>Select to use the International Standard Atmosphere model.
//// <br>This model has a temperature of
//// and a pressure of
//// at sea level.
check.setToolTipText(trans.get("simedtdlg.checkbox.ttip.InterStdAtmosphere1") + " " +
UnitGroup.UNITS_TEMPERATURE.toStringUnit(ExtendedISAModel.STANDARD_TEMPERATURE) +
" " + trans.get("simedtdlg.checkbox.ttip.InterStdAtmosphere2") + " " +
UnitGroup.UNITS_PRESSURE.toStringUnit(ExtendedISAModel.STANDARD_PRESSURE) +
" " + trans.get("simedtdlg.checkbox.ttip.InterStdAtmosphere3"));
sub.add(check, "spanx, wrap unrel");
// Temperature:
label = new JLabel(trans.get("simedtdlg.lbl.Temperature"));
//// The temperature at the launch site.
tip = trans.get("simedtdlg.lbl.ttip.Temperature");
label.setToolTipText(tip);
isa.addEnableComponent(label, false);
sub.add(label);
m = new DoubleModel(conditions, "LaunchTemperature", UnitGroup.UNITS_TEMPERATURE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
isa.addEnableComponent(spin, false);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
isa.addEnableComponent(unit, false);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(253.15, 308.15)); // -20 ... 35
slider.setToolTipText(tip);
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.
tip = trans.get("simedtdlg.lbl.ttip.Pressure");
label.setToolTipText(tip);
isa.addEnableComponent(label, false);
sub.add(label);
m = new DoubleModel(conditions, "LaunchPressure", UnitGroup.UNITS_PRESSURE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
isa.addEnableComponent(spin, false);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
isa.addEnableComponent(unit, false);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(0.950e5, 1.050e5));
slider.setToolTipText(tip);
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!]", ""));
//// Launch site
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.lbl.Launchsite")));
this.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>
//// Positive values are on the Northern hemisphere, negative values on the Southern hemisphere.
tip = trans.get("simedtdlg.lbl.ttip.Latitude");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchLatitude", UnitGroup.UNITS_NONE, -90, 90);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
label = new JLabel(Chars.DEGREE + " N");
label.setToolTipText(tip);
sub.add(label, "growx");
slider = new BasicSlider(m.getSliderModel(-90, 90));
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");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchLongitude", UnitGroup.UNITS_NONE, -180, 180);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
label = new JLabel(Chars.DEGREE + " E");
label.setToolTipText(tip);
sub.add(label, "growx");
slider = new BasicSlider(m.getSliderModel(-180, 180));
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>
//// This affects the position of the rocket in the atmospheric model.
tip = trans.get("simedtdlg.lbl.ttip.Altitude");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchAltitude", UnitGroup.UNITS_DISTANCE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(0, 250, 1000));
slider.setToolTipText(tip);
sub.add(slider, "w 75lp, wrap");
//// Launch rod
sub = new JPanel(new MigLayout("fill, gap rel unrel",
"[grow][65lp!][30lp!][75lp!]", ""));
//// Launch rod
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Launchrod")));
this.add(sub, "growx, aligny 0, wrap");
// Length:
label = new JLabel(trans.get("simedtdlg.lbl.Length"));
//// The length of the launch rod.
tip = trans.get("simedtdlg.lbl.ttip.Length");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchRodLength", UnitGroup.UNITS_LENGTH, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(0, 1, 5));
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.
tip = trans.get("simedtdlg.lbl.ttip.Angle");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchRodAngle", UnitGroup.UNITS_ANGLE,
0, SimulationOptions.MAX_LAUNCH_ROD_ANGLE);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(0, Math.PI / 9,
SimulationOptions.MAX_LAUNCH_ROD_ANGLE));
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>
//// = towards the wind,
//// = downwind.
tip = trans.get("simedtdlg.lbl.ttip.Direction1") +
UnitGroup.UNITS_ANGLE.toStringUnit(0) +
" " + trans.get("simedtdlg.lbl.ttip.Direction2") + " " +
UnitGroup.UNITS_ANGLE.toStringUnit(Math.PI) +
" " + trans.get("simedtdlg.lbl.ttip.Direction3");
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "LaunchRodDirection", UnitGroup.UNITS_ANGLE,
-Math.PI, Math.PI);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "growx");
slider = new BasicSlider(m.getSliderModel(-Math.PI, Math.PI));
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);
}
});
this.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);
}
});
this.add(saveDefaults, "gapbottom para, gapright para, right");
}
private String getIntensityDescription(double i) {
if (i < 0.001)
//// None
return trans.get("simedtdlg.IntensityDesc.None");
if (i < 0.05)
//// Very low
return trans.get("simedtdlg.IntensityDesc.Verylow");
if (i < 0.10)
//// Low
return trans.get("simedtdlg.IntensityDesc.Low");
if (i < 0.15)
//// Medium
return trans.get("simedtdlg.IntensityDesc.Medium");
if (i < 0.20)
//// High
return trans.get("simedtdlg.IntensityDesc.High");
if (i < 0.25)
//// Very high
return trans.get("simedtdlg.IntensityDesc.Veryhigh");
//// Extreme
return trans.get("simedtdlg.IntensityDesc.Extreme");
}
}

View File

@ -0,0 +1,298 @@
package net.sf.openrocket.gui.simulation;
import java.awt.CardLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.adaptors.FlightConfigurationModel;
import net.sf.openrocket.gui.dialogs.flightconfiguration.FlightConfigurationDialog;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.simulation.SimulationOptions;
import net.sf.openrocket.startup.Application;
public class SimulationEditDialog 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();
JPanel cards;
private final static String EDITMODE = "EDIT";
private final static String PLOTMODE = "PLOT";
public SimulationEditDialog(Window parent, final 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 = sims;
this.conditions = simulation[0].getOptions();
configuration = simulation[0].getConfiguration();
this.cards = new JPanel(new CardLayout());
this.add(cards);
buildEditCard();
buildPlotCard();
this.validate();
this.pack();
this.setLocationByPlatform(true);
GUIUtil.setDisposableDialogOptions(this, null);
}
private boolean isSingleEdit() {
return simulation.length == 1;
}
private boolean allowsPlotMode() {
return simulation.length == 1 && simulation[0].hasSimulationData();
}
public void setEditMode() {
CardLayout cl = (CardLayout) (cards.getLayout());
cl.show(cards, EDITMODE);
cards.validate();
}
public void setPlotMode() {
if (!allowsPlotMode()) {
return;
}
CardLayout cl = (CardLayout) (cards.getLayout());
cl.show(cards, PLOTMODE);
cards.validate();
}
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 void refreshView() {
cards.removeAll();
buildEditCard();
buildPlotCard();
this.validate();
}
private void buildEditCard() {
JPanel simEditPanel = new JPanel(new MigLayout("fill"));
if (isSingleEdit()) {
//// Simulation name:
simEditPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "span, split 2, shrink");
final JTextField field = new JTextField(simulation[0].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[0].setName(name);
}
});
simEditPanel.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"));
simEditPanel.add(label, "span, split 2, 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());
}
});
simEditPanel.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);
}
});
simEditPanel.add(button, "shrink, align left, wrap");
}
JTabbedPane tabbedPane = new JTabbedPane();
//// Launch conditions
tabbedPane.addTab(trans.get("simedtdlg.tab.Launchcond"), new SimulationConditionsPanel(simulation[0]));
//// Simulation options
tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), new SimulationOptionsPanel(simulation[0]));
tabbedPane.setSelectedIndex(0);
simEditPanel.add(tabbedPane, "spanx, grow, wrap");
//// Open Plot button
JButton button = new JButton("<<Plot");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SimulationEditDialog.this.setPlotMode();
}
});
simEditPanel.add(button, "spanx, split 3, align left");
if (allowsPlotMode()) {
button.setVisible(true);
} else {
button.setVisible(false);
}
//// Run simulation button
button = new JButton(trans.get("simedtdlg.but.runsimulation"));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
copyChangesToAllSims();
SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulation);
refreshView();
if (allowsPlotMode()) {
setPlotMode();
}
}
});
simEditPanel.add(button, " align right, tag ok");
//// Close button
JButton close = new JButton(trans.get("dlg.but.close"));
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
copyChangesToAllSims();
SimulationEditDialog.this.dispose();
}
});
simEditPanel.add(close, "tag ok");
cards.add(simEditPanel, EDITMODE);
}
private void buildPlotCard() {
if (allowsPlotMode()) {
JPanel plotExportPanel = new JPanel(new MigLayout("fill"));
//// Simulation name:
plotExportPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "span, split 2, shrink");
final JTextField field = new JTextField(simulation[0].getName());
field.setEditable(false);
plotExportPanel.add(field, "shrinky, growx, wrap");
final JTabbedPane tabbedPane = new JTabbedPane();
//// Plot data
final SimulationPlotPanel plotTab = new SimulationPlotPanel(simulation[0]);
tabbedPane.addTab(trans.get("simedtdlg.tab.Plotdata"), plotTab);
//// Export data
final SimulationExportPanel exportTab = new SimulationExportPanel(simulation[0]);
tabbedPane.addTab(trans.get("simedtdlg.tab.Exportdata"), exportTab);
plotExportPanel.add(tabbedPane, "grow, wrap");
JButton button = new JButton("<<Edit");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SimulationEditDialog.this.setEditMode();
}
});
plotExportPanel.add(button, "spanx, split 3, align left");
JButton ok = new JButton(trans.get("dlg.but.ok"));
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// If the simulation is out of date, run the simulation.
if (simulation[0].getStatus() != Simulation.Status.UPTODATE) {
new SimulationRunDialog(SimulationEditDialog.this.parentWindow, document, simulation[0]).setVisible(true);
}
if (tabbedPane.getSelectedIndex() == 0) {
JDialog plot = plotTab.doPlot(SimulationEditDialog.this.parentWindow);
if (plot != null) {
plot.setVisible(true);
}
} else {
if (exportTab.doExport()) {
SimulationEditDialog.this.dispose();
}
}
}
});
plotExportPanel.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) {
SimulationEditDialog.this.dispose();
}
});
plotExportPanel.add(close, "tag cancel");
//plotExportPanel.validate();
cards.add(plotExportPanel, PLOTMODE);
}
}
}

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,294 @@
package net.sf.openrocket.gui.simulation;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.l10n.Translator;
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.UnitGroup;
import net.sf.openrocket.util.GeodeticComputationStrategy;
class SimulationOptionsPanel extends JPanel {
private static final Translator trans = Application.getTranslator();
final Simulation simulation;
SimulationOptionsPanel(final Simulation simulation) {
super(new MigLayout("fill"));
this.simulation = simulation;
final SimulationOptions conditions = simulation.getOptions();
JPanel sub, subsub;
String tip;
JLabel label;
DoubleModel m;
JSpinner spin;
UnitSelector unit;
BasicSlider slider;
//// Simulation options
sub = new JPanel(new MigLayout("fill, gap rel unrel",
"[grow][65lp!][30lp!][75lp!]", ""));
//// Simulator options
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.Simopt")));
this.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"));
label.setToolTipText(tip);
subsub.add(label, "gapright para");
//// Extended Barrowman
label = new JLabel(trans.get("simedtdlg.lbl.ExtBarrowman"));
label.setToolTipText(tip);
subsub.add(label, "growx, wrap para");
// Simulation method
tip = trans.get("simedtdlg.lbl.ttip.Simmethod1") +
trans.get("simedtdlg.lbl.ttip.Simmethod2");
label = new JLabel(trans.get("simedtdlg.lbl.Simmethod"));
label.setToolTipText(tip);
subsub.add(label, "gapright para");
label = new JLabel("6-DOF Runge-Kutta 4");
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"));
subsub.add(label, "gapright para");
EnumModel<GeodeticComputationStrategy> gcsModel = new EnumModel<GeodeticComputationStrategy>(conditions, "GeodeticComputation");
final JComboBox gcsCombo = new JComboBox(gcsModel);
ActionListener gcsTTipListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
GeodeticComputationStrategy gcs = (GeodeticComputationStrategy) gcsCombo.getSelectedItem();
gcsCombo.setToolTipText(gcs.getDescription());
}
};
gcsCombo.addActionListener(gcsTTipListener);
gcsTTipListener.actionPerformed(null);
subsub.add(gcsCombo, "growx, wrap para");
sub.add(subsub, "spanx, wrap para");
//// Time step:
label = new JLabel(trans.get("simedtdlg.lbl.Timestep"));
tip = trans.get("simedtdlg.lbl.ttip.Timestep1") +
trans.get("simedtdlg.lbl.ttip.Timestep2") + " " +
UnitGroup.UNITS_TIME_STEP.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP) +
".";
label.setToolTipText(tip);
sub.add(label);
m = new DoubleModel(conditions, "TimeStep", UnitGroup.UNITS_TIME_STEP, 0, 1);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
sub.add(spin, "w 65lp!");
//sub.add(spin, "nogrid");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
sub.add(unit, "w 25");
//sub.add(unit, "nogrid");
slider = new BasicSlider(m.getSliderModel(0, 0.2));
slider.setToolTipText(tip);
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 (
button.setToolTipText(trans.get("simedtdlg.but.ttip.resettodefault") +
UnitGroup.UNITS_SHORT_TIME.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP) +
").");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
conditions.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP);
conditions.setGeodeticComputation(GeodeticComputationStrategy.SPHERICAL);
}
});
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")));
this.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.
desc.setText(trans.get("simedtdlg.txt.longA1") +
trans.get("simedtdlg.txt.longA2"));
sub.add(desc, "aligny 0, growx, wrap para");
//// Current listeners:
label = new JLabel(trans.get("simedtdlg.lbl.Curlist"));
sub.add(label, "spanx, wrap rel");
final ListenerListModel listenerModel = new ListenerListModel();
final JList list = new JList(listenerModel);
list.setCellRenderer(new ListenerCellRenderer());
JScrollPane scroll = new JScrollPane(list);
// scroll.setPreferredSize(new Dimension(1,1));
sub.add(scroll, "height 1px, grow, wrap rel");
//// Add button
button = new JButton(trans.get("simedtdlg.but.add"));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String previous = Application.getPreferences().getString("previousListenerName", "");
String input = (String) JOptionPane.showInputDialog(SwingUtilities.getRoot(SimulationOptionsPanel.this),
new Object[] {
//// Type the full Java class name of the simulation listener, for example:
"Type the full Java class name of the simulation listener, for example:",
"<html><tt>" + CSVSaveListener.class.getName() + "</tt>" },
//// Add simulation listener
trans.get("simedtdlg.lbl.Addsimlist"),
JOptionPane.QUESTION_MESSAGE,
null, null,
previous
);
if (input == null || input.equals(""))
return;
Application.getPreferences().putString("previousListenerName", input);
simulation.getSimulationListeners().add(input);
listenerModel.fireContentsChanged();
}
});
sub.add(button, "split 2, sizegroup buttons, alignx 50%, gapright para");
//// Remove button
button = new JButton(trans.get("simedtdlg.but.remove"));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] selected = list.getSelectedIndices();
Arrays.sort(selected);
for (int i = selected.length - 1; i >= 0; i--) {
simulation.getSimulationListeners().remove(selected[i]);
}
listenerModel.fireContentsChanged();
}
});
sub.add(button, "sizegroup buttons, alignx 50%");
}
private class ListenerCellRenderer extends JLabel implements ListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
String s = value.toString();
setText(s);
// Attempt instantiating, catch any exceptions
Exception ex = null;
try {
Class<?> c = Class.forName(s);
@SuppressWarnings("unused")
SimulationListener l = (SimulationListener) c.newInstance();
} catch (Exception e) {
ex = e;
}
if (ex == null) {
setIcon(Icons.SIMULATION_LISTENER_OK);
//// Listener instantiated successfully.
setToolTipText("Listener instantiated successfully.");
} else {
setIcon(Icons.SIMULATION_LISTENER_ERROR);
//// <html>Unable to instantiate listener due to exception:<br>
setToolTipText("<html>Unable to instantiate listener due to exception:<br>" +
ex.toString());
}
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setOpaque(true);
return this;
}
}
private class ListenerListModel extends AbstractListModel {
@Override
public String getElementAt(int index) {
if (index < 0 || index >= getSize())
return null;
return simulation.getSimulationListeners().get(index);
}
@Override
public int getSize() {
return simulation.getSimulationListeners().size();
}
public void fireContentsChanged() {
super.fireContentsChanged(this, 0, getSize());
}
}
}

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,12 +11,12 @@ 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;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
@ -24,6 +25,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;
@ -71,18 +74,18 @@ public class SimulationPlotPanel extends JPanel {
PRESET_ARRAY[PRESET_ARRAY.length - 1] = CUSTOM_CONFIGURATION;
}
/** The current default configuration, set each time a plot is made. */
private static PlotConfiguration defaultConfiguration =
PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
private final Simulation simulation;
private final FlightDataType[] types;
private PlotConfiguration configuration;
private JComboBox configurationSelector;
private JComboBox domainTypeSelector;
@ -91,7 +94,7 @@ public class SimulationPlotPanel extends JPanel {
private JPanel typeSelectorPanel;
private FlightEventTableModel eventTableModel;
private int modifying = 0;
@ -107,7 +110,7 @@ public class SimulationPlotPanel extends JPanel {
types = branch.getTypes();
setConfiguration(defaultConfiguration);
//// Configuration selector
// Setup the combo box
@ -126,7 +129,7 @@ public class SimulationPlotPanel extends JPanel {
// the UI when the selected item changes.
// TODO - this should probably be implemented as an ActionListener instead
// of ItemStateListener.
if ( e.getStateChange() == ItemEvent.DESELECTED) {
if (e.getStateChange() == ItemEvent.DESELECTED) {
return;
}
if (modifying > 0)
@ -144,8 +147,8 @@ public class SimulationPlotPanel extends JPanel {
this.add(new JLabel(trans.get("simplotpanel.lbl.Presetplotconf")), "spanx, split");
this.add(configurationSelector, "growx, wrap 20lp");
//// X axis
//// X axis type:
@ -185,8 +188,8 @@ public class SimulationPlotPanel extends JPanel {
desc.setViewportBorder(BorderFactory.createEmptyBorder());
this.add(desc, "width 1px, growx 1, wrap unrel");
//// Y axis selector panel
//// Y axis types:
this.add(new JLabel(trans.get("simplotpanel.lbl.Yaxistypes")));
@ -197,7 +200,7 @@ public class SimulationPlotPanel extends JPanel {
JScrollPane scroll = new JScrollPane(typeSelectorPanel);
this.add(scroll, "spany 2, height 10px, wmin 400lp, grow 100, gapright para");
//// Flight events
eventTableModel = new FlightEventTableModel();
JTable table = new JTable(eventTableModel);
@ -213,9 +216,9 @@ 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
JButton button = new JButton(trans.get("simplotpanel.but.All"));
button.addActionListener(new ActionListener() {
@ -240,8 +243,8 @@ public class SimulationPlotPanel extends JPanel {
});
this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
//// New Y axis plot type
button = new JButton(trans.get("simplotpanel.but.NewYaxisplottype"));
button.addActionListener(new ActionListener() {
@ -260,40 +263,41 @@ public class SimulationPlotPanel extends JPanel {
// Select new type smartly
FlightDataType type = null;
for (FlightDataType t :
simulation.getSimulatedData().getBranch(0).getTypes()) {
boolean used = false;
if (configuration.getDomainAxisType().equals(t)) {
used = true;
} else {
for (int i = 0; i < configuration.getTypeCount(); i++) {
if (configuration.getType(i).equals(t)) {
used = true;
break;
}
simulation.getSimulatedData().getBranch(0).getTypes()) {
boolean used = false;
if (configuration.getDomainAxisType().equals(t)) {
used = true;
} else {
for (int i = 0; i < configuration.getTypeCount(); i++) {
if (configuration.getType(i).equals(t)) {
used = true;
break;
}
}
if (!used) {
type = t;
break;
}
}
if (type == null) {
type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
}
// Add new type
configuration.addPlotDataType(type);
setToCustom();
updatePlots();
if (!used) {
type = t;
break;
}
}
if (type == null) {
type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
}
// Add new type
configuration.addPlotDataType(type);
setToCustom();
updatePlots();
}
});
this.add(button, "spanx, split");
this.add(new JPanel(), "growx");
/*
//// Plot flight
button = new JButton(trans.get("simplotpanel.but.Plotflight"));
button.addActionListener(new ActionListener() {
@ -312,10 +316,21 @@ public class SimulationPlotPanel extends JPanel {
}
});
this.add(button, "right");
*/
updatePlots();
}
public JDialog doPlot(Window parent) {
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(parent, simulation, configuration);
}
private void setConfiguration(PlotConfiguration conf) {
@ -366,14 +381,14 @@ public class SimulationPlotPanel extends JPanel {
// In order to consistantly update the ui, we need to validate before repaint.
typeSelectorPanel.validate();
typeSelectorPanel.repaint();
typeSelectorPanel.repaint();
eventTableModel.fireTableDataChanged();
}
/**
* A JPanel which configures a single plot of a PlotConfiguration.
*/
@ -443,7 +458,7 @@ public class SimulationPlotPanel extends JPanel {
});
this.add(axisSelector);
JButton button = new JButton(Icons.DELETE);
//// Remove this plot
button.setToolTipText(trans.get("simplotpanel.but.ttip.Removethisplot"));
@ -461,7 +476,7 @@ public class SimulationPlotPanel extends JPanel {
}
private class FlightEventTableModel extends AbstractTableModel {
private final FlightEvent.Type[] eventTypes;

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui.main;
package net.sf.openrocket.gui.simulation;
import java.awt.Dialog;
@ -23,9 +23,6 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
@ -50,6 +47,9 @@ import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.MathUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimulationRunDialog extends JDialog {
private static final Logger log = LoggerFactory.getLogger(SimulationRunDialog.class);

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;
@ -86,7 +86,7 @@ public abstract class SimulationWorker extends SwingWorker<FlightData, Simulatio
protected abstract void simulationInterrupted(Throwable t);
/**
* Marks this simulation as done and calls the progress update.
*/
@ -99,7 +99,7 @@ public abstract class SimulationWorker extends SwingWorker<FlightData, Simulatio
}
/**
* A simulation listener that throws a {@link SimulationCancelledException} if
* this SwingWorker has been cancelled. The conditions is checked every time a step

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(e.getLocalizedMessage());
}
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,71 @@ public class SimulationOptions implements ChangeSource, Cloneable {
fireChangeEvent();
}
public void copyConditionsFrom(SimulationOptions src) {
// Be a little smart about triggering the change event.
// only do it if one of the "important" (user specified) parameters has really changed.
boolean isChanged = false;
if (this.launchAltitude != src.launchAltitude) {
isChanged = true;
this.launchAltitude = src.launchAltitude;
}
if (this.launchLatitude != src.launchLatitude) {
isChanged = true;
this.launchLatitude = src.launchLatitude;
}
if (this.launchLongitude != src.launchLongitude) {
isChanged = true;
this.launchLongitude = src.launchLongitude;
}
if (this.launchPressure != src.launchPressure) {
isChanged = true;
this.launchPressure = src.launchPressure;
}
if (this.launchRodAngle != src.launchRodAngle) {
isChanged = true;
this.launchRodAngle = src.launchRodAngle;
}
if (this.launchRodDirection != src.launchRodDirection) {
isChanged = true;
this.launchRodDirection = src.launchRodDirection;
}
if (this.launchRodLength != src.launchRodLength) {
isChanged = true;
this.launchRodLength = src.launchRodLength;
}
if (this.launchTemperature != src.launchTemperature) {
isChanged = true;
this.launchTemperature = src.launchTemperature;
}
if (this.maximumAngle != src.maximumAngle) {
isChanged = true;
this.maximumAngle = src.maximumAngle;
}
this.maximumAngle = src.maximumAngle;
if (this.timeStep != src.timeStep) {
isChanged = true;
this.timeStep = src.timeStep;
}
if (this.windAverage != src.windAverage) {
isChanged = true;
this.windAverage = src.windAverage;
}
if (this.windTurbulence != src.windTurbulence) {
isChanged = true;
this.windTurbulence = src.windTurbulence;
}
if (this.calculateExtras != src.calculateExtras) {
isChanged = true;
this.calculateExtras = src.calculateExtras;
}
if (isChanged) {
// Only copy the randomSeed if something else has changed.
// Honestly, I don't really see a need for that.
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.