diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index ea5bab35b..6bb9d119f 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -265,6 +265,8 @@ pref.dlg.opengl.lbl.useFBO = Use Off-screen Rendering pref.dlg.lbl.Positiontoinsert = Position to insert new body components: pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations: +pref.dlg.checkbox.Runsimulations = Run out-dated simulations when you open the simulation tab. +pref.dlg.checkbox.Updateestimates = Update estimated flight parameters in design window pref.dlg.lbl.User-definedthrust = User-defined thrust curves: pref.dlg.lbl.Windspeed = Wind speed pref.dlg.Allthrustcurvefiles = All thrust curve files (*.eng; *.rse; *.zip; directories) diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index 594734e2b..da5c74976 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -2,6 +2,7 @@ package net.sf.openrocket.document; import java.util.EventListener; import java.util.EventObject; +import java.util.Iterator; import java.util.List; import net.sf.openrocket.aerodynamics.AerodynamicCalculator; @@ -10,8 +11,14 @@ import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.BasicEventSimulationEngine; import net.sf.openrocket.simulation.DefaultSimulationOptionFactory; import net.sf.openrocket.simulation.FlightData; @@ -58,7 +65,10 @@ public class Simulation implements ChangeSource, Cloneable { EXTERNAL, /** Not yet simulated */ - NOT_SIMULATED + NOT_SIMULATED, + + /** Can't be simulated, NO_MOTORS **/ + CANT_RUN } private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); @@ -250,13 +260,35 @@ public class Simulation implements ChangeSource, Cloneable { */ public Status getStatus() { mutex.verify(); - if (status == Status.UPTODATE || status == Status.LOADED) { - if (rocket.getFunctionalModID() != simulatedRocketID || - !options.equals(simulatedConditions)) - return Status.OUTDATED; + if (rocket.getFunctionalModID() != simulatedRocketID || !options.equals(simulatedConditions)) { + status = Status.OUTDATED; + } } + + //Make sure this simulation has motors. + Configuration c = new Configuration(this.getRocket()); + MotorInstanceConfiguration motors = new MotorInstanceConfiguration(); + c.setFlightConfigurationID(options.getMotorConfigurationID()); + final String flightConfigId = c.getFlightConfigurationID(); + + Iterator iterator = c.motorIterator(); + boolean no_motors = true; + + while (iterator.hasNext()) { + MotorMount mount = iterator.next(); + RocketComponent component = (RocketComponent) mount; + MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId); + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId); + Motor motor = motorConfig.getMotor(); + if (motor != null) + no_motors = false; + } + + if (no_motors) + status = Status.CANT_RUN; + return status; } diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 23f8cf58c..b7d8889a0 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -43,8 +43,8 @@ public class BasicEventSimulationEngine implements SimulationEngine { private SimulationStepper landingStepper = new BasicLandingStepper(); private SimulationStepper tumbleStepper = new BasicTumbleStepper(); - // Constant holding 20 degress in radians. This is the AOA condition - // necessary to transistion to tumbling. + // Constant holding 20 degrees in radians. This is the AOA condition + // necessary to transition to tumbling. private final static double AOA_TUMBLE_CONDITION = Math.PI / 9.0; // The thrust must be below this value for the transition to tumbling. diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index 16d9721d1..0f9c7f3e7 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -30,6 +30,8 @@ public abstract class Preferences { public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition"; public static final String USER_THRUST_CURVES_KEY = "UserThrustCurves"; public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation"; + public static final String AUTO_RUN_SIMULATIONS = "AutoRunSimulations"; + // Preferences related to data export public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator"; public static final String EXPORT_SIMULATION_COMMENT = "ExportSimulationComment"; @@ -101,6 +103,14 @@ public abstract class Preferences { this.putBoolean(CHECK_UPDATES, check); } + public final boolean getAutoRunSimulations() { + return this.getBoolean(AUTO_RUN_SIMULATIONS, false); + } + + public final void setAutoRunSimulations(boolean check) { + this.putBoolean(AUTO_RUN_SIMULATIONS, check); + } + public final double getDefaultMach() { // TODO: HIGH: implement custom default mach number return 0.3; diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java index f60b54b55..5fdfa4f60 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java @@ -189,7 +189,7 @@ public class PreferencesDialog extends JDialog { trans.get("pref.dlg.PrefFontSmall"), trans.get("pref.dlg.PrefFontMedium"), trans.get("pref.dlg.PrefFontLarge"))), "wrap 40lp, growx, sg combos"); - + //// User-defined thrust curves: panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap"); final JTextField field = new JTextField(); @@ -295,6 +295,7 @@ public class PreferencesDialog extends JDialog { + //// Check for software updates at startup final JCheckBox softwareUpdateBox = new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates")); @@ -327,8 +328,32 @@ public class PreferencesDialog extends JDialog { preferences.setAutoOpenLastDesignOnStartup(autoOpenDesignFile.isSelected()); } }); - panel.add(autoOpenDesignFile); + panel.add(autoOpenDesignFile, "wrap, growx, span 2"); + //// Automatically run all simulation out-dated by design changes. + final JCheckBox automaticallyRunSimsBox = + new JCheckBox(trans.get("pref.dlg.checkbox.Runsimulations")); + automaticallyRunSimsBox.setSelected(preferences.getAutoRunSimulations()); + automaticallyRunSimsBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setAutoRunSimulations(automaticallyRunSimsBox.isSelected()); + } + }); + panel.add(automaticallyRunSimsBox, "wrap, growx, sg combos "); + + //// Update flight estimates in the design window + final JCheckBox updateEstimates = + new JCheckBox(trans.get("pref.dlg.checkbox.Updateestimates")); + updateEstimates.setSelected(preferences.computeFlightInBackground()); + updateEstimates.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setComputeFlightInBackground(updateEstimates.isSelected()); + } + }); + panel.add(updateEstimates, "wrap, growx, sg combos "); + return panel; } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index fd8c11272..10fb9e71f 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -54,6 +54,7 @@ import javax.swing.border.BevelBorder; import javax.swing.border.TitledBorder; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; +import javax.swing.event.ChangeEvent; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; @@ -153,7 +154,7 @@ public class BasicFrame extends JFrame { /** Actions available for rocket modifications */ private final RocketActions actions; - + private SimulationPanel simulationPanel; /** @@ -174,7 +175,7 @@ public class BasicFrame extends JFrame { componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); // Obtain the simulation selection model that will be used - SimulationPanel simulationPanel = new SimulationPanel(document); + simulationPanel = new SimulationPanel(document); simulationSelectionModel = simulationPanel.getSimulationListSelectionModel(); // Combine into a DocumentSelectionModel @@ -203,6 +204,11 @@ public class BasicFrame extends JFrame { //// Flight simulations tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); + // Add change listener to catch when the tabs are changed. This is to run simulations + // automagically when the simulation tab is selected. + tabbedPane.addChangeListener(new BasicFrame_changeAdapter(this)); + + vertical.setTopComponent(tabbedPane); @@ -1553,4 +1559,24 @@ public class BasicFrame extends JFrame { return null; } } + + public void stateChanged(ChangeEvent e) { + JTabbedPane tabSource = (JTabbedPane) e.getSource(); + String tab = tabSource.getTitleAt(tabSource.getSelectedIndex()); + if (tab.equals(trans.get("BasicFrame.tab.Flightsim"))) { + simulationPanel.activating(); + } + } } + + +class BasicFrame_changeAdapter implements javax.swing.event.ChangeListener { + BasicFrame adaptee; + + BasicFrame_changeAdapter(BasicFrame adaptee) { + this.adaptee = adaptee; + } + public void stateChanged(ChangeEvent e) { + adaptee.stateChanged(e); + } +} \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index c25d19eb1..a81436995 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -44,6 +44,7 @@ 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.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; @@ -586,6 +587,44 @@ public class SimulationPanel extends JPanel { } + /// when the simulation tab is selected this run outdated simulated if appropriate. + public void activating(){ + if( ((Preferences) Application.getPreferences()).getAutoRunSimulations()){ + int nSims = simulationTable.getRowCount(); + int outdated = 0; + if (nSims == 0) { + return; + } + + for (int i = 0; i < nSims; i++) { + Simulation.Status s = document.getSimulation(simulationTable.convertRowIndexToModel(i)).getStatus(); + if((s==Simulation.Status.NOT_SIMULATED) || + (s==Simulation.Status.OUTDATED)){ + outdated++; + } + } + if(outdated>0){ + Simulation[] sims = new Simulation[outdated]; + + int index=0; + for (int i = 0; i < nSims; i++) { + int t = simulationTable.convertRowIndexToModel(i); + Simulation s = document.getSimulation(t); + if((s.getStatus()==Status.NOT_SIMULATED)||(s.getStatus()==Status.OUTDATED)){ + sims[index] = s; + index++; + } + } + + long t = System.currentTimeMillis(); + new SimulationRunDialog(SwingUtilities.getWindowAncestor( + SimulationPanel.this), document, sims).setVisible(true); + log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); + fireMaintainSelection(); + } + } + } + public ListSelectionModel getSimulationListSelectionModel() { return simulationTable.getSelectionModel(); } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index c458b8e66..d011ea5ec 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -699,15 +699,17 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } // Start calculation process - extraText.setCalculatingData(true); + if(((SwingPreferences) Application.getPreferences()).computeFlightInBackground()){ + extraText.setCalculatingData(true); + + Rocket duplicate = (Rocket) configuration.getRocket().copy(); + Simulation simulation = ((SwingPreferences) Application.getPreferences()).getBackgroundSimulation(duplicate); + simulation.getOptions().setMotorConfigurationID( + configuration.getFlightConfigurationID()); - Rocket duplicate = (Rocket) configuration.getRocket().copy(); - Simulation simulation = ((SwingPreferences) Application.getPreferences()).getBackgroundSimulation(duplicate); - simulation.getOptions().setMotorConfigurationID( - configuration.getFlightConfigurationID()); - - backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation); - backgroundSimulationExecutor.execute(backgroundSimulationWorker); + backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation); + backgroundSimulationExecutor.execute(backgroundSimulationWorker); + } } /** diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java index 0246cb2eb..ff67daefa 100644 --- a/swing/src/net/sf/openrocket/gui/util/Icons.java +++ b/swing/src/net/sf/openrocket/gui/util/Icons.java @@ -31,6 +31,7 @@ public class Icons { static { HashMap map = new HashMap(); map.put(Simulation.Status.NOT_SIMULATED, loadImageIcon("pix/spheres/gray-16x16.png", "Not simulated")); + map.put(Simulation.Status.CANT_RUN, loadImageIcon("pix/spheres/yellow-16x16.png", "Can't run, no motors assigned.")); map.put(Simulation.Status.UPTODATE, loadImageIcon("pix/spheres/green-16x16.png", "Up to date")); map.put(Simulation.Status.LOADED, loadImageIcon("pix/spheres/yellow-16x16.png", "Loaded from file")); map.put(Simulation.Status.OUTDATED, loadImageIcon("pix/spheres/red-16x16.png", "Out-of-date")); diff --git a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java index 5b35c753d..499620c9b 100644 --- a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java +++ b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java @@ -418,7 +418,11 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences { public boolean computeFlightInBackground() { return PREFNODE.getBoolean("backgroundFlight", true); } - + + public void setComputeFlightInBackground(boolean b) { + PREFNODE.putBoolean("backgroundFlight", b); + } + public Simulation getBackgroundSimulation(Rocket rocket) { Simulation s = new Simulation(rocket); SimulationOptions cond = s.getOptions();