Rework the CustomExpression evaluation to use SimulationListeners. Removed the OpenRocketDocument member variable from the Simulation object.

This commit is contained in:
Kevin Ruland 2012-08-08 02:24:40 +00:00
parent 2b802c2f7d
commit a5c3c1ac76
17 changed files with 232 additions and 211 deletions

View File

@ -75,8 +75,7 @@ public class CurrentRocket {
public synchronized void addNewSimulation( Context context ) { public synchronized void addNewSimulation( Context context ) {
isModified = true; isModified = true;
Rocket rocket = rocketDocument.getRocket(); Rocket rocket = rocketDocument.getRocket();
// FIXME - hopefully the change to the Simulation object will be reverted soon. Simulation newSim = new Simulation(rocket);
Simulation newSim = new Simulation(rocketDocument, rocket);
newSim.setName(rocketDocument.getNextSimulationName()); newSim.setName(rocketDocument.getNextSimulationName());
rocketDocument.addSimulation(newSim); rocketDocument.addSimulation(newSim);
notifySimsChanged(context); notifySimsChanged(context);

View File

@ -117,7 +117,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
customExpressions.remove(expression); customExpressions.remove(expression);
} }
public ArrayList<CustomExpression> getCustomExpressions(){ public List<CustomExpression> getCustomExpressions(){
return customExpressions; return customExpressions;
} }

View File

@ -60,7 +60,6 @@ public class Simulation implements ChangeSource, Cloneable {
private SafetyMutex mutex = SafetyMutex.newInstance(); private SafetyMutex mutex = SafetyMutex.newInstance();
private final Rocket rocket; private final Rocket rocket;
private final OpenRocketDocument document;
private String name = ""; private String name = "";
@ -94,11 +93,7 @@ public class Simulation implements ChangeSource, Cloneable {
* *
* @param rocket the rocket associated with the simulation. * @param rocket the rocket associated with the simulation.
*/ */
public Simulation(OpenRocketDocument doc, Rocket rocket) { public Simulation(Rocket rocket) {
// It may seem silly to pass in the document and rocket, since usually when called we
// use doc.getRocket, but I guess there is some reason; when cloning a simulation + rocket we don't need
// to make a duplicate of the undo data etc stored in the document. --Richard
this.document = doc;
this.rocket = rocket; this.rocket = rocket;
this.status = Status.NOT_SIMULATED; this.status = Status.NOT_SIMULATED;
@ -109,7 +104,7 @@ public class Simulation implements ChangeSource, Cloneable {
} }
public Simulation(OpenRocketDocument doc, Rocket rocket, Status status, String name, SimulationOptions options, public Simulation(Rocket rocket, Status status, String name, SimulationOptions options,
List<String> listeners, FlightData data) { List<String> listeners, FlightData data) {
if (rocket == null) if (rocket == null)
@ -122,7 +117,6 @@ public class Simulation implements ChangeSource, Cloneable {
throw new IllegalArgumentException("options cannot be null"); throw new IllegalArgumentException("options cannot be null");
this.rocket = rocket; this.rocket = rocket;
this.document = doc;
if (status == Status.UPTODATE) { if (status == Status.UPTODATE) {
this.status = Status.LOADED; this.status = Status.LOADED;
@ -152,13 +146,6 @@ public class Simulation implements ChangeSource, Cloneable {
} }
/*
* Return the parent document for this simulation
*/
public OpenRocketDocument getDocument(){
return document;
}
/** /**
* Return the rocket associated with this simulation. * Return the rocket associated with this simulation.
* *
@ -423,7 +410,7 @@ public class Simulation implements ChangeSource, Cloneable {
public Simulation duplicateSimulation(Rocket newRocket) { public Simulation duplicateSimulation(Rocket newRocket) {
mutex.lock("duplicateSimulation"); mutex.lock("duplicateSimulation");
try { try {
Simulation copy = new Simulation(document, newRocket); Simulation copy = new Simulation(newRocket);
copy.name = this.name; copy.name = this.name;
copy.options.copyFrom(this.options); copy.options.copyFrom(this.options);

View File

@ -1383,7 +1383,7 @@ class SingleSimulationHandler extends AbstractElementHandler {
else else
data = dataHandler.getFlightData(); data = dataHandler.getFlightData();
Simulation simulation = new Simulation(doc, doc.getRocket(), status, name, Simulation simulation = new Simulation(doc.getRocket(), status, name,
conditions, listeners, data); conditions, listeners, data);
doc.addSimulation(simulation); doc.addSimulation(simulation);

View File

@ -5,8 +5,8 @@ import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JButton; import javax.swing.JButton;
@ -17,23 +17,17 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.DatabaseMotorFinder; import net.sf.openrocket.file.DatabaseMotorFinder;
import net.sf.openrocket.file.GeneralRocketLoader; import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.file.MotorFinder;
import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.file.RocketLoadException;
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.components.UnitSelector;
import net.sf.openrocket.gui.customexpression.ExpressionBuilderDialog;
import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpression;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
@ -158,7 +152,7 @@ public class CustomExpressionPanel extends JPanel {
* @param move integer - +1 to move down, -1 to move up * @param move integer - +1 to move down, -1 to move up
*/ */
private void moveExpression(CustomExpression expression, int move){ private void moveExpression(CustomExpression expression, int move){
ArrayList<CustomExpression> expressions = doc.getCustomExpressions(); List<CustomExpression> expressions = doc.getCustomExpressions();
int i = expressions.indexOf(expression); int i = expressions.indexOf(expression);
if (i+move == expressions.size() || i+move < 0) if (i+move == expressions.size() || i+move < 0)
return; return;

View File

@ -1000,14 +1000,14 @@ public class GeneralOptimizationDialog extends JDialog {
if (id == null) { if (id == null) {
continue; continue;
} }
Simulation sim = new Simulation(documentCopy, rocket); Simulation sim = new Simulation(rocket);
sim.getConfiguration().setMotorConfigurationID(id); sim.getConfiguration().setMotorConfigurationID(id);
String name = createSimulationName(trans.get("basicSimulationName"), rocket.getMotorConfigurationNameOrDescription(id)); String name = createSimulationName(trans.get("basicSimulationName"), rocket.getMotorConfigurationNameOrDescription(id));
simulations.add(new Named<Simulation>(sim, name)); simulations.add(new Named<Simulation>(sim, name));
} }
Simulation sim = new Simulation(documentCopy, rocket); Simulation sim = new Simulation(rocket);
sim.getConfiguration().setMotorConfigurationID(null); sim.getConfiguration().setMotorConfigurationID(null);
String name = createSimulationName(trans.get("noSimulationName"), rocket.getMotorConfigurationNameOrDescription(null)); String name = createSimulationName(trans.get("noSimulationName"), rocket.getMotorConfigurationNameOrDescription(null));
simulations.add(new Named<Simulation>(sim, name)); simulations.add(new Named<Simulation>(sim, name));

View File

@ -30,6 +30,7 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.BooleanModel;
@ -41,7 +42,6 @@ import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.SimulationExportPanel; import net.sf.openrocket.gui.components.SimulationExportPanel;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.plot.Axis; import net.sf.openrocket.gui.plot.Axis;
import net.sf.openrocket.gui.customexpression.CustomExpressionPanel;
import net.sf.openrocket.gui.plot.PlotConfiguration; import net.sf.openrocket.gui.plot.PlotConfiguration;
import net.sf.openrocket.gui.plot.SimulationPlotPanel; import net.sf.openrocket.gui.plot.SimulationPlotPanel;
import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.GUIUtil;
@ -83,19 +83,20 @@ public class SimulationEditDialog extends JDialog {
private final Window parentWindow; private final Window parentWindow;
private final Simulation simulation; private final Simulation simulation;
private final OpenRocketDocument document;
private final SimulationOptions conditions; private final SimulationOptions conditions;
private final Configuration configuration; private final Configuration configuration;
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
public SimulationEditDialog(Window parent, Simulation s) { public SimulationEditDialog(Window parent, OpenRocketDocument document, Simulation s) {
this(parent, s, 0); this(parent, document, s, 0);
} }
public SimulationEditDialog(Window parent, Simulation s, int tab) { public SimulationEditDialog(Window parent, OpenRocketDocument document, Simulation s, int tab) {
//// Edit simulation //// Edit simulation
super(parent, trans.get("simedtdlg.title.Editsim"), JDialog.ModalityType.DOCUMENT_MODAL); super(parent, trans.get("simedtdlg.title.Editsim"), JDialog.ModalityType.DOCUMENT_MODAL);
this.document = document;
this.parentWindow = parent; this.parentWindow = parent;
this.simulation = s; this.simulation = s;
this.conditions = simulation.getOptions(); this.conditions = simulation.getOptions();
@ -170,7 +171,7 @@ public class SimulationEditDialog extends JDialog {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
SimulationEditDialog.this.dispose(); SimulationEditDialog.this.dispose();
SimulationRunDialog.runSimulations(parentWindow, simulation); SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulation);
} }
}); });
mainPanel.add(button, "gapright para"); mainPanel.add(button, "gapright para");

View File

@ -82,7 +82,7 @@ public class SimulationPanel extends JPanel {
button.addActionListener(new ActionListener() { button.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Simulation sim = new Simulation(document, document.getRocket()); Simulation sim = new Simulation(document.getRocket());
sim.setName(document.getNextSimulationName()); sim.setName(document.getNextSimulationName());
int n = document.getSimulationCount(); int n = document.getSimulationCount();
@ -135,7 +135,7 @@ public class SimulationPanel extends JPanel {
long t = System.currentTimeMillis(); long t = System.currentTimeMillis();
new SimulationRunDialog(SwingUtilities.getWindowAncestor( new SimulationRunDialog(SwingUtilities.getWindowAncestor(
SimulationPanel.this), sims).setVisible(true); SimulationPanel.this), document, sims).setVisible(true);
log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms");
fireMaintainSelection(); fireMaintainSelection();
} }
@ -514,7 +514,7 @@ public class SimulationPanel extends JPanel {
} }
private void openDialog(final Simulation sim, int position) { private void openDialog(final Simulation sim, int position) {
new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), sim, position) new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sim, position)
.setVisible(true); .setVisible(true);
fireMaintainSelection(); fireMaintainSelection();
} }

View File

@ -24,6 +24,7 @@ import javax.swing.JPanel;
import javax.swing.JProgressBar; import javax.swing.JProgressBar;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.dialogs.DetailDialog; import net.sf.openrocket.gui.dialogs.DetailDialog;
import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.GUIUtil;
@ -35,6 +36,8 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent; import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;
import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.customexpression.CustomExpression;
import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener;
import net.sf.openrocket.simulation.exception.SimulationCancelledException; import net.sf.openrocket.simulation.exception.SimulationCancelledException;
import net.sf.openrocket.simulation.exception.SimulationException; import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.exception.SimulationLaunchException; import net.sf.openrocket.simulation.exception.SimulationLaunchException;
@ -95,6 +98,7 @@ public class SimulationRunDialog extends JDialog {
* will result in an exception being thrown! * will result in an exception being thrown!
*/ */
private final Simulation[] simulations; private final Simulation[] simulations;
private final OpenRocketDocument document;
private final String[] simulationNames; private final String[] simulationNames;
private final SimulationWorker[] simulationWorkers; private final SimulationWorker[] simulationWorkers;
private final SimulationStatus[] simulationStatuses; private final SimulationStatus[] simulationStatuses;
@ -102,9 +106,10 @@ public class SimulationRunDialog extends JDialog {
private final double[] simulationMaxVelocity; private final double[] simulationMaxVelocity;
private final boolean[] simulationDone; private final boolean[] simulationDone;
public SimulationRunDialog(Window window, Simulation... simulations) { public SimulationRunDialog(Window window, OpenRocketDocument document, Simulation... simulations) {
//// Running simulations... //// Running simulations...
super(window, trans.get("SimuRunDlg.title.RunSim"), Dialog.ModalityType.APPLICATION_MODAL); super(window, trans.get("SimuRunDlg.title.RunSim"), Dialog.ModalityType.APPLICATION_MODAL);
this.document = document;
if (simulations.length == 0) { if (simulations.length == 0) {
throw new IllegalArgumentException("Called with no simulations to run"); throw new IllegalArgumentException("Called with no simulations to run");
@ -129,7 +134,7 @@ public class SimulationRunDialog extends JDialog {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
simulationNames[i] = simulations[i].getName(); simulationNames[i] = simulations[i].getName();
simulationWorkers[i] = new InteractiveSimulationWorker(simulations[i], i); simulationWorkers[i] = new InteractiveSimulationWorker(document, simulations[i], i);
executor.execute(simulationWorkers[i]); executor.execute(simulationWorkers[i]);
} }
@ -208,8 +213,8 @@ public class SimulationRunDialog extends JDialog {
* @param parent the parent Window of the dialog to use. * @param parent the parent Window of the dialog to use.
* @param simulations the simulations to run. * @param simulations the simulations to run.
*/ */
public static void runSimulations(Window parent, Simulation... simulations) { public static void runSimulations(Window parent, OpenRocketDocument document, Simulation... simulations) {
new SimulationRunDialog(parent, simulations).setVisible(true); new SimulationRunDialog(parent, document, simulations).setVisible(true);
} }
@ -277,6 +282,8 @@ public class SimulationRunDialog extends JDialog {
private volatile double burnoutVelocity; private volatile double burnoutVelocity;
private volatile double apogeeAltitude; private volatile double apogeeAltitude;
private final CustomExpressionSimulationListener exprListener;
/* /*
* -2 = time from 0 ... burnoutTimeEstimate * -2 = time from 0 ... burnoutTimeEstimate
* -1 = velocity from v(burnoutTimeEstimate) ... 0 * -1 = velocity from v(burnoutTimeEstimate) ... 0
@ -287,8 +294,10 @@ public class SimulationRunDialog extends JDialog {
private int progress = 0; private int progress = 0;
public InteractiveSimulationWorker(Simulation sim, int index) { public InteractiveSimulationWorker(OpenRocketDocument doc, Simulation sim, int index) {
super(sim); super(sim);
List<CustomExpression> exprs = doc.getCustomExpressions();
exprListener = new CustomExpressionSimulationListener(exprs);
this.index = index; this.index = index;
// Calculate estimate of motor burn time // Calculate estimate of motor burn time
@ -314,7 +323,7 @@ public class SimulationRunDialog extends JDialog {
*/ */
@Override @Override
protected SimulationListener[] getExtraListeners() { protected SimulationListener[] getExtraListeners() {
return new SimulationListener[] { new SimulationProgressListener() }; return new SimulationListener[] { new SimulationProgressListener(), exprListener };
} }

View File

@ -62,6 +62,8 @@ import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightData;
import net.sf.openrocket.simulation.customexpression.CustomExpression;
import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener;
import net.sf.openrocket.simulation.listeners.SimulationListener; import net.sf.openrocket.simulation.listeners.SimulationListener;
import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener; import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
import net.sf.openrocket.simulation.listeners.system.InterruptListener; import net.sf.openrocket.simulation.listeners.system.InterruptListener;
@ -87,47 +89,47 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
private boolean is3d; private boolean is3d;
private final RocketFigure figure; private final RocketFigure figure;
private final RocketFigure3d figure3d; private final RocketFigure3d figure3d;
private final ScaleScrollPane scrollPane; private final ScaleScrollPane scrollPane;
private final JPanel figureHolder; private final JPanel figureHolder;
private JLabel infoMessage; private JLabel infoMessage;
private TreeSelectionModel selectionModel = null; private TreeSelectionModel selectionModel = null;
private BasicSlider rotationSlider; private BasicSlider rotationSlider;
ScaleSelector scaleSelector; ScaleSelector scaleSelector;
/* Calculation of CP and CG */ /* Calculation of CP and CG */
private AerodynamicCalculator aerodynamicCalculator; private AerodynamicCalculator aerodynamicCalculator;
private MassCalculator massCalculator; private MassCalculator massCalculator;
private final OpenRocketDocument document; private final OpenRocketDocument document;
private final Configuration configuration; private final Configuration configuration;
private Caret extraCP = null; private Caret extraCP = null;
private Caret extraCG = null; private Caret extraCG = null;
private RocketInfo extraText = null; private RocketInfo extraText = null;
private double cpAOA = Double.NaN; private double cpAOA = Double.NaN;
private double cpTheta = Double.NaN; private double cpTheta = Double.NaN;
private double cpMach = Double.NaN; private double cpMach = Double.NaN;
private double cpRoll = Double.NaN; private double cpRoll = Double.NaN;
// The functional ID of the rocket that was simulated // The functional ID of the rocket that was simulated
private int flightDataFunctionalID = -1; private int flightDataFunctionalID = -1;
private String flightDataMotorID = null; private String flightDataMotorID = null;
private SimulationWorker backgroundSimulationWorker = null; private SimulationWorker backgroundSimulationWorker = null;
private List<EventListener> listeners = new ArrayList<EventListener>(); private List<EventListener> listeners = new ArrayList<EventListener>();
/** /**
* The executor service used for running the background simulations. * The executor service used for running the background simulations.
@ -138,34 +140,34 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
static { static {
backgroundSimulationExecutor = Executors.newFixedThreadPool(SwingPreferences.getMaxThreadCount(), backgroundSimulationExecutor = Executors.newFixedThreadPool(SwingPreferences.getMaxThreadCount(),
new ThreadFactory() { new ThreadFactory() {
private ThreadFactory factory = Executors.defaultThreadFactory(); private ThreadFactory factory = Executors.defaultThreadFactory();
@Override @Override
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread t = factory.newThread(r); Thread t = factory.newThread(r);
t.setDaemon(true); t.setDaemon(true);
t.setPriority(Thread.MIN_PRIORITY); t.setPriority(Thread.MIN_PRIORITY);
return t; return t;
} }
}); });
} }
public RocketPanel(OpenRocketDocument document) { public RocketPanel(OpenRocketDocument document) {
this.document = document; this.document = document;
configuration = document.getDefaultConfiguration(); configuration = document.getDefaultConfiguration();
// TODO: FUTURE: calculator selection // TODO: FUTURE: calculator selection
aerodynamicCalculator = new BarrowmanCalculator(); aerodynamicCalculator = new BarrowmanCalculator();
massCalculator = new BasicMassCalculator(); massCalculator = new BasicMassCalculator();
// Create figure and custom scroll pane // Create figure and custom scroll pane
figure = new RocketFigure(configuration); figure = new RocketFigure(configuration);
figure3d = new RocketFigure3d(configuration); figure3d = new RocketFigure3d(configuration);
figureHolder = new JPanel(new BorderLayout()); figureHolder = new JPanel(new BorderLayout());
scrollPane = new ScaleScrollPane(figure) { scrollPane = new ScaleScrollPane(figure) {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -176,12 +178,12 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
}; };
scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
scrollPane.setFitting(true); scrollPane.setFitting(true);
createPanel(); createPanel();
is3d = true; is3d = true;
go2D(); go2D();
configuration.addChangeListener(new StateChangeListener() { configuration.addChangeListener(new StateChangeListener() {
@Override @Override
public void stateChanged(EventObject e) { public void stateChanged(EventObject e) {
@ -190,7 +192,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
updateFigures(); updateFigures();
} }
}); });
figure3d.addComponentSelectionListener(new RocketFigure3d.ComponentSelectionListener() { figure3d.addComponentSelectionListener(new RocketFigure3d.ComponentSelectionListener() {
@Override @Override
public void componentClicked(RocketComponent clicked[], MouseEvent event) { public void componentClicked(RocketComponent clicked[], MouseEvent event) {
@ -198,7 +200,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
}); });
} }
private void updateFigures() { private void updateFigures() {
if (!is3d) if (!is3d)
figure.updateFigure(); figure.updateFigure();
@ -233,20 +235,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figureHolder.revalidate(); figureHolder.revalidate();
figure.repaint(); figure.repaint();
} }
/** /**
* Creates the layout and components of the panel. * Creates the layout and components of the panel.
*/ */
private void createPanel() { private void createPanel() {
setLayout(new MigLayout("", "[shrink][grow]", "[shrink][shrink][grow][shrink]")); setLayout(new MigLayout("", "[shrink][grow]", "[shrink][shrink][grow][shrink]"));
setPreferredSize(new Dimension(800, 300)); setPreferredSize(new Dimension(800, 300));
//// Create toolbar //// Create toolbar
ButtonGroup bg = new ButtonGroup(); ButtonGroup bg = new ButtonGroup();
// Side/back buttons // Side/back buttons
FigureTypeAction action = new FigureTypeAction(RocketFigure.TYPE_SIDE); FigureTypeAction action = new FigureTypeAction(RocketFigure.TYPE_SIDE);
//// Side view //// Side view
@ -256,7 +258,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
JToggleButton toggle = new JToggleButton(action); JToggleButton toggle = new JToggleButton(action);
bg.add(toggle); bg.add(toggle);
add(toggle, "spanx, split"); add(toggle, "spanx, split");
action = new FigureTypeAction(RocketFigure.TYPE_BACK); action = new FigureTypeAction(RocketFigure.TYPE_BACK);
//// Back view //// Back view
action.putValue(Action.NAME, trans.get("RocketPanel.FigTypeAct.Backview")); action.putValue(Action.NAME, trans.get("RocketPanel.FigTypeAct.Backview"));
@ -265,7 +267,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
toggle = new JToggleButton(action); toggle = new JToggleButton(action);
bg.add(toggle); bg.add(toggle);
add(toggle, "gap rel"); add(toggle, "gap rel");
//// 3d Toggle //// 3d Toggle
final JToggleButton toggle3d = new JToggleButton(new AbstractAction("3D") { final JToggleButton toggle3d = new JToggleButton(new AbstractAction("3D") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -284,18 +286,18 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
}); });
bg.add(toggle3d); bg.add(toggle3d);
add(toggle3d, "gap rel"); add(toggle3d, "gap rel");
// Zoom level selector // Zoom level selector
scaleSelector = new ScaleSelector(scrollPane); scaleSelector = new ScaleSelector(scrollPane);
add(scaleSelector); add(scaleSelector);
// Stage selector // Stage selector
StageSelector stageSelector = new StageSelector(configuration); StageSelector stageSelector = new StageSelector(configuration);
add(stageSelector, ""); add(stageSelector, "");
// Motor configuration selector // Motor configuration selector
@ -304,55 +306,55 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
label.setHorizontalAlignment(JLabel.RIGHT); label.setHorizontalAlignment(JLabel.RIGHT);
add(label, "growx, right"); add(label, "growx, right");
add(new JComboBox(new MotorConfigurationModel(configuration)), "wrap"); add(new JComboBox(new MotorConfigurationModel(configuration)), "wrap");
// Create slider and scroll pane // Create slider and scroll pane
DoubleModel theta = new DoubleModel(figure, "Rotation", DoubleModel theta = new DoubleModel(figure, "Rotation",
UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI); UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
UnitSelector us = new UnitSelector(theta, true); UnitSelector us = new UnitSelector(theta, true);
us.setHorizontalAlignment(JLabel.CENTER); us.setHorizontalAlignment(JLabel.CENTER);
add(us, "alignx 50%, growx"); add(us, "alignx 50%, growx");
// Add the rocket figure // Add the rocket figure
add(figureHolder, "grow, spany 2, wmin 300lp, hmin 100lp, wrap"); add(figureHolder, "grow, spany 2, wmin 300lp, hmin 100lp, wrap");
// Add rotation slider // Add rotation slider
// Minimum size to fit "360deg" // Minimum size to fit "360deg"
JLabel l = new JLabel("360" + Chars.DEGREE); JLabel l = new JLabel("360" + Chars.DEGREE);
Dimension d = l.getPreferredSize(); Dimension d = l.getPreferredSize();
add(rotationSlider = new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true), add(rotationSlider = new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true),
"ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy"); "ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy");
//// <html>Click to select &nbsp;&nbsp; Shift+click to select other &nbsp;&nbsp; Double-click to edit &nbsp;&nbsp; Click+drag to move //// <html>Click to select &nbsp;&nbsp; Shift+click to select other &nbsp;&nbsp; Double-click to edit &nbsp;&nbsp; Click+drag to move
infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage")); infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage"));
infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9)); infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9));
add(infoMessage, "skip, span, gapleft 25, wrap"); add(infoMessage, "skip, span, gapleft 25, wrap");
addExtras(); addExtras();
} }
public RocketFigure getFigure() { public RocketFigure getFigure() {
return figure; return figure;
} }
public AerodynamicCalculator getAerodynamicCalculator() { public AerodynamicCalculator getAerodynamicCalculator() {
return aerodynamicCalculator; return aerodynamicCalculator;
} }
public Configuration getConfiguration() { public Configuration getConfiguration() {
return configuration; return configuration;
} }
/** /**
* Get the center of pressure figure element. * Get the center of pressure figure element.
* *
@ -361,7 +363,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
public Caret getExtraCP() { public Caret getExtraCP() {
return extraCP; return extraCP;
} }
/** /**
* Get the center of gravity figure element. * Get the center of gravity figure element.
* *
@ -370,7 +372,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
public Caret getExtraCG() { public Caret getExtraCG() {
return extraCG; return extraCG;
} }
/** /**
* Get the extra text figure element. * Get the extra text figure element.
* *
@ -379,7 +381,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
public RocketInfo getExtraText() { public RocketInfo getExtraText() {
return extraText; return extraText;
} }
public void setSelectionModel(TreeSelectionModel m) { public void setSelectionModel(TreeSelectionModel m) {
if (selectionModel != null) { if (selectionModel != null) {
selectionModel.removeTreeSelectionListener(this); selectionModel.removeTreeSelectionListener(this);
@ -388,8 +390,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
selectionModel.addTreeSelectionListener(this); selectionModel.addTreeSelectionListener(this);
valueChanged((TreeSelectionEvent) null); // updates FigureParameters valueChanged((TreeSelectionEvent) null); // updates FigureParameters
} }
/** /**
* Return the angle of attack used in CP calculation. NaN signifies the default value * Return the angle of attack used in CP calculation. NaN signifies the default value
@ -399,7 +401,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
public double getCPAOA() { public double getCPAOA() {
return cpAOA; return cpAOA;
} }
/** /**
* Set the angle of attack to be used in CP calculation. A value of NaN signifies that * Set the angle of attack to be used in CP calculation. A value of NaN signifies that
* the default AOA (zero) should be used. * the default AOA (zero) should be used.
@ -414,11 +416,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
updateFigures(); updateFigures();
fireChangeEvent(); fireChangeEvent();
} }
public double getCPTheta() { public double getCPTheta() {
return cpTheta; return cpTheta;
} }
public void setCPTheta(double theta) { public void setCPTheta(double theta) {
if (MathUtil.equals(theta, cpTheta) || if (MathUtil.equals(theta, cpTheta) ||
(Double.isNaN(theta) && Double.isNaN(cpTheta))) (Double.isNaN(theta) && Double.isNaN(cpTheta)))
@ -430,11 +432,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
updateFigures(); updateFigures();
fireChangeEvent(); fireChangeEvent();
} }
public double getCPMach() { public double getCPMach() {
return cpMach; return cpMach;
} }
public void setCPMach(double mach) { public void setCPMach(double mach) {
if (MathUtil.equals(mach, cpMach) || if (MathUtil.equals(mach, cpMach) ||
(Double.isNaN(mach) && Double.isNaN(cpMach))) (Double.isNaN(mach) && Double.isNaN(cpMach)))
@ -444,11 +446,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
updateFigures(); updateFigures();
fireChangeEvent(); fireChangeEvent();
} }
public double getCPRoll() { public double getCPRoll() {
return cpRoll; return cpRoll;
} }
public void setCPRoll(double roll) { public void setCPRoll(double roll) {
if (MathUtil.equals(roll, cpRoll) || if (MathUtil.equals(roll, cpRoll) ||
(Double.isNaN(roll) && Double.isNaN(cpRoll))) (Double.isNaN(roll) && Double.isNaN(cpRoll)))
@ -458,19 +460,19 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
updateFigures(); updateFigures();
fireChangeEvent(); fireChangeEvent();
} }
@Override @Override
public void addChangeListener(EventListener listener) { public void addChangeListener(EventListener listener) {
listeners.add(0, listener); listeners.add(0, listener);
} }
@Override @Override
public void removeChangeListener(EventListener listener) { public void removeChangeListener(EventListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
protected void fireChangeEvent() { protected void fireChangeEvent() {
EventObject e = new EventObject(this); EventObject e = new EventObject(this);
for (EventListener l : listeners) { for (EventListener l : listeners) {
@ -479,8 +481,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
} }
} }
/** /**
@ -493,7 +495,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
* the next component. Otherwise select the first component in the list. * the next component. Otherwise select the first component in the list.
*/ */
public static final int CYCLE_SELECTION_MODIFIER = InputEvent.SHIFT_DOWN_MASK; public static final int CYCLE_SELECTION_MODIFIER = InputEvent.SHIFT_DOWN_MASK;
private void handleMouseClick(MouseEvent event) { private void handleMouseClick(MouseEvent event) {
if (event.getButton() != MouseEvent.BUTTON1) if (event.getButton() != MouseEvent.BUTTON1)
return; return;
@ -501,18 +503,18 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
Point p1 = scrollPane.getViewport().getViewPosition(); Point p1 = scrollPane.getViewport().getViewPosition();
int x = p0.x + p1.x; int x = p0.x + p1.x;
int y = p0.y + p1.y; int y = p0.y + p1.y;
RocketComponent[] clicked = figure.getComponentsByPoint(x, y); RocketComponent[] clicked = figure.getComponentsByPoint(x, y);
handleComponentClick(clicked, event); handleComponentClick(clicked, event);
} }
private void handleComponentClick(RocketComponent[] clicked, MouseEvent event){ private void handleComponentClick(RocketComponent[] clicked, MouseEvent event){
// If no component is clicked, do nothing // If no component is clicked, do nothing
if (clicked.length == 0) if (clicked.length == 0)
return; return;
// Check whether the currently selected component is in the clicked components. // Check whether the currently selected component is in the clicked components.
TreePath path = selectionModel.getSelectionPath(); TreePath path = selectionModel.getSelectionPath();
if (path != null) { if (path != null) {
@ -529,7 +531,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
} }
} }
// Currently selected component not clicked // Currently selected component not clicked
if (path == null) { if (path == null) {
if (event.isShiftDown() && event.getClickCount() == 1 && clicked.length > 1) { if (event.isShiftDown() && event.getClickCount() == 1 && clicked.length > 1) {
@ -538,18 +540,18 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
path = ComponentTreeModel.makeTreePath(clicked[0]); path = ComponentTreeModel.makeTreePath(clicked[0]);
} }
} }
// Set selection and check for double-click // Set selection and check for double-click
selectionModel.setSelectionPath(path); selectionModel.setSelectionPath(path);
if (event.getClickCount() == 2) { if (event.getClickCount() == 2) {
RocketComponent component = (RocketComponent) path.getLastPathComponent(); RocketComponent component = (RocketComponent) path.getLastPathComponent();
ComponentConfigDialog.showDialog(SwingUtilities.getWindowAncestor(this), ComponentConfigDialog.showDialog(SwingUtilities.getWindowAncestor(this),
document, component); document, component);
} }
} }
/** /**
@ -557,15 +559,15 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
* the CP and CG carets. * the CP and CG carets.
*/ */
private WarningSet warnings = new WarningSet(); private WarningSet warnings = new WarningSet();
private void updateExtras() { private void updateExtras() {
Coordinate cp, cg; Coordinate cp, cg;
double cpx, cgx; double cpx, cgx;
// TODO: MEDIUM: User-definable conditions // TODO: MEDIUM: User-definable conditions
FlightConditions conditions = new FlightConditions(configuration); FlightConditions conditions = new FlightConditions(configuration);
warnings.clear(); warnings.clear();
if (!Double.isNaN(cpMach)) { if (!Double.isNaN(cpMach)) {
conditions.setMach(cpMach); conditions.setMach(cpMach);
extraText.setMach(cpMach); extraText.setMach(cpMach);
@ -573,20 +575,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
conditions.setMach(Application.getPreferences().getDefaultMach()); conditions.setMach(Application.getPreferences().getDefaultMach());
extraText.setMach(Application.getPreferences().getDefaultMach()); extraText.setMach(Application.getPreferences().getDefaultMach());
} }
if (!Double.isNaN(cpAOA)) { if (!Double.isNaN(cpAOA)) {
conditions.setAOA(cpAOA); conditions.setAOA(cpAOA);
} else { } else {
conditions.setAOA(0); conditions.setAOA(0);
} }
extraText.setAOA(cpAOA); extraText.setAOA(cpAOA);
if (!Double.isNaN(cpRoll)) { if (!Double.isNaN(cpRoll)) {
conditions.setRollRate(cpRoll); conditions.setRollRate(cpRoll);
} else { } else {
conditions.setRollRate(0); conditions.setRollRate(0);
} }
if (!Double.isNaN(cpTheta)) { if (!Double.isNaN(cpTheta)) {
conditions.setTheta(cpTheta); conditions.setTheta(cpTheta);
cp = aerodynamicCalculator.getCP(configuration, conditions, warnings); cp = aerodynamicCalculator.getCP(configuration, conditions, warnings);
@ -594,24 +596,24 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
cp = aerodynamicCalculator.getWorstCP(configuration, conditions, warnings); cp = aerodynamicCalculator.getWorstCP(configuration, conditions, warnings);
} }
extraText.setTheta(cpTheta); extraText.setTheta(cpTheta);
cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS); cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS);
// System.out.println("CG computed as "+cg+ " CP as "+cp); // System.out.println("CG computed as "+cg+ " CP as "+cp);
if (cp.weight > 0.000001) if (cp.weight > 0.000001)
cpx = cp.x; cpx = cp.x;
else else
cpx = Double.NaN; cpx = Double.NaN;
if (cg.weight > 0.000001) if (cg.weight > 0.000001)
cgx = cg.x; cgx = cg.x;
else else
cgx = Double.NaN; cgx = Double.NaN;
figure3d.setCG(cg); figure3d.setCG(cg);
figure3d.setCP(cp); figure3d.setCP(cp);
// Length bound is assumed to be tight // Length bound is assumed to be tight
double length = 0, diameter = 0; double length = 0, diameter = 0;
Collection<Coordinate> bounds = configuration.getBounds(); Collection<Coordinate> bounds = configuration.getBounds();
@ -625,7 +627,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
length = maxX - minX; length = maxX - minX;
} }
for (RocketComponent c : configuration) { for (RocketComponent c : configuration) {
if (c instanceof SymmetricComponent) { if (c instanceof SymmetricComponent) {
double d1 = ((SymmetricComponent) c).getForeRadius() * 2; double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
@ -633,31 +635,31 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
diameter = MathUtil.max(diameter, d1, d2); diameter = MathUtil.max(diameter, d1, d2);
} }
} }
extraText.setCG(cgx); extraText.setCG(cgx);
extraText.setCP(cpx); extraText.setCP(cpx);
extraText.setLength(length); extraText.setLength(length);
extraText.setDiameter(diameter); extraText.setDiameter(diameter);
extraText.setMass(cg.weight); extraText.setMass(cg.weight);
extraText.setWarnings(warnings); extraText.setWarnings(warnings);
if (figure.getType() == RocketFigure.TYPE_SIDE && length > 0) { if (figure.getType() == RocketFigure.TYPE_SIDE && length > 0) {
// TODO: LOW: Y-coordinate and rotation // TODO: LOW: Y-coordinate and rotation
extraCP.setPosition(cpx * RocketFigure.EXTRA_SCALE, 0); extraCP.setPosition(cpx * RocketFigure.EXTRA_SCALE, 0);
extraCG.setPosition(cgx * RocketFigure.EXTRA_SCALE, 0); extraCG.setPosition(cgx * RocketFigure.EXTRA_SCALE, 0);
} else { } else {
extraCP.setPosition(Double.NaN, Double.NaN); extraCP.setPosition(Double.NaN, Double.NaN);
extraCG.setPosition(Double.NaN, Double.NaN); extraCG.setPosition(Double.NaN, Double.NaN);
} }
//////// Flight simulation in background //////// Flight simulation in background
// Check whether to compute or not // Check whether to compute or not
if (!((SwingPreferences) Application.getPreferences()).computeFlightInBackground()) { if (!((SwingPreferences) Application.getPreferences()).computeFlightInBackground()) {
extraText.setFlightData(null); extraText.setFlightData(null);
@ -665,38 +667,38 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
stopBackgroundSimulation(); stopBackgroundSimulation();
return; return;
} }
// Check whether data is already up to date // Check whether data is already up to date
if (flightDataFunctionalID == configuration.getRocket().getFunctionalModID() && if (flightDataFunctionalID == configuration.getRocket().getFunctionalModID() &&
flightDataMotorID == configuration.getMotorConfigurationID()) { flightDataMotorID == configuration.getMotorConfigurationID()) {
return; return;
} }
flightDataFunctionalID = configuration.getRocket().getFunctionalModID(); flightDataFunctionalID = configuration.getRocket().getFunctionalModID();
flightDataMotorID = configuration.getMotorConfigurationID(); flightDataMotorID = configuration.getMotorConfigurationID();
// Stop previous computation (if any) // Stop previous computation (if any)
stopBackgroundSimulation(); stopBackgroundSimulation();
// Check that configuration has motors // Check that configuration has motors
if (!configuration.hasMotors()) { if (!configuration.hasMotors()) {
extraText.setFlightData(FlightData.NaN_DATA); extraText.setFlightData(FlightData.NaN_DATA);
extraText.setCalculatingData(false); extraText.setCalculatingData(false);
return; return;
} }
// Start calculation process // Start calculation process
extraText.setCalculatingData(true); extraText.setCalculatingData(true);
Rocket duplicate = (Rocket) configuration.getRocket().copy(); Rocket duplicate = (Rocket) configuration.getRocket().copy();
Simulation simulation = ((SwingPreferences)Application.getPreferences()).getBackgroundSimulation(duplicate); Simulation simulation = ((SwingPreferences)Application.getPreferences()).getBackgroundSimulation(duplicate);
simulation.getOptions().setMotorConfigurationID( simulation.getOptions().setMotorConfigurationID(
configuration.getMotorConfigurationID()); configuration.getMotorConfigurationID());
backgroundSimulationWorker = new BackgroundSimulationWorker(simulation); backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation);
backgroundSimulationExecutor.execute(backgroundSimulationWorker); backgroundSimulationExecutor.execute(backgroundSimulationWorker);
} }
/** /**
* Cancels the current background simulation worker, if any. * Cancels the current background simulation worker, if any.
*/ */
@ -706,22 +708,26 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
backgroundSimulationWorker = null; backgroundSimulationWorker = null;
} }
} }
/** /**
* A SimulationWorker that simulates the rocket flight in the background and * A SimulationWorker that simulates the rocket flight in the background and
* sets the results to the extra text when finished. The worker can be cancelled * sets the results to the extra text when finished. The worker can be cancelled
* if necessary. * if necessary.
*/ */
private class BackgroundSimulationWorker extends SimulationWorker { private class BackgroundSimulationWorker extends SimulationWorker {
public BackgroundSimulationWorker(Simulation sim) { private final CustomExpressionSimulationListener exprListener;
public BackgroundSimulationWorker(OpenRocketDocument doc, Simulation sim) {
super(sim); super(sim);
List<CustomExpression> exprs = doc.getCustomExpressions();
exprListener = new CustomExpressionSimulationListener(exprs);
} }
@Override @Override
protected FlightData doInBackground() { protected FlightData doInBackground() {
// Pause a little while to allow faster UI reaction // Pause a little while to allow faster UI reaction
try { try {
Thread.sleep(300); Thread.sleep(300);
@ -729,36 +735,38 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
if (isCancelled() || backgroundSimulationWorker != this) if (isCancelled() || backgroundSimulationWorker != this)
return null; return null;
return super.doInBackground(); return super.doInBackground();
} }
@Override @Override
protected void simulationDone() { protected void simulationDone() {
// Do nothing if cancelled // Do nothing if cancelled
if (isCancelled() || backgroundSimulationWorker != this) if (isCancelled() || backgroundSimulationWorker != this)
return; return;
backgroundSimulationWorker = null; backgroundSimulationWorker = null;
extraText.setFlightData(simulation.getSimulatedData()); extraText.setFlightData(simulation.getSimulatedData());
extraText.setCalculatingData(false); extraText.setCalculatingData(false);
figure.repaint(); figure.repaint();
figure3d.repaint(); figure3d.repaint();
} }
@Override @Override
protected SimulationListener[] getExtraListeners() { protected SimulationListener[] getExtraListeners() {
return new SimulationListener[] { return new SimulationListener[] {
InterruptListener.INSTANCE, InterruptListener.INSTANCE,
ApogeeEndListener.INSTANCE }; ApogeeEndListener.INSTANCE,
exprListener};
} }
@Override @Override
protected void simulationInterrupted(Throwable t) { protected void simulationInterrupted(Throwable t) {
// Do nothing on cancel, set N/A data otherwise // Do nothing on cancel, set N/A data otherwise
if (isCancelled() || backgroundSimulationWorker != this) // Double-check if (isCancelled() || backgroundSimulationWorker != this) // Double-check
return; return;
backgroundSimulationWorker = null; backgroundSimulationWorker = null;
extraText.setFlightData(FlightData.NaN_DATA); extraText.setFlightData(FlightData.NaN_DATA);
extraText.setCalculatingData(false); extraText.setCalculatingData(false);
@ -766,8 +774,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure3d.repaint(); figure3d.repaint();
} }
} }
/** /**
* Adds the extra data to the figure. Currently this includes the CP and CG carets. * Adds the extra data to the figure. Currently this includes the CP and CG carets.
@ -777,21 +785,21 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
extraCP = new CPCaret(0, 0); extraCP = new CPCaret(0, 0);
extraText = new RocketInfo(configuration); extraText = new RocketInfo(configuration);
updateExtras(); updateExtras();
figure.clearRelativeExtra(); figure.clearRelativeExtra();
figure.addRelativeExtra(extraCP); figure.addRelativeExtra(extraCP);
figure.addRelativeExtra(extraCG); figure.addRelativeExtra(extraCG);
figure.addAbsoluteExtra(extraText); figure.addAbsoluteExtra(extraText);
figure3d.clearRelativeExtra(); figure3d.clearRelativeExtra();
//figure3d.addRelativeExtra(extraCP); //figure3d.addRelativeExtra(extraCP);
//figure3d.addRelativeExtra(extraCG); //figure3d.addRelativeExtra(extraCG);
figure3d.addAbsoluteExtra(extraText); figure3d.addAbsoluteExtra(extraText);
} }
/** /**
* Updates the selection in the FigureParameters and repaints the figure. * Updates the selection in the FigureParameters and repaints the figure.
* Ignores the event itself. * Ignores the event itself.
@ -803,16 +811,16 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure.setSelection(null); figure.setSelection(null);
return; return;
} }
RocketComponent[] components = new RocketComponent[paths.length]; RocketComponent[] components = new RocketComponent[paths.length];
for (int i = 0; i < paths.length; i++) for (int i = 0; i < paths.length; i++)
components[i] = (RocketComponent) paths[i].getLastPathComponent(); components[i] = (RocketComponent) paths[i].getLastPathComponent();
figure.setSelection(components); figure.setSelection(components);
figure3d.setSelection(components); figure3d.setSelection(components);
} }
/** /**
* An <code>Action</code> that shows whether the figure type is the type * An <code>Action</code> that shows whether the figure type is the type
@ -823,13 +831,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
private class FigureTypeAction extends AbstractAction implements StateChangeListener { private class FigureTypeAction extends AbstractAction implements StateChangeListener {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final int type; private final int type;
public FigureTypeAction(int type) { public FigureTypeAction(int type) {
this.type = type; this.type = type;
stateChanged(null); stateChanged(null);
figure.addChangeListener(this); figure.addChangeListener(this);
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
boolean state = (Boolean) getValue(Action.SELECTED_KEY); boolean state = (Boolean) getValue(Action.SELECTED_KEY);
@ -841,11 +849,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
stateChanged(null); stateChanged(null);
} }
@Override @Override
public void stateChanged(EventObject e) { public void stateChanged(EventObject e) {
putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d); putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d);
} }
} }
} }

View File

@ -15,7 +15,6 @@ import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import net.sf.openrocket.arch.SystemInfo; import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
@ -399,7 +398,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
} }
public Simulation getBackgroundSimulation(Rocket rocket) { public Simulation getBackgroundSimulation(Rocket rocket) {
Simulation s = new Simulation(new OpenRocketDocument(rocket), rocket); Simulation s = new Simulation(rocket);
SimulationOptions cond = s.getOptions(); SimulationOptions cond = s.getOptions();
cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2); cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);

View File

@ -126,7 +126,7 @@ public class DefaultSimulationModifierService implements SimulationModifierServi
Rocket rocket = document.getRocket(); Rocket rocket = document.getRocket();
// Simulation is used to calculate default min/max values // Simulation is used to calculate default min/max values
Simulation simulation = new Simulation(document, rocket); Simulation simulation = new Simulation(rocket);
simulation.getConfiguration().setMotorConfigurationID(null); simulation.getConfiguration().setMotorConfigurationID(null);
for (RocketComponent c : rocket) { for (RocketComponent c : rocket) {

View File

@ -90,12 +90,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
} }
SimulationListenerHelper.firePostStep(status); SimulationListenerHelper.firePostStep(status);
// Calculate values for custom expressions
FlightDataBranch data = status.getFlightData();
ArrayList<CustomExpression> allExpressions = status.getSimulationConditions().getSimulation().getDocument().getCustomExpressions();
for (CustomExpression expression : allExpressions ) {
data.setValue(expression.getType(), expression.evaluateDouble(status));
}
// Check for NaN values in the simulation status // Check for NaN values in the simulation status
checkNaN(); checkNaN();

View File

@ -175,7 +175,7 @@ public class CustomExpression implements Cloneable{
names.add(type.getName()); names.add(type.getName());
if (doc != null){ if (doc != null){
ArrayList<CustomExpression> expressions = doc.getCustomExpressions(); List<CustomExpression> expressions = doc.getCustomExpressions();
for (CustomExpression exp : expressions ){ for (CustomExpression exp : expressions ){
if (exp != this) if (exp != this)
names.add(exp.getName()); names.add(exp.getName());
@ -412,7 +412,7 @@ public class CustomExpression implements Cloneable{
*/ */
public void addToDocument(){ public void addToDocument(){
// Abort if exact expression already in // Abort if exact expression already in
ArrayList<CustomExpression> expressions = doc.getCustomExpressions(); List<CustomExpression> expressions = doc.getCustomExpressions();
if ( !expressions.isEmpty() ) { if ( !expressions.isEmpty() ) {
// check if expression already exists // check if expression already exists
if ( expressions.contains(this) ){ if ( expressions.contains(this) ){

View File

@ -0,0 +1,34 @@
package net.sf.openrocket.simulation.customexpression;
import java.util.ArrayList;
import java.util.List;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
public class CustomExpressionSimulationListener extends AbstractSimulationListener {
private final List<CustomExpression> expressions;
public CustomExpressionSimulationListener(List<CustomExpression> expressions) {
super();
this.expressions = expressions;
}
@Override
public void postStep(SimulationStatus status) throws SimulationException {
if ( expressions == null || expressions.size() == 0 ) {
return;
}
// Calculate values for custom expressions
FlightDataBranch data = status.getFlightData();
for (CustomExpression expression : expressions ) {
data.setValue(expression.getType(), expression.evaluateDouble(status));
}
}
}

View File

@ -1,7 +1,6 @@
package net.sf.openrocket.optimization.rocketoptimization; package net.sf.openrocket.optimization.rocketoptimization;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.optimization.general.OptimizationException; import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.optimization.general.Point; import net.sf.openrocket.optimization.general.Point;
@ -40,7 +39,7 @@ public class TestRocketOptimizationFunction {
@Test @Test
public void testNormalEvaluation() throws InterruptedException, OptimizationException { public void testNormalEvaluation() throws InterruptedException, OptimizationException {
final Rocket rocket = new Rocket(); final Rocket rocket = new Rocket();
final Simulation simulation = new Simulation(new OpenRocketDocument(rocket), rocket); final Simulation simulation = new Simulation(rocket);
final double p1 = 0.4; final double p1 = 0.4;
final double p2 = 0.7; final double p2 = 0.7;
@ -86,7 +85,7 @@ public class TestRocketOptimizationFunction {
@Test @Test
public void testNaNValue() throws InterruptedException, OptimizationException { public void testNaNValue() throws InterruptedException, OptimizationException {
final Rocket rocket = new Rocket(); final Rocket rocket = new Rocket();
final Simulation simulation = new Simulation(new OpenRocketDocument(rocket), rocket); final Simulation simulation = new Simulation(rocket);
final double p1 = 0.4; final double p1 = 0.4;
final double p2 = 0.7; final double p2 = 0.7;
@ -123,7 +122,7 @@ public class TestRocketOptimizationFunction {
@Test @Test
public void testOutsideDomain() throws InterruptedException, OptimizationException { public void testOutsideDomain() throws InterruptedException, OptimizationException {
final Rocket rocket = new Rocket(); final Rocket rocket = new Rocket();
final Simulation simulation = new Simulation(new OpenRocketDocument(rocket), rocket); final Simulation simulation = new Simulation(rocket);
final double p1 = 0.4; final double p1 = 0.4;
final double p2 = 0.7; final double p2 = 0.7;
@ -164,7 +163,7 @@ public class TestRocketOptimizationFunction {
@Test @Test
public void testOutsideDomain2() throws InterruptedException, OptimizationException { public void testOutsideDomain2() throws InterruptedException, OptimizationException {
final Rocket rocket = new Rocket(); final Rocket rocket = new Rocket();
final Simulation simulation = new Simulation(new OpenRocketDocument(rocket), rocket); final Simulation simulation = new Simulation(rocket);
final double p1 = 0.4; final double p1 = 0.4;
final double p2 = 0.7; final double p2 = 0.7;
@ -197,7 +196,7 @@ public class TestRocketOptimizationFunction {
public void testNewSimulationInstance() { public void testNewSimulationInstance() {
final Rocket rocket = new Rocket(); final Rocket rocket = new Rocket();
rocket.setName("Foobar"); rocket.setName("Foobar");
final Simulation simulation = new Simulation(new OpenRocketDocument(rocket), rocket); final Simulation simulation = new Simulation(rocket);
simulation.setName("MySim"); simulation.setName("MySim");
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation, RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,

View File

@ -2,7 +2,6 @@ package net.sf.openrocket.optimization.rocketoptimization.modifiers;
import static net.sf.openrocket.util.MathUtil.EPSILON; import static net.sf.openrocket.util.MathUtil.EPSILON;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.optimization.general.OptimizationException; import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Rocket;
@ -21,9 +20,7 @@ public class TestGenericModifier {
@Before @Before
public void setup() { public void setup() {
value = new TestValue(); value = new TestValue();
Rocket rocket = new Rocket(); sim = new Simulation(new Rocket());
sim = new Simulation(new OpenRocketDocument(rocket), rocket);
Object related = new Object(); Object related = new Object();