diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 33b6e0e2e..3d77c09bc 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -714,11 +714,32 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab final double finLength = singleFinBounds.max.x; final double finHeight = singleFinBounds.max.y; - BoundingBox compBox = new BoundingBox().update(getComponentLocations()); + Coordinate[] locations = getInstanceLocations(); + double[] angles = getInstanceAngles(); + BoundingBox finSetBox = new BoundingBox(); - BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ), - compBox.max.add( finLength, finHeight, finHeight )); - return finSetBox; + /* + * The fins themselves will be offset by the location itself to match + * the outside radius of a body tube. The bounds encapsulate the outer + * portion of all the tips of the fins. + * + * The height of each fin along the Y axis can be determined by: + * yHeight = cos(angle) * finHeight + * + * The height (depth?) of each fin along the Z axis can be determined by: + * zHeight = sin(angle) * finHeight + * + * The boundingBox should be that box which is the smallest box where + * a convex hull will contain all Coordinates. + */ + for (int i = 0; i < locations.length; i++) { + double y = Math.cos(angles[i]) * finHeight; + double z = Math.sin(angles[i]) * finHeight; + finSetBox.update(locations[i].add(0, y, z)); + finSetBox.update(locations[i].add(finLength, y, z)); + } + + return finSetBox; } /** diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 803013df3..239e03d04 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -1,6 +1,7 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayDeque; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -551,16 +552,71 @@ public class FlightConfiguration implements FlightConfigurableParameter> entry : map.entrySet()) { + RocketComponent component = entry.getKey(); + List contexts = entry.getValue(); + + Collection coordinates = new ArrayList(); + /* FinSets already provide a bounding box, so let's use that. + */ + if (component instanceof FinSet) { + bounds.update(((FinSet) component).getBoundingBox()); + continue; + } else { + coordinates.addAll(component.getComponentBounds()); + } + BoundingBox componentBox = new BoundingBox(); + List transformedCoords = new ArrayList(); + for (InstanceContext ctxt : contexts) { + /* + * If the instance is not active in the current context, then + * skip the bound calculations. This is mildly confusing since + * getActiveInstances() implies that it will only return the + * instances that are active, but it returns all instances and + * the context indicates if it is active or not. + */ + if (!ctxt.active) { + continue; + } + for (Coordinate c : coordinates) { + Coordinate tc = null; + /* These components do not need the transform performed in + * order to provide the proper coordinates for length calculation. + * The transformation will cause the values to be calculated + * incorrectly. This should be fixed in the appropriate places + * not handled as one-offs in here. + */ + if ((component instanceof AxialStage) || (component instanceof BodyTube) || + (component instanceof PodSet)) { + tc = c; + } else { + tc = ctxt.transform.transform(c); + } + componentBox.update(tc); + transformedCoords.add(tc); + } + } + + bounds.update(componentBox); + } + boundsModID = rocket.getModID(); cachedLength = bounds.span().x; + /* Special case for the scenario that all of the stages are removed and are + * inactive. Its possible that this shouldn't be allowed, but it is currently + * so we'll just adjust the length here. + */ + if (getActiveStages().isEmpty()) { + cachedLength = 0; + } cachedBounds.update( bounds ); } diff --git a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java index fe11df0dc..ebec46523 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java @@ -44,7 +44,7 @@ public class FlightConfigurationTest extends BaseTestCase { // preconditions assertThat("active stage count doesn't match", config.getActiveStageCount(), equalTo(2)); - final double expectedLength = 0.33; + final double expectedLength = 0.335; final double calculatedLength = config.getLength(); assertEquals("source config length doesn't match: ", expectedLength, calculatedLength, EPSILON); @@ -70,7 +70,7 @@ public class FlightConfigurationTest extends BaseTestCase { int expectedMotorCount = 2; int actualMotorCount = config1.getActiveMotors().size(); assertThat("active motor count doesn't match", actualMotorCount, equalTo(expectedMotorCount)); - double expectedLength = 0.33; + double expectedLength = 0.335; assertEquals("source config length doesn't match: ", expectedLength, config1.getLength(), EPSILON); double expectedReferenceLength = 0.024; assertEquals("source config reference length doesn't match: ", expectedReferenceLength, config1.getReferenceLength(), EPSILON); diff --git a/swing/src/net/sf/openrocket/gui/components/StageSelector.java b/swing/src/net/sf/openrocket/gui/components/StageSelector.java index 81accf8b8..f5972ad28 100644 --- a/swing/src/net/sf/openrocket/gui/components/StageSelector.java +++ b/swing/src/net/sf/openrocket/gui/components/StageSelector.java @@ -10,21 +10,17 @@ import javax.swing.JPanel; import javax.swing.JToggleButton; import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.StateChangeListener; @SuppressWarnings("serial") public class StageSelector extends JPanel implements StateChangeListener { - private static final Translator trans = Application.getTranslator(); - private final Rocket rocket; private List buttons = new ArrayList(); @@ -41,7 +37,7 @@ public class StageSelector extends JPanel implements StateChangeListener { this.removeAll(); for(AxialStage stage : configuration.getRocket().getStageList()){ JToggleButton button = new JToggleButton(new StageAction(stage)); - button.setSelected(true); + button.setSelected(configuration.isStageActive(stage.getStageNumber())); this.add(button); buttons.add(button); } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 5775390a1..188d70ad2 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -419,13 +419,14 @@ public class RocketFigure extends AbstractScaleFigure { protected void updateSubjectDimensions() { // calculate bounds, and store in class variables final BoundingBox bounds = rocket.getSelectedConfiguration().getBoundingBox(); + final double maxR = Math.max(Math.hypot(bounds.min.y, bounds.min.z), + Math.hypot(bounds.max.y, bounds.max.z)); switch (currentViewType) { case SideView: - subjectBounds_m = new Rectangle2D.Double(bounds.min.x, bounds.min.y, bounds.span().x, bounds.span().y); + subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR); break; case BackView: - final double maxR = Math.max(Math.hypot(bounds.min.y, bounds.min.z), Math.hypot(bounds.max.y, bounds.max.z)); subjectBounds_m = new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR); break; default: diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 17690194a..ca06de076 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -607,20 +607,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figure3d.setCG(cg); figure3d.setCP(cp); - // Length bound is assumed to be tight - double length = 0; - Collection bounds = curConfig.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; - } - + double length = curConfig.getLength(); + double diameter = Double.NaN; for (RocketComponent c : curConfig.getCoreComponents()) { if (c instanceof SymmetricComponent) {