diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index 7d81f82eb..563738509 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -294,6 +294,8 @@ pref.dlg.lbl.PositiontoinsertStages = Position to insert new stages:
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.checkbox.Markers = Only show pod set/booster markers when the pod set/booster is selected
+pref.dlg.checkbox.Markers.ttip = If checked, pod set/booster markers will only be shown when the pod set/booster is selected.
If unchecked, pod set/booster markers will always be shown.
pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a component edit dialog
pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will always pop up with the first tab selected.
If unchecked, the previous selected tab will be used.
pref.dlg.lbl.User-definedthrust = User-defined thrust curves:
diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java
index 5411440f7..90fa5a5a7 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java
@@ -304,6 +304,17 @@ public class Rocket extends ComponentAssembly {
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
}
+
+ @Override
+ public double getBoundingRadius() {
+ double bounding = 0;
+ for (RocketComponent comp : children) {
+ if (comp instanceof ComponentAssembly) {
+ bounding = Math.max(bounding, ((ComponentAssembly) comp).getBoundingRadius());
+ }
+ }
+ return bounding;
+ }
diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java
index 64f1de5a0..3d750c651 100644
--- a/core/src/net/sf/openrocket/startup/Preferences.java
+++ b/core/src/net/sf/openrocket/startup/Preferences.java
@@ -68,6 +68,7 @@ public abstract class Preferences implements ChangeSource {
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN";
private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB";
+ private static final String SHOW_MARKERS = "SHOW_MARKERS";
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
//Preferences related to 3D graphics
@@ -469,7 +470,26 @@ public abstract class Preferences implements ChangeSource {
public final boolean isAlwaysOpenLeftmostTab() {
return this.getBoolean(OPEN_LEFTMOST_DESIGN_TAB, false);
}
-
+
+ /**
+ * Set whether pod set/booster markers should only be displayed when the pod set/booster is selected.
+ * @param enabled true if pod set/booster markers should only be displayed when the pod set/booster is selected,
+ * false if they should be displayed permanently.
+ */
+ public final void setShowMarkers(boolean enabled) {
+ this.putBoolean(SHOW_MARKERS, enabled);
+ }
+
+ /**
+ * Answer if pod set/booster markers should only be displayed when the pod set/booster is selected
+ *
+ * @return true if pod set/booster markers should only be displayed when the pod set/booster is selected,
+ * false if they should be displayed permanently.
+ */
+ public final boolean isShowMarkers() {
+ return this.getBoolean(SHOW_MARKERS, false);
+ }
+
/**
* Return the OpenRocket unique ID.
*
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
index 08ef4acb1..3a98bd616 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
@@ -11,6 +11,7 @@ import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.unit.UnitGroup;
@@ -93,7 +94,6 @@ public class DesignPreferencesPanel extends PreferencesPanel {
// // Always open leftmost tab when opening a component edit dialog
final JCheckBox alwaysOpenLeftmostTab = new JCheckBox(
trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost"));
-
alwaysOpenLeftmostTab.setSelected(preferences.isAlwaysOpenLeftmostTab());
alwaysOpenLeftmostTab.setToolTipText(trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost.ttip"));
alwaysOpenLeftmostTab.addActionListener(new ActionListener() {
@@ -103,7 +103,7 @@ public class DesignPreferencesPanel extends PreferencesPanel {
.isSelected());
}
});
- this.add(alwaysOpenLeftmostTab, "wrap, growx, span 2");
+ this.add(alwaysOpenLeftmostTab, "wrap, growx, spanx");
// // Update flight estimates in the design window
final JCheckBox updateEstimates = new JCheckBox(
@@ -117,5 +117,23 @@ public class DesignPreferencesPanel extends PreferencesPanel {
}
});
this.add(updateEstimates, "wrap, growx, sg combos ");
+
+ // // Only show pod set/booster markers when they are selected
+ final JCheckBox showMarkers = new JCheckBox(
+ trans.get("pref.dlg.checkbox.Markers"));
+ showMarkers.setToolTipText(trans.get("pref.dlg.checkbox.Markers.ttip"));
+ showMarkers.setSelected(preferences.isShowMarkers());
+ showMarkers.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ preferences.setShowMarkers(showMarkers
+ .isSelected());
+ // Update all BasicFrame rocket panel figures because it can change due to the preference change
+ for (BasicFrame frame : BasicFrame.getAllFrames()) {
+ frame.getRocketPanel().updateFigures();
+ }
+ }
+ });
+ this.add(showMarkers, "wrap, growx, spanx");
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
index 04fdf9bf3..6f76a838d 100644
--- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -117,7 +117,7 @@ public class BasicFrame extends JFrame {
* List of currently open frames. When the list goes empty
* it is time to exit the application.
*/
- private static final ArrayList frames = new ArrayList();
+ private static final List frames = new ArrayList();
private static BasicFrame startupFrame = null; // the frame that was created at startup
@@ -490,6 +490,10 @@ public class BasicFrame extends JFrame {
return result;
}
+ public RocketPanel getRocketPanel() {
+ return rocketpanel;
+ }
+
/**
* Creates the menu for the window.
*/
@@ -1890,6 +1894,13 @@ public class BasicFrame extends JFrame {
return null;
}
+ /**
+ * Return all BasicFrame instances
+ */
+ public static List getAllFrames() {
+ return frames;
+ }
+
/**
* Checks whether all the BasicFrames are closed.
* @return true if all the BasicFrames are closed, false if not
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java
new file mode 100644
index 000000000..35b048551
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java
@@ -0,0 +1,122 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Shapes of an "empty"/virtual object, e.g. a podset without any children.
+ * The shape is a center square with additional lines on the north, east, south and west side of the square.
+ *
+ * @author Sibo Van Gool
+ */
+public class EmptyShapes extends RocketComponentShape {
+ /**
+ * Returns the empty shape in the side view.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesSide(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double x = instanceAbsoluteLocation.x;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ final Shape[] s = new Shape[5];
+ // Center square
+ s[0] = new Rectangle2D.Double(x - radius, y - radius, 2 * radius, 2 * radius);
+ // Line North
+ s[1] = new Line2D.Double(x, y + radius, x, y + radius + lineLength);
+ // Line East
+ s[2] = new Line2D.Double(x + radius, y, x + radius + lineLength, y);
+ // Line South
+ s[3] = new Line2D.Double(x, y - radius, x, y - radius - lineLength);
+ // Line West
+ s[4] = new Line2D.Double(x - radius, y, x - radius - lineLength, y);
+
+ return s;
+ }
+
+ /**
+ * Returns the empty shape in the side view, with an additional square encompassing the shape that can be used
+ * for selecting the object.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesSideWithSelectionSquare(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double x = instanceAbsoluteLocation.x;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ Shape[] shapes = getShapesSide(transformation, radius);
+
+ // Invisible shape for selecting the component (= a square encompassing the component)
+ Shape selectionShape = new Rectangle2D.Double(x - radius - lineLength, y - radius - lineLength,
+ lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
+
+ Shape[] finalShapes = new Shape[shapes.length + 1];
+ System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
+ finalShapes[finalShapes.length - 1] = selectionShape;
+
+ return finalShapes;
+ }
+
+ /**
+ * Returns the empty shape in the side view.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesBack(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double z = instanceAbsoluteLocation.z;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ final Shape[] s = new Shape[5];
+ // Center square
+ s[0] = new Rectangle2D.Double(z - radius, y - radius, 2 * radius, 2 * radius);
+ // Line North
+ s[1] = new Line2D.Double(z, y + radius, z, y + radius + lineLength);
+ // Line East
+ s[2] = new Line2D.Double(z + radius, y, z + radius + lineLength, y);
+ // Line South
+ s[3] = new Line2D.Double(z, y - radius, z, y - radius - lineLength);
+ // Line West
+ s[4] = new Line2D.Double(z - radius, y, z - radius - lineLength, y);
+
+ return s;
+ }
+
+ /**
+ * Returns the empty shape in the back view, with an additional square encompassing the shape that can be used
+ * for selecting the object.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesBackWithSelectionSquare(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double z = instanceAbsoluteLocation.z;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ Shape[] shapes = getShapesBack(transformation, radius);
+
+ // Invisible shape for selecting the component (= a square encompassing the component)
+ Shape selectionShape = new Rectangle2D.Double(z - radius - lineLength, y - radius - lineLength,
+ lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
+
+ Shape[] finalShapes = new Shape[shapes.length + 1];
+ System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
+ finalShapes[finalShapes.length - 1] = selectionShape;
+
+ return finalShapes;
+ }
+
+ private static double getLineLength(double radius) {
+ return radius * 3;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java
new file mode 100644
index 000000000..565a23591
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java
@@ -0,0 +1,48 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.rocketcomponent.ParallelStage;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.Color;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+
+public class ParallelStageShapes extends RocketComponentShape {
+ public static final Color boosterColor = new Color(198,163,184);
+
+ public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) {
+ ParallelStage booster = (ParallelStage)component;
+ double radius = getDisplayRadius(booster);
+
+ Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(boosterColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) {
+ ParallelStage booster = (ParallelStage)component;
+ double radius = getDisplayRadius(booster);
+
+ Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(boosterColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ private static double getDisplayRadius(ParallelStage booster) {
+ return booster.getRocket().getBoundingRadius() * 0.03;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java
new file mode 100644
index 000000000..7ef928f2d
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java
@@ -0,0 +1,48 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.rocketcomponent.PodSet;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.Color;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+
+public class PodSetShapes extends RocketComponentShape {
+ public static final Color podsetColor = new Color(160,160,215);
+
+ public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) {
+ PodSet podset = (PodSet)component;
+ double radius = getDisplayRadius(podset);
+
+ Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(podsetColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) {
+ PodSet podset = (PodSet)component;
+ double radius = getDisplayRadius(podset);
+
+ Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(podsetColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ private static double getDisplayRadius(PodSet podset) {
+ return podset.getRocket().getBoundingRadius() * 0.03;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
index 725713b80..c132bfd3d 100644
--- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
@@ -64,6 +64,10 @@ public class RocketComponentShape {
return new RocketComponentShape[0];
}
+ public Color getColor() {
+ return color;
+ }
+
public void setColor(Color color) {
this.color = color;
}
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
index 3226abee2..6e4bf9e4b 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
@@ -17,6 +17,9 @@ import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.Map.Entry;
+import net.sf.openrocket.rocketcomponent.AxialStage;
+import net.sf.openrocket.rocketcomponent.ParallelStage;
+import net.sf.openrocket.rocketcomponent.PodSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +29,6 @@ import net.sf.openrocket.gui.util.ColorConversion;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
-import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.InstanceContext;
import net.sf.openrocket.rocketcomponent.MotorMount;
@@ -52,6 +54,7 @@ import net.sf.openrocket.util.Transformation;
public class RocketFigure extends AbstractScaleFigure {
private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class);
+ protected final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure";
private static final String ROCKET_FIGURE_SUFFIX = "Shapes";
@@ -378,6 +381,20 @@ public class RocketFigure extends AbstractScaleFigure {
for(Entry> entry: config.getActiveInstances().entrySet() ) {
final RocketComponent comp = entry.getKey();
+
+ // Only draw podsets when they are selected
+ if ((comp instanceof PodSet || comp instanceof ParallelStage) && preferences.isShowMarkers()) {
+ boolean selected = false;
+
+ // Check if component is in the selection
+ for (RocketComponent component : selection) {
+ if (comp == component) {
+ selected = true;
+ break;
+ }
+ }
+ if (!selected) continue;
+ }
final ArrayList contextList = entry.getValue();
@@ -390,8 +407,12 @@ public class RocketFigure extends AbstractScaleFigure {
/**
* Gets the shapes required to draw the component.
- *
- * @param component
+ *
+ * @param allShapes output buffer for the shapes to add to
+ * @param viewType the view type to draw the component in
+ * @param component component to draw and add to
+ * @param transformation transformation to apply to the component before drawing it
+ * @param color color to draw the component in
*
* @return the ArrayList
containing all the shapes to draw.
*/
@@ -399,10 +420,11 @@ public class RocketFigure extends AbstractScaleFigure {
PriorityQueue allShapes, // this is the output parameter
final RocketPanel.VIEW_TYPE viewType,
final RocketComponent component,
- final Transformation transformation) {
+ final Transformation transformation,
+ final net.sf.openrocket.util.Color color) {
Reflection.Method m;
- if(( component instanceof Rocket)||( component instanceof ComponentAssembly )){
+ if ((component instanceof Rocket) || (component instanceof AxialStage && !(component instanceof ParallelStage))){
// no-op; no shapes here
return allShapes;
}
@@ -431,9 +453,35 @@ public class RocketFigure extends AbstractScaleFigure {
RocketComponentShape[] returnValue = (RocketComponentShape[]) m.invokeStatic(component, transformation);
+
+ if (color != null) {
+ for (RocketComponentShape rcs : returnValue) {
+ if (rcs.getColor() == net.sf.openrocket.util.Color.INVISIBLE) continue; // don't change the color of invisible (often selection) components
+ rcs.setColor(color);
+ }
+ }
+
allShapes.addAll(Arrays.asList(returnValue));
return allShapes;
}
+
+ /**
+ * Gets the shapes required to draw the component.
+ *
+ * @param allShapes output buffer for the shapes to add to
+ * @param viewType the view type to draw the component in
+ * @param component component to draw and add to
+ * @param transformation transformation to apply to the component before drawing it
+ *
+ * @return the ArrayList
containing all the shapes to draw.
+ */
+ private static PriorityQueue addThisShape(
+ PriorityQueue allShapes, // this is the output parameter
+ final RocketPanel.VIEW_TYPE viewType,
+ final RocketComponent component,
+ final Transformation transformation) {
+ return addThisShape(allShapes, viewType, component, transformation, null);
+ }
/**
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
index 92df95906..9f4f309f7 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
@@ -252,7 +252,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
});
}
- private void updateFigures() {
+ public void updateFigures() {
if (!is3d)
figure.updateFigure();
else