Include a figure of the ORK in the sim tab
authorBill Kuker <bkuker@billkuker.com>
Wed, 15 Feb 2012 21:24:03 +0000 (21:24 +0000)
committerBill Kuker <bkuker@billkuker.com>
Wed, 15 Feb 2012 21:24:03 +0000 (21:24 +0000)
gpl/com/billkuker/rocketry/motorsim/visual/openRocket/OneMotorDatabase.java
gpl/com/billkuker/rocketry/motorsim/visual/openRocket/RocketPanel.java [new file with mode: 0644]
gpl/com/billkuker/rocketry/motorsim/visual/openRocket/RocketSimTable.java
ivy.xml

index cdd31d67bc875b42e4200c82a4e23debce7545b8..3ec5cd47bdf24c4d5e9f115235ec243b31e40095 100644 (file)
@@ -59,12 +59,16 @@ public class OneMotorDatabase implements MotorDatabase {
 \r
                @Override\r
                public double getDiameter() {\r
+                       if ( burn == null )\r
+                               return 0;\r
                        return ((ICylindricalChamber) burn.getMotor().getChamber()).getOD()\r
                                        .doubleValue(SI.METER);\r
                }\r
 \r
                @Override\r
                public double getLength() {\r
+                       if ( burn == null )\r
+                               return 0;\r
                        return ((ICylindricalChamber) burn.getMotor().getChamber())\r
                                        .getLength().doubleValue(SI.METER);\r
                }\r
diff --git a/gpl/com/billkuker/rocketry/motorsim/visual/openRocket/RocketPanel.java b/gpl/com/billkuker/rocketry/motorsim/visual/openRocket/RocketPanel.java
new file mode 100644 (file)
index 0000000..6079c2f
--- /dev/null
@@ -0,0 +1,620 @@
+package com.billkuker.rocketry.motorsim.visual.openRocket;
+
+import java.awt.Dimension;
+import java.awt.event.InputEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.JViewport;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
+import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
+import net.sf.openrocket.aerodynamics.FlightConditions;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.Simulation;
+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.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.scalefigure.RocketFigure;
+import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
+import net.sf.openrocket.gui.util.SwingPreferences;
+import net.sf.openrocket.masscalc.BasicMassCalculator;
+import net.sf.openrocket.masscalc.MassCalculator;
+import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
+import net.sf.openrocket.rocketcomponent.Configuration;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.SymmetricComponent;
+import net.sf.openrocket.simulation.FlightData;
+import net.sf.openrocket.simulation.listeners.SimulationListener;
+import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
+import net.sf.openrocket.simulation.listeners.system.InterruptListener;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.Chars;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.StateChangeListener;
+
+/**
+ * A JPanel that contains a RocketFigure and buttons to manipulate the figure.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class RocketPanel extends JPanel implements TreeSelectionListener,
+               ChangeSource {
+
+       private static final long serialVersionUID = 1L;
+
+       private final RocketFigure figure;
+       private final ScaleScrollPane scrollPane;
+
+       private TreeSelectionModel selectionModel = null;
+
+       /* Calculation of CP and CG */
+       private AerodynamicCalculator aerodynamicCalculator;
+       private MassCalculator massCalculator;
+
+       private final Configuration configuration;
+
+       private Caret extraCP = null;
+       private Caret extraCG = null;
+       private RocketInfo extraText = null;
+
+       private double cpAOA = Double.NaN;
+       private double cpTheta = Double.NaN;
+       private double cpMach = Double.NaN;
+       private double cpRoll = Double.NaN;
+
+       // The functional ID of the rocket that was simulated
+       private int flightDataFunctionalID = -1;
+       private String flightDataMotorID = null;
+
+       private SimulationWorker backgroundSimulationWorker = null;
+
+       private List<EventListener> listeners = new ArrayList<EventListener>();
+
+       /**
+        * The executor service used for running the background simulations. This
+        * uses a fixed-sized thread pool for all background simulations with all
+        * threads in daemon mode and with minimum priority.
+        */
+       private static final Executor backgroundSimulationExecutor;
+       static {
+               backgroundSimulationExecutor = Executors.newFixedThreadPool(
+                               SwingPreferences.getMaxThreadCount(), new ThreadFactory() {
+                                       private ThreadFactory factory = Executors
+                                                       .defaultThreadFactory();
+
+                                       @Override
+                                       public Thread newThread(Runnable r) {
+                                               Thread t = factory.newThread(r);
+                                               t.setDaemon(true);
+                                               t.setPriority(Thread.MIN_PRIORITY);
+                                               return t;
+                                       }
+                               });
+       }
+
+       public RocketPanel(OpenRocketDocument document) {
+
+               configuration = document.getDefaultConfiguration();
+
+               // TODO: FUTURE: calculator selection
+               aerodynamicCalculator = new BarrowmanCalculator();
+               massCalculator = new BasicMassCalculator();
+
+               // Create figure and custom scroll pane
+               figure = new RocketFigure(configuration);
+
+               scrollPane = new ScaleScrollPane(figure);
+
+               scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
+               scrollPane.setFitting(true);
+
+               createPanel();
+
+               configuration.addChangeListener(new StateChangeListener() {
+                       @Override
+                       public void stateChanged(EventObject e) {
+                               // System.out.println("Configuration changed, calling updateFigure");
+                               updateExtras();
+                               figure.updateFigure();
+                       }
+               });
+       }
+
+       /**
+        * Creates the layout and components of the panel.
+        */
+       private void createPanel() {
+               setLayout(new MigLayout("", "[shrink][grow]", "[shrink][grow]"));
+
+               setPreferredSize(new Dimension(800, 250));
+
+               // // Create toolbar
+
+               /*
+                * // Side/back buttons FigureTypeAction action = new
+                * FigureTypeAction(RocketFigure.TYPE_SIDE); // // Side view
+                * action.putValue(Action.NAME,
+                * trans.get("RocketPanel.FigTypeAct.Sideview")); // // Side view
+                * action.putValue(Action.SHORT_DESCRIPTION,
+                * trans.get("RocketPanel.FigTypeAct.ttip.Sideview")); JToggleButton
+                * toggle = new JToggleButton(action); add(toggle, "spanx, split");
+                * 
+                * action = new FigureTypeAction(RocketFigure.TYPE_BACK); // // Back
+                * view action.putValue(Action.NAME,
+                * trans.get("RocketPanel.FigTypeAct.Backview")); // // Back view
+                * action.putValue(Action.SHORT_DESCRIPTION,
+                * trans.get("RocketPanel.FigTypeAct.ttip.Backview")); toggle = new
+                * JToggleButton(action); add(toggle, "gap rel");
+                */
+               /*
+                * // Zoom level selector ScaleSelector scaleSelector = new
+                * ScaleSelector(scrollPane); add(scaleSelector);
+                */
+               // add(new JLabel());
+
+               /*
+                * // Stage selector StageSelector stageSelector = new
+                * StageSelector(configuration); add(stageSelector, "");
+                * 
+                * add(new JLabel(), "wrap");
+                */
+               /*
+                * 
+                * // Motor configuration selector //// Motor configuration: JLabel
+                * label = new JLabel(trans.get("RocketPanel.lbl.Motorcfg"));
+                * label.setHorizontalAlignment(JLabel.RIGHT); add(label,
+                * "growx, right"); add(new JComboBox(new
+                * MotorConfigurationModel(configuration)), "wrap");
+                */
+               configuration.setMotorConfigurationID(null);
+
+               // add(new JLabel(), "growx, right");
+               // add(new JLabel(), "wrap");
+
+               // Create slider and scroll pane
+
+               DoubleModel theta = new DoubleModel(figure, "Rotation",
+                               UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
+               UnitSelector us = new UnitSelector(theta, true);
+               us.setHorizontalAlignment(JLabel.CENTER);
+               add(us, "alignx 50%, growx");
+
+               // Add the rocket figure
+               add(scrollPane, "grow, spany 2, wmin 300lp, hmin 100lp, wrap");
+
+               // Add rotation slider
+               // Minimum size to fit "360deg"
+               JLabel l = new JLabel("360" + Chars.DEGREE);
+               Dimension d = l.getPreferredSize();
+
+               add(new BasicSlider(theta.getSliderModel(0, 2 * Math.PI),
+                               JSlider.VERTICAL, true), "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
+                * infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage"));
+                * infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9));
+                * add(infoMessage, "skip, span, gapleft 25, wrap");
+                */
+               addExtras();
+       }
+
+       public RocketFigure getFigure() {
+               return figure;
+       }
+
+       public AerodynamicCalculator getAerodynamicCalculator() {
+               return aerodynamicCalculator;
+       }
+
+       public Configuration getConfiguration() {
+               return configuration;
+       }
+
+       /**
+        * Get the center of pressure figure element.
+        * 
+        * @return center of pressure info
+        */
+       public Caret getExtraCP() {
+               return extraCP;
+       }
+
+       /**
+        * Get the center of gravity figure element.
+        * 
+        * @return center of gravity info
+        */
+       public Caret getExtraCG() {
+               return extraCG;
+       }
+
+       /**
+        * Get the extra text figure element.
+        * 
+        * @return extra text that contains info about the rocket design
+        */
+       public RocketInfo getExtraText() {
+               return extraText;
+       }
+
+       public void setSelectionModel(TreeSelectionModel m) {
+               if (selectionModel != null) {
+                       selectionModel.removeTreeSelectionListener(this);
+               }
+               selectionModel = m;
+               selectionModel.addTreeSelectionListener(this);
+               valueChanged((TreeSelectionEvent) null); // updates FigureParameters
+       }
+
+       /**
+        * Return the angle of attack used in CP calculation. NaN signifies the
+        * default value of zero.
+        * 
+        * @return the angle of attack used, or NaN.
+        */
+       public double getCPAOA() {
+               return cpAOA;
+       }
+
+       /**
+        * Set the angle of attack to be used in CP calculation. A value of NaN
+        * signifies that the default AOA (zero) should be used.
+        * 
+        * @param aoa
+        *            the angle of attack to use, or NaN
+        */
+       public void setCPAOA(double aoa) {
+               if (MathUtil.equals(aoa, cpAOA)
+                               || (Double.isNaN(aoa) && Double.isNaN(cpAOA)))
+                       return;
+               cpAOA = aoa;
+               updateExtras();
+               figure.updateFigure();
+               fireChangeEvent();
+       }
+
+       public double getCPTheta() {
+               return cpTheta;
+       }
+
+       public void setCPTheta(double theta) {
+               if (MathUtil.equals(theta, cpTheta)
+                               || (Double.isNaN(theta) && Double.isNaN(cpTheta)))
+                       return;
+               cpTheta = theta;
+               if (!Double.isNaN(theta))
+                       figure.setRotation(theta);
+               updateExtras();
+               figure.updateFigure();
+               fireChangeEvent();
+       }
+
+       public double getCPMach() {
+               return cpMach;
+       }
+
+       public void setCPMach(double mach) {
+               if (MathUtil.equals(mach, cpMach)
+                               || (Double.isNaN(mach) && Double.isNaN(cpMach)))
+                       return;
+               cpMach = mach;
+               updateExtras();
+               figure.updateFigure();
+               fireChangeEvent();
+       }
+
+       public double getCPRoll() {
+               return cpRoll;
+       }
+
+       public void setCPRoll(double roll) {
+               if (MathUtil.equals(roll, cpRoll)
+                               || (Double.isNaN(roll) && Double.isNaN(cpRoll)))
+                       return;
+               cpRoll = roll;
+               updateExtras();
+               figure.updateFigure();
+               fireChangeEvent();
+       }
+
+       @Override
+       public void addChangeListener(EventListener listener) {
+               listeners.add(0, listener);
+       }
+
+       @Override
+       public void removeChangeListener(EventListener listener) {
+               listeners.remove(listener);
+       }
+
+       protected void fireChangeEvent() {
+               EventObject e = new EventObject(this);
+               for (EventListener l : listeners) {
+                       if (l instanceof StateChangeListener) {
+                               ((StateChangeListener) l).stateChanged(e);
+                       }
+               }
+       }
+
+       /**
+        * Handle clicking on figure shapes. The functioning is the following:
+        * 
+        * Get the components clicked. If no component is clicked, do nothing. If
+        * the currently selected component is in the set, keep it, unless the
+        * selector specified is pressed. If it is pressed, cycle to the next
+        * component. Otherwise select the first component in the list.
+        */
+       public static final int CYCLE_SELECTION_MODIFIER = InputEvent.SHIFT_DOWN_MASK;
+
+       /**
+        * Updates the extra data included in the figure. Currently this includes
+        * the CP and CG carets.
+        */
+       private WarningSet warnings = new WarningSet();
+
+       private void updateExtras() {
+               Coordinate cp, cg;
+               double cpx, cgx;
+
+               // TODO: MEDIUM: User-definable conditions
+               FlightConditions conditions = new FlightConditions(configuration);
+               warnings.clear();
+
+               if (!Double.isNaN(cpMach)) {
+                       conditions.setMach(cpMach);
+                       extraText.setMach(cpMach);
+               } else {
+                       conditions.setMach(Application.getPreferences().getDefaultMach());
+                       extraText.setMach(Application.getPreferences().getDefaultMach());
+               }
+
+               if (!Double.isNaN(cpAOA)) {
+                       conditions.setAOA(cpAOA);
+               } else {
+                       conditions.setAOA(0);
+               }
+               extraText.setAOA(cpAOA);
+
+               if (!Double.isNaN(cpRoll)) {
+                       conditions.setRollRate(cpRoll);
+               } else {
+                       conditions.setRollRate(0);
+               }
+
+               if (!Double.isNaN(cpTheta)) {
+                       conditions.setTheta(cpTheta);
+                       cp = aerodynamicCalculator.getCP(configuration, conditions,
+                                       warnings);
+               } else {
+                       cp = aerodynamicCalculator.getWorstCP(configuration, conditions,
+                                       warnings);
+               }
+               extraText.setTheta(cpTheta);
+
+               cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS);
+               // System.out.println("CG computed as "+cg+ " CP as "+cp);
+
+               if (cp.weight > 0.000001)
+                       cpx = cp.x;
+               else
+                       cpx = Double.NaN;
+
+               if (cg.weight > 0.000001)
+                       cgx = cg.x;
+               else
+                       cgx = Double.NaN;
+
+               // Length bound is assumed to be tight
+               double length = 0, diameter = 0;
+               Collection<Coordinate> bounds = configuration.getBounds();
+               if (!bounds.isEmpty()) {
+                       double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
+                       for (Coordinate c : bounds) {
+                               if (c.x < minX)
+                                       minX = c.x;
+                               if (c.x > maxX)
+                                       maxX = c.x;
+                       }
+                       length = maxX - minX;
+               }
+
+               for (RocketComponent c : configuration) {
+                       if (c instanceof SymmetricComponent) {
+                               double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
+                               double d2 = ((SymmetricComponent) c).getAftRadius() * 2;
+                               diameter = MathUtil.max(diameter, d1, d2);
+                       }
+               }
+
+               extraText.setCG(cgx);
+               extraText.setCP(cpx);
+               extraText.setLength(length);
+               extraText.setDiameter(diameter);
+               extraText.setMass(cg.weight);
+               extraText.setWarnings(warnings);
+
+               if (figure.getType() == RocketFigure.TYPE_SIDE && length > 0) {
+
+                       // TODO: LOW: Y-coordinate and rotation
+                       extraCP.setPosition(cpx * RocketFigure.EXTRA_SCALE, 0);
+                       extraCG.setPosition(cgx * RocketFigure.EXTRA_SCALE, 0);
+
+               } else {
+
+                       extraCP.setPosition(Double.NaN, Double.NaN);
+                       extraCG.setPosition(Double.NaN, Double.NaN);
+
+               }
+
+               // ////// Flight simulation in background
+
+               // Check whether to compute or not
+               if (!((SwingPreferences) Application.getPreferences())
+                               .computeFlightInBackground()) {
+                       extraText.setFlightData(null);
+                       extraText.setCalculatingData(false);
+                       stopBackgroundSimulation();
+                       return;
+               }
+
+               // Check whether data is already up to date
+               if (flightDataFunctionalID == configuration.getRocket()
+                               .getFunctionalModID()
+                               && flightDataMotorID == configuration.getMotorConfigurationID()) {
+                       return;
+               }
+
+               flightDataFunctionalID = configuration.getRocket().getFunctionalModID();
+               flightDataMotorID = configuration.getMotorConfigurationID();
+
+               // Stop previous computation (if any)
+               stopBackgroundSimulation();
+
+               // Check that configuration has motors
+               if (!configuration.hasMotors()) {
+                       extraText.setFlightData(FlightData.NaN_DATA);
+                       extraText.setCalculatingData(false);
+                       return;
+               }
+
+               // Start calculation process
+               extraText.setCalculatingData(true);
+
+               Rocket duplicate = (Rocket) configuration.getRocket().copy();
+               Simulation simulation = ((SwingPreferences) Application
+                               .getPreferences()).getBackgroundSimulation(duplicate);
+               simulation.getOptions().setMotorConfigurationID(
+                               configuration.getMotorConfigurationID());
+
+               backgroundSimulationWorker = new BackgroundSimulationWorker(simulation);
+               backgroundSimulationExecutor.execute(backgroundSimulationWorker);
+       }
+
+       /**
+        * Cancels the current background simulation worker, if any.
+        */
+       private void stopBackgroundSimulation() {
+               if (backgroundSimulationWorker != null) {
+                       backgroundSimulationWorker.cancel(true);
+                       backgroundSimulationWorker = null;
+               }
+       }
+
+       /**
+        * 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 if necessary.
+        */
+       private class BackgroundSimulationWorker extends SimulationWorker {
+
+               public BackgroundSimulationWorker(Simulation sim) {
+                       super(sim);
+               }
+
+               @Override
+               protected FlightData doInBackground() {
+
+                       // Pause a little while to allow faster UI reaction
+                       try {
+                               Thread.sleep(300);
+                       } catch (InterruptedException ignore) {
+                       }
+                       if (isCancelled() || backgroundSimulationWorker != this)
+                               return null;
+
+                       return super.doInBackground();
+               }
+
+               @Override
+               protected void simulationDone() {
+                       // Do nothing if cancelled
+                       if (isCancelled() || backgroundSimulationWorker != this)
+                               return;
+
+                       backgroundSimulationWorker = null;
+                       extraText.setFlightData(simulation.getSimulatedData());
+                       extraText.setCalculatingData(false);
+                       figure.repaint();
+               }
+
+               @Override
+               protected SimulationListener[] getExtraListeners() {
+                       return new SimulationListener[] { InterruptListener.INSTANCE,
+                                       ApogeeEndListener.INSTANCE };
+               }
+
+               @Override
+               protected void simulationInterrupted(Throwable t) {
+                       // Do nothing on cancel, set N/A data otherwise
+                       if (isCancelled() || backgroundSimulationWorker != this) // Double-check
+                               return;
+
+                       backgroundSimulationWorker = null;
+                       extraText.setFlightData(FlightData.NaN_DATA);
+                       extraText.setCalculatingData(false);
+                       figure.repaint();
+               }
+       }
+
+       /**
+        * Adds the extra data to the figure. Currently this includes the CP and CG
+        * carets.
+        */
+       private void addExtras() {
+               figure.clearRelativeExtra();
+               extraCG = new CGCaret(0, 0);
+               extraCP = new CPCaret(0, 0);
+               extraText = new RocketInfo(configuration);
+               updateExtras();
+               figure.addRelativeExtra(extraCP);
+               figure.addRelativeExtra(extraCG);
+               figure.addAbsoluteExtra(extraText);
+       }
+
+       /**
+        * Updates the selection in the FigureParameters and repaints the figure.
+        * Ignores the event itself.
+        */
+       @Override
+       public void valueChanged(TreeSelectionEvent e) {
+               TreePath[] paths = selectionModel.getSelectionPaths();
+               if (paths == null) {
+                       figure.setSelection(null);
+                       return;
+               }
+
+               RocketComponent[] components = new RocketComponent[paths.length];
+               for (int i = 0; i < paths.length; i++)
+                       components[i] = (RocketComponent) paths[i].getLastPathComponent();
+               figure.setSelection(components);
+       }
+
+}
index 445991c75458459849be8b52bfe5be0d5f451433..2e7023affb56ccf6825ef849ffe37a4ee14cdbf0 100644 (file)
@@ -13,7 +13,6 @@ import java.util.Set;
 import java.util.Vector;\r
 \r
 import javax.measure.unit.SI;\r
-import javax.swing.JLabel;\r
 import javax.swing.JPanel;\r
 import javax.swing.JScrollPane;\r
 import javax.swing.JTable;\r
@@ -70,7 +69,6 @@ public class RocketSimTable extends JPanel implements BurnWatcher,
                Burn b;\r
        }\r
 \r
-\r
        class TM extends AbstractTableModel {\r
                private static final long serialVersionUID = 1L;\r
 \r
@@ -199,13 +197,16 @@ public class RocketSimTable extends JPanel implements BurnWatcher,
 \r
        public void openRocket(File f) throws RocketLoadException {\r
                this.doc = new OpenRocketLoader().load(f);\r
-               JPanel rocketInfo = new JPanel();\r
-               JLabel name = new JLabel("File: " + f.getAbsolutePath());\r
-               rocketInfo.add(name);\r
-               add(rocketInfo, BorderLayout.NORTH);\r
-               \r
+               // JPanel rocketInfo = new JPanel();\r
+               // JLabel name = new JLabel("File: " + f.getAbsolutePath());\r
+               // rocketInfo.add(name);\r
+               // add(rocketInfo, BorderLayout.NORTH);\r
+\r
+               RocketPanel p = new RocketPanel(this.doc);\r
+               add(p, BorderLayout.NORTH);\r
+\r
                entries = new Vector<Entry>();\r
-               for ( Burn b : burns ){\r
+               for (Burn b : burns) {\r
                        entries.add(toEntry(b));\r
                }\r
                tm.fireTableDataChanged();\r
@@ -255,7 +256,7 @@ public class RocketSimTable extends JPanel implements BurnWatcher,
        public void replace(Burn oldBurn, Burn newBurn) {\r
                burns.add(newBurn);\r
                burns.remove(oldBurn);\r
-               \r
+\r
                if (doc == null) {\r
                        return; // TODO, deal with changing rockets\r
                }\r
diff --git a/ivy.xml b/ivy.xml
index c4b3005d612b920fe293825202b04cd0f5570a55..d39f400b986e96ae1ec3d56748d2ab6a12a3172c 100644 (file)
--- a/ivy.xml
+++ b/ivy.xml
@@ -9,9 +9,6 @@
 \r
                <dependency org="log4j" name="log4j" rev="1.2.16" conf="core->default" />\r
 \r
-\r
-               <dependency org="junit" name="junit" rev="4.10" conf="test->default" />\r
-\r
                <dependency org="xpp3" name="xpp3_min" rev="1.1.4c" conf="core->default" />\r
 \r
                <dependency org="com.thoughtworks.xstream" name="xstream"\r
                <dependency org="jfree" name="jfreechart" rev="1.0.13"\r
                        conf="gui->default" />\r
 \r
+               <dependency org="com.miglayout" name="miglayout" rev="3.7.4"\r
+                       conf="gui->default" />\r
+\r
+               <dependency org="junit" name="junit" rev="4.10" conf="test->default" />\r
 \r
        </dependencies>\r
 </ivy-module>\r