diff --git a/core/resources/datafiles/thrustcurves/thrustcurves.ser b/core/resources/datafiles/thrustcurves/thrustcurves.ser index b6227b887..061d74654 100644 Binary files a/core/resources/datafiles/thrustcurves/thrustcurves.ser and b/core/resources/datafiles/thrustcurves/thrustcurves.ser differ diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 169cd9cdd..7dfc9d3a3 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -306,6 +306,7 @@ PreferencesDialog.lbl.languageEffect = The language will change the next time yo ! Simulation edit dialog simedtdlg.but.runsimulation = Run simulation simedtdlg.but.resettodefault = Reset to default +simedtdlg.but.savedefault = Save as default simedtdlg.but.add = Add simedtdlg.but.remove = Remove simedtdlg.title.Editsim = Edit simulation @@ -438,12 +439,6 @@ SimuRunDlg.lbl.Altitude = Altitude: SimuRunDlg.lbl.Velocity = Velocity: SimuRunDlg.msg.Unabletosim = Unable to simulate: SimuRunDlg.msg.errorOccurred = An error occurred during the simulation: -SimuRunDlg.msg.AnException1 = An exception occurred during the simulation: -SimuRunDlg.msg.AnException2 = Please report this as a bug along with the details below. -SimuRunDlg.msg.AssertionError1 = A computation error occurred during the simulation. -SimuRunDlg.msg.AssertionError2 = Please report this as a bug along with the details below. -SimuRunDlg.msg.unknownerror1 = An unknown error was encountered during the simulation. -SimuRunDlg.msg.unknownerror2 = The program may be unstable, you should save all your designs and restart OpenRocket now! RK4SimulationStepper.error.valuesTooLarge = Simulation values exceeded limits. Try selecting a shorter time step. @@ -468,7 +463,6 @@ SimExpPan.checkbox.Incflightevents = Include flight events SimExpPan.checkbox.ttip.Incflightevents = Include a comment line for every flight event. SimExpPan.lbl.Commentchar = Comment character: SimExpPan.lbl.ttip.Commentchar = The character(s) that mark a comment line. -SimExpPan.but.Exporttofile = Export to file... SimExpPan.Fileexists.desc1 = File \" SimExpPan.Fileexists.desc2 = \" exists. Overwrite? SimExpPan.Fileexists.title = File exists @@ -581,7 +575,6 @@ simplotpanel.lbl.Flightevents = Flight events: simplotpanel.but.All = All simplotpanel.but.None = None simplotpanel.but.NewYaxisplottype = New Y axis plot type -simplotpanel.but.Plotflight = Plot flight simplotpanel.lbl.Axis = Axis: simplotpanel.but.ttip.Removethisplot = Remove this plot simplotpanel.Desc = The data will be plotted in time order even if the X axis type is not time. @@ -1394,6 +1387,7 @@ FlightEvent.Type.GROUND_HIT = Ground hit FlightEvent.Type.SIMULATION_END = Simulation end FlightEvent.Type.ALTITUDE = Altitude change FlightEvent.Type.TUMBLE = Tumbling +FlightEvent.Type.EXCEPTION = Exception ! ThrustCurveMotorColumns TCurveMotorCol.MANUFACTURER = Manufacturer diff --git a/core/src/net/sf/openrocket/aerodynamics/Warning.java b/core/src/net/sf/openrocket/aerodynamics/Warning.java index 363606d72..72df1ec49 100644 --- a/core/src/net/sf/openrocket/aerodynamics/Warning.java +++ b/core/src/net/sf/openrocket/aerodynamics/Warning.java @@ -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 hashCode method compatible with the equals 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 Warning 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 Warning 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 Warning 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 Warning 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")); } diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index ad3379b8a..594734e2b 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -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. diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java index 9a4d98d69..487b0ac66 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java @@ -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); } diff --git a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java b/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java deleted file mode 100644 index 62175e4de..000000000 --- a/core/src/net/sf/openrocket/gui/main/SimulationEditDialog.java +++ /dev/null @@ -1,1070 +0,0 @@ -package net.sf.openrocket.gui.main; - - -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; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JDialog; -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.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.ListCellRenderer; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -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.SpinnerEditor; -import net.sf.openrocket.gui.adaptors.BooleanModel; -import net.sf.openrocket.gui.adaptors.DoubleModel; -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.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 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) { - //// 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(); - - JPanel mainPanel = new JPanel(new MigLayout("fill", "[grow, fill]")); - - //// 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); - - } - }); - mainPanel.add(field, "shrinky, growx, 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); - } - - 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")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - SimulationEditDialog.this.dispose(); - SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulation); - } - }); - mainPanel.add(button, "gapright para"); - - //// Close button - JButton close = new JButton(trans.get("dlg.but.close")); - close.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - SimulationEditDialog.this.dispose(); - } - }); - mainPanel.add(close, ""); - - - this.add(mainPanel); - this.validate(); - this.pack(); - this.setLocationByPlatform(true); - - GUIUtil.setDisposableDialogOptions(this, button); - } - - - - - - private JPanel flightConditionsTab() { - JPanel panel = new JPanel(new MigLayout("fill")); - JPanel sub; - String tip; - UnitSelector unit; - BasicSlider slider; - 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!]", "")); - //// Wind - 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")); - //// 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")); - //// The standard deviation of the windspeed.
- //// 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")); - //// The turbulence intensity is the standard deviation divided by the average windspeed.
- //// 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"))); - panel.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")); - //// Select to use the International Standard Atmosphere model. - ////
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"))); - panel.add(sub, "growx, split 2, aligny 0, flowy"); - - - // Latitude: - label = new JLabel(trans.get("simedtdlg.lbl.Latitude")); - //// The launch site latitude affects the gravitational pull of Earth.
- //// 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")); - //// The launch altitude above mean sea level.
- //// 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"))); - panel.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")); - //// Direction of the launch rod relative to the wind.
- //// = 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"); - - return panel; - } - - - 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"); - } - - - - private JPanel simulationOptionsTab() { - JPanel panel = new JPanel(new MigLayout("fill")); - 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"))); - 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")); - 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 gcsModel = new EnumModel(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"))); - panel.add(sub, "growx, growy"); - - - DescriptionArea desc = new DescriptionArea(5); - //// Simulation listeners is an advanced feature that allows user-written code to listen to and interact with the simulation. - //// For details on writing simulation listeners, see the OpenRocket technical documentation. - 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(SimulationEditDialog.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:", - "" + CSVSaveListener.class.getName() + "" }, - //// 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%"); - - - return panel; - } - - - 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()); - } - } - - - - - /** - * 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. - */ - public static JPanel noDataPanel() { - JPanel panel = new JPanel(new MigLayout("fill")); - - // No data available - //// No flight data available. - panel.add(new JLabel(trans.get("simedtdlg.lbl.Noflightdata")), - "alignx 50%, aligny 100%, wrap para"); - //// Please run the simulation first. - panel.add(new JLabel(trans.get("simedtdlg.lbl.runsimfirst")), - "alignx 50%, aligny 0%, wrap"); - return panel; - } - - - @SuppressWarnings("unused") - private void performPlot(PlotConfiguration config) { - - // Fill the auto-selections - FlightDataBranch branch = simulation.getSimulatedData().getBranch(0); - PlotConfiguration filled = config.fillAutoAxes(branch); - List axes = filled.getAllAxes(); - - - // Create the data series for both axes - XYSeriesCollection[] data = new XYSeriesCollection[2]; - data[0] = new XYSeriesCollection(); - data[1] = new XYSeriesCollection(); - - - // Get the domain axis type - final FlightDataType domainType = filled.getDomainAxisType(); - final Unit domainUnit = filled.getDomainAxisUnit(); - if (domainType == null) { - throw new IllegalArgumentException("Domain axis type not specified."); - } - List x = branch.get(domainType); - - - // Create the XYSeries objects from the flight data and store into the collections - int length = filled.getTypeCount(); - String[] axisLabel = new String[2]; - for (int i = 0; i < length; i++) { - // Get info - FlightDataType type = filled.getType(i); - Unit unit = filled.getUnit(i); - int axis = filled.getAxis(i); - String name = getLabel(type, unit); - - // Store data in provided units - List y = branch.get(type); - XYSeries series = new XYSeries(name, false, true); - for (int j = 0; j < x.size(); j++) { - series.add(domainUnit.toUnit(x.get(j)), unit.toUnit(y.get(j))); - } - data[axis].addSeries(series); - - // Update axis label - if (axisLabel[axis] == null) - axisLabel[axis] = type.getName(); - else - axisLabel[axis] += "; " + type.getName(); - } - - - // Create the chart using the factory to get all default settings - JFreeChart chart = ChartFactory.createXYLineChart( - //// Simulated flight - trans.get("simedtdlg.chart.Simflight"), - null, - null, - null, - PlotOrientation.VERTICAL, - true, - true, - false - ); - - - // Add the data and formatting to the plot - XYPlot plot = chart.getXYPlot(); - int axisno = 0; - for (int i = 0; i < 2; i++) { - // Check whether axis has any data - if (data[i].getSeriesCount() > 0) { - // Create and set axis - double min = axes.get(i).getMinValue(); - double max = axes.get(i).getMaxValue(); - NumberAxis axis = new PresetNumberAxis(min, max); - axis.setLabel(axisLabel[i]); - // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue()); - plot.setRangeAxis(axisno, axis); - - // Add data and map to the axis - plot.setDataset(axisno, data[i]); - plot.setRenderer(axisno, new StandardXYItemRenderer()); - plot.mapDatasetToRangeAxis(axisno, axisno); - axisno++; - } - } - - plot.getDomainAxis().setLabel(getLabel(domainType, domainUnit)); - plot.addDomainMarker(new ValueMarker(0)); - plot.addRangeMarker(new ValueMarker(0)); - - - // Create the dialog - //// Simulation results - final JDialog dialog = new JDialog(this, trans.get("simedtdlg.dlg.Simres")); - dialog.setModalityType(ModalityType.DOCUMENT_MODAL); - - JPanel panel = new JPanel(new MigLayout("fill")); - dialog.add(panel); - - ChartPanel chartPanel = new ChartPanel(chart, - false, // properties - true, // save - false, // print - true, // zoom - true); // tooltips - chartPanel.setMouseWheelEnabled(true); - chartPanel.setEnforceFileExtensions(true); - chartPanel.setInitialDelay(500); - - chartPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); - - panel.add(chartPanel, "grow, wrap 20lp"); - - //// Close button - JButton button = new JButton(trans.get("dlg.but.close")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dialog.setVisible(false); - } - }); - panel.add(button, "right"); - - dialog.setLocationByPlatform(true); - dialog.pack(); - - GUIUtil.setDisposableDialogOptions(dialog, button); - - dialog.setVisible(true); - } - - - private class PresetNumberAxis extends NumberAxis { - private final double min; - private final double max; - - public PresetNumberAxis(double min, double max) { - this.min = min; - this.max = max; - autoAdjustRange(); - } - - @Override - protected void autoAdjustRange() { - this.setRange(min, max); - } - } - - - private String getLabel(FlightDataType type, Unit unit) { - String name = type.getName(); - if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) && - !UnitGroup.UNITS_COEFFICIENT.contains(unit) && unit.getUnit().length() > 0) - name += " (" + unit.getUnit() + ")"; - return name; - } - - - - private class ListenerCellRenderer extends JLabel implements ListCellRenderer { - - @Override - 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); - //// Unable to instantiate listener due to exception:
- setToolTipText("Unable to instantiate listener due to exception:
" + - ex.toString()); - } - - if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); - } - setOpaque(true); - return this; - } - } -} diff --git a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java index 9b34c547b..cf962e6d0 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/core/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -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(); diff --git a/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java b/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java index db604a1f2..8f1f455de 100644 --- a/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java +++ b/core/src/net/sf/openrocket/gui/plot/PlotConfiguration.java @@ -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]); diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java index a254275c1..043617a51 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.List; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.simulation.SimulationPlotPanel; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightEvent; @@ -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 seriesNames = Util.generateSeriesLabels(simulation); @@ -166,7 +166,7 @@ public class SimulationPlot { List plotx = thisBranch.get(domainType); List 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); diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index 1bb158549..d57563a1c 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -7,8 +7,6 @@ import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -113,8 +111,8 @@ public class SimulationPlotDialog extends JDialog { //// Add series selection box ArrayList stages = new ArrayList(); stages.add("All"); - stages.addAll( Util.generateSeriesLabels(simulation)); - + stages.addAll(Util.generateSeriesLabels(simulation)); + final JComboBox stageSelection = new JComboBox(stages.toArray(new String[0])); stageSelection.addItemListener(new ItemListener() { @@ -159,8 +157,8 @@ public class SimulationPlotDialog extends JDialog { * @param simulation the simulation to plot. * @param config the configuration of the plot. */ - public static void showPlot(Window parent, Simulation simulation, PlotConfiguration config) { - new SimulationPlotDialog(parent, simulation, config).setVisible(true); + public static SimulationPlotDialog getPlot(Window parent, Simulation simulation, PlotConfiguration config) { + return new SimulationPlotDialog(parent, simulation, config); } } diff --git a/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java b/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java index 53a710c80..be4fbe6c9 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java +++ b/core/src/net/sf/openrocket/gui/print/PrintSimulationWorker.java @@ -4,7 +4,7 @@ package net.sf.openrocket.gui.print; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.gui.main.SimulationWorker; +import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.simulation.FlightData; /** diff --git a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 70e9928e8..5ae1d1581 100644 --- a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -54,8 +54,8 @@ import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CPCaret; import net.sf.openrocket.gui.figureelements.Caret; import net.sf.openrocket.gui.figureelements.RocketInfo; -import net.sf.openrocket.gui.main.SimulationWorker; import net.sf.openrocket.gui.main.componenttree.ComponentTreeModel; +import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.masscalc.BasicMassCalculator; diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationConditionsPanel.java b/core/src/net/sf/openrocket/gui/simulation/SimulationConditionsPanel.java new file mode 100644 index 000000000..2839704b1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationConditionsPanel.java @@ -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")); + //// The standard deviation of the windspeed.
+ //// 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")); + //// The turbulence intensity is the standard deviation divided by the average windspeed.
+ //// 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")); + //// Select to use the International Standard Atmosphere model. + ////
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")); + //// The launch site latitude affects the gravitational pull of Earth.
+ //// 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")); + //// The launch altitude above mean sea level.
+ //// 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")); + //// Direction of the launch rod relative to the wind.
+ //// = 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"); + } + +} diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java new file mode 100644 index 000000000..cdaf9e0c1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java @@ -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("< gcsModel = new EnumModel(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); + //// Simulation listeners is an advanced feature that allows user-written code to listen to and interact with the simulation. + //// For details on writing simulation listeners, see the OpenRocket technical documentation. + 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:", + "" + CSVSaveListener.class.getName() + "" }, + //// 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); + //// Unable to instantiate listener due to exception:
+ setToolTipText("Unable to instantiate listener due to exception:
" + + 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()); + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java similarity index 91% rename from core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java index 5a8787b28..40c3ebba2 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java @@ -1,5 +1,6 @@ -package net.sf.openrocket.gui.plot; +package net.sf.openrocket.gui.simulation; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -10,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; diff --git a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java similarity index 99% rename from core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java index 25bb169c1..c770be0dc 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationRunDialog.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui.main; +package net.sf.openrocket.gui.simulation; import java.awt.Dialog; @@ -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); diff --git a/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java b/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java new file mode 100644 index 000000000..45a9bfa80 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationWarningDialog.java @@ -0,0 +1,32 @@ +package net.sf.openrocket.gui.simulation; + +import java.awt.Component; +import java.util.ArrayList; + +import javax.swing.JOptionPane; + +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.dialogs.DetailDialog; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class SimulationWarningDialog { + + private static final Translator trans = Application.getTranslator(); + + public static void showWarningDialog(Component parent, Simulation simulation) { + + if (simulation.getSimulatedWarnings().size() > 0) { + ArrayList messages = new ArrayList(); + messages.add(trans.get("SimuRunDlg.msg.errorOccurred")); + for (Warning m : simulation.getSimulatedWarnings()) { + messages.add(m.toString()); + } + DetailDialog.showDetailedMessageDialog(parent, + messages.toArray(), + null, simulation.getName(), JOptionPane.ERROR_MESSAGE); + } + + } +} diff --git a/core/src/net/sf/openrocket/gui/main/SimulationWorker.java b/core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java similarity index 98% rename from core/src/net/sf/openrocket/gui/main/SimulationWorker.java rename to core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java index f348fbcdb..201b4f22e 100644 --- a/core/src/net/sf/openrocket/gui/main/SimulationWorker.java +++ b/core/src/net/sf/openrocket/gui/simulation/SimulationWorker.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui.main; +package net.sf.openrocket.gui.simulation; import java.util.Arrays; @@ -86,7 +86,7 @@ public abstract class SimulationWorker extends SwingWorker { /** * The rocket begins to tumble. */ - TUMBLE(trans.get("FlightEvent.Type.TUMBLE")); + TUMBLE(trans.get("FlightEvent.Type.TUMBLE")), + + /** + * Simulation aborted + */ + EXCEPTION(trans.get("FlightEvent.Type.EXCEPTION")); private final String name; diff --git a/core/src/net/sf/openrocket/simulation/SimulationOptions.java b/core/src/net/sf/openrocket/simulation/SimulationOptions.java index d47389b0d..f32c01407 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/core/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -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(); + } + } /** diff --git a/core/src/net/sf/openrocket/simulation/SimulationStepper.java b/core/src/net/sf/openrocket/simulation/SimulationStepper.java index 90855c3cb..f218f387e 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/SimulationStepper.java @@ -11,7 +11,7 @@ public interface SimulationStepper { * @param status the current simulation status. * @return a SimulationStatus suitable for simulating with this simulation stepper. */ - public SimulationStatus initialize(SimulationStatus status) throws SimulationException; + public SimulationStatus initialize(SimulationStatus status); /** * Perform one simulation time step.