From 5f42a10c2030b9403ab39186a2683d693418a5b5 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Fri, 24 Jul 2015 16:44:10 -0400 Subject: [PATCH] [Major Refactor] Parallel stages are children of centerline stages --- .../file/openrocket/savers/StageSaver.java | 14 - .../sf/openrocket/rocketcomponent/FinSet.java | 1 + .../sf/openrocket/rocketcomponent/Rocket.java | 3 +- .../rocketcomponent/RocketComponent.java | 217 +++--- .../sf/openrocket/rocketcomponent/Stage.java | 331 ++++----- .../openrocket/rocketcomponent/StageTest.java | 647 ++++++++++-------- .../gui/adaptors/StageSelectModel.java | 130 ---- .../gui/configdialog/FinSetConfig.java | 12 +- .../gui/configdialog/StageConfig.java | 20 +- .../print/visitor/CenteringRingStrategy.java | 4 +- .../gui/rocketfigure/BodyTubeShapes.java | 2 +- .../gui/rocketfigure/ShockCordShapes.java | 4 +- .../gui/rocketfigure/StreamerShapes.java | 4 +- .../gui/scalefigure/RocketFigure.java | 53 +- 14 files changed, 727 insertions(+), 715 deletions(-) delete mode 100644 swing/src/net/sf/openrocket/gui/adaptors/StageSelectModel.java diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java index 7144092d0..c5d4f5327 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java @@ -62,26 +62,12 @@ public class StageSaver extends ComponentAssemblySaver { private Collection addStageReplicationParams(final Stage currentStage) { List elementsToReturn = new ArrayList(); - final String relTo_tag = "relativeto"; - final String outside_tag = "outside"; final String instCt_tag = "instancecount"; final String radoffs_tag = "radialoffset"; final String startangle_tag = "angleoffset"; if (null != currentStage) { - // Save position unless "AFTER" - if (currentStage.getRelativePosition() != RocketComponent.Position.AFTER) { - // position type and offset are saved in superclass - // String type = currentStage.getRelativePositionMethod().name().toLowerCase(Locale.ENGLISH); - // double axialOffset = currentStage.getAxialPosition(); - // elementsToReturn.add("" + axialOffset + ""); - int relativeTo = currentStage.getRelativeToStage(); - elementsToReturn.add("<" + relTo_tag + ">" + relativeTo + ""); - } - - boolean outsideFlag = currentStage.getOutside(); - elementsToReturn.add("<" + outside_tag + ">" + outsideFlag + ""); int instanceCount = currentStage.getInstanceCount(); elementsToReturn.add("<" + instCt_tag + ">" + instanceCount + ""); double radialOffset = currentStage.getRadialOffset(); diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index dfa6c64b0..8185b932c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -664,6 +664,7 @@ public abstract class FinSet extends ExternalComponent { finArea = -1; cantRotation = null; } + super.componentChanged(e); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 1bd4bc27b..865bf1249 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -77,7 +77,6 @@ public class Rocket extends RocketComponent { flightConfigurationIDs.add(null); } - // Does the rocket have a perfect finish (a notable amount of laminar flow) private boolean perfectFinish = false; @@ -93,6 +92,8 @@ public class Rocket extends RocketComponent { treeModID = modID; functionalModID = modID; defaultConfiguration = new Configuration(this); + + Stage.resetStageCount(); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 297154a85..86d70224c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -79,12 +79,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab /** * Parent component of the current component, or null if none exists. */ - private RocketComponent parent = null; + protected RocketComponent parent = null; + + /** + * previous child in parent's child list + */ + protected RocketComponent previousComponent = null; /** * List of child components of this component. */ - private ArrayList children = new ArrayList(); + protected ArrayList children = new ArrayList(); //////// Parameters common to all components: @@ -106,7 +111,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * Offset of the position of this component relative to the normal position given by * relativePosition. By default zero, i.e. no position change. */ - // protected double position = 0; + protected double offset = 0; /** * Position of this component relative to it's parent. @@ -298,11 +303,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab protected void componentChanged(ComponentChangeEvent e) { // No-op checkState(); + this.update(); } - /** * Return the user-provided name of the component, or the component base * name if the user-provided name is empty. This can be used in the UI. @@ -883,7 +888,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @param position the relative positioning. */ protected void setRelativePosition(RocketComponent.Position position) { - // this variable does not change the internal representation // the relativePosition (method) is just the lens through which external code may view this component's position. this.relativePosition = position; @@ -898,40 +902,41 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * * @return double position of the component relative to the parent, with respect to position */ - public double asPositionValue(Position thePosition, RocketComponent relativeTo) { - double result = this.position.x; // relative - // this should be the usual case.... only 'Stage' classes should call this with anything else.... - double relativeAxialPosition = this.position.x; - if (relativeTo != this.parent) { - Coordinate refAbsPos = relativeTo.getAbsolutePositionVector(); - Coordinate curAbsPos = this.getAbsolutePositionVector(); - relativeAxialPosition = (curAbsPos.x - refAbsPos.x); + public double asPositionValue(Position thePosition) { + if (null == this.parent) { + return 0.0; } - if (relativeTo != null) { - double relLength = relativeTo.getLength(); - - switch (thePosition) { - case AFTER: - result = relativeAxialPosition + (-relLength - this.getLength()) / 2; - break; - case ABSOLUTE: - Coordinate curAbsPos = this.getAbsolutePositionVector(); - result = curAbsPos.x; - break; - case TOP: - result = relativeAxialPosition + (relLength - this.getLength()) / 2; - break; - case MIDDLE: - result = relativeAxialPosition; - break; - case BOTTOM: - result = relativeAxialPosition + (this.length - relLength) / 2; - break; - default: - throw new BugException("Unknown position type: " + thePosition); + double thisCenterX = this.position.x; + double relativeLength = this.parent.length; + double result = Double.NaN; + + switch (thePosition) { + case AFTER: + if (null == this.previousComponent) { + result = thisCenterX + (relativeLength - this.getLength()) / 2; + } else { + double relativeAxialOffset = this.previousComponent.getRelativePositionVector().x; + relativeLength = this.previousComponent.getLength(); + result = (thisCenterX - relativeAxialOffset) - (relativeLength + this.getLength()) / 2; } + break; + case ABSOLUTE: + result = this.getAbsolutePositionVector().x; + break; + case TOP: + result = thisCenterX + (relativeLength - this.getLength()) / 2; + break; + case MIDDLE: + result = thisCenterX; + break; + case BOTTOM: + result = thisCenterX + (this.length - relativeLength) / 2; + break; + default: + throw new BugException("Unknown position type: " + thePosition); } + return result; } @@ -948,7 +953,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab public double getAxialOffset() { mutex.verify(); - return this.asPositionValue(this.relativePosition, this.parent); + return this.asPositionValue(this.relativePosition); } /** @@ -990,72 +995,88 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab setAxialOffset(value); } - public void setAxialOffset(double value) { - this.setAxialOffset(this.relativePosition, value, this.getParent()); + public void setAxialOffset(double _value) { + this.setAxialOffset(this.relativePosition, _value); this.fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - protected void setAxialOffset(Position positionMethod, double newOffset, RocketComponent referenceComponent) { - // if this is the root of a hierarchy, constrain the position to zeros. - // if referenceComponent is null, the function call is simply in error + protected void setAfter(RocketComponent referenceComponent) { + checkState(); - if ((null == this.parent) || (referenceComponent == null)) { + double newAxialPosition; + double refLength; + + if (null == referenceComponent) { + // if this is the first component in the stage, position from the top of the parent + if (null == this.parent) { + // Probably initialization order issue. Ignore a.t.t. + return; + } else { + refLength = this.parent.getLength(); + newAxialPosition = (-refLength + this.length) / 2; + } + } else { + refLength = referenceComponent.getLength(); + double refRelX = referenceComponent.getRelativePositionVector().x; + + newAxialPosition = refRelX + (refLength + this.length) / 2; + } + + //this.relativePosition = Position.AFTER; + this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z); + } + + protected void setAxialOffset(Position positionMethod, double newOffset) { + // if this is the root of a hierarchy, constrain the position to zero. + if (null == this.parent) { return; } - if (referenceComponent == this) { - throw new BugException("cannot move a component relative to itself!"); - } checkState(); - // if (this instanceof Stage) { - // System.err.println(String.format(" Setting Stage X offs: type: %s pos: %f <== (%s,%f,%s)", this.relativePosition.name(), this.position.x, - // positionMethod.name(), newOffset, referenceComponent.getName())); - // } + + this.relativePosition = positionMethod; + this.offset = newOffset; double newAxialPosition = Double.NaN; - double refRelX = referenceComponent.position.x; - double refLength = referenceComponent.getLength(); - - if (referenceComponent.isAncestor(this)) { - referenceComponent = this.parent; - refRelX = 0; - } + double refLength = this.parent.getLength(); switch (positionMethod) { case ABSOLUTE: newAxialPosition = newOffset - this.parent.position.x; break; case AFTER: - newAxialPosition = refRelX + (refLength + this.length) / 2; - break; + this.setAfter(this.previousComponent); + return; case TOP: - newAxialPosition = refRelX + (-refLength + this.length) / 2 + newOffset; + newAxialPosition = (-refLength + this.length) / 2 + newOffset; break; case MIDDLE: - newAxialPosition = refRelX + newOffset; + newAxialPosition = newOffset; break; case BOTTOM: - newAxialPosition = refRelX + (+refLength - this.length) / 2 + newOffset; + newAxialPosition = (+refLength - this.length) / 2 + newOffset; break; default: throw new BugException("Unknown position type: " + positionMethod); } + if (Double.NaN == newAxialPosition) { + throw new BugException("setAxialOffset is broken -- attempted to update as NaN: " + this.toDebugDetail()); + } this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z); + } + + protected void update() { + if (null == this.parent) { + return; + } - // if ((this instanceof Stage) && (2 == this.getStageNumber())) { - // System.err.println(String.format(" Set Stage X offs: type: %s pos: %f", this.relativePosition.name(), this.position.x)); - // } + this.setAxialOffset(this.relativePosition, this.offset); } public Coordinate getRelativePositionVector() { return this.position; } - // disabled - // public void setRelativePositionVector(final Coordinate _newPos) { - // // this.setPosition( this.relativePosition, _newPos ); - // } - public Coordinate getAbsolutePositionVector() { if (null == this.parent) { // i.e. root / Rocket instance OR improperly initialized components return new Coordinate(); @@ -1364,6 +1385,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab throw new IllegalStateException("Component " + component.getComponentName() + " not currently compatible with component " + getComponentName()); } + children.add(index, component); component.parent = this; @@ -1556,24 +1578,25 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * * @return the stage number this component belongs to. */ - public final int getStageNumber() { + public int getStageNumber() { checkState(); if (parent == null) { throw new IllegalArgumentException("getStageNumber() called for root component"); } - RocketComponent stage = this; - while (!(stage instanceof Stage)) { - stage = stage.parent; - if (stage == null || stage.parent == null) { + RocketComponent curComponent = this; + while (!(curComponent instanceof Stage)) { + curComponent = curComponent.parent; + if (curComponent == null || curComponent.parent == null) { throw new IllegalStateException("getStageNumber() could not find parent " + "stage."); } } - return stage.parent.getChildPosition(stage); + Stage stage = (Stage) curComponent; + + return stage.getStageNumber(); } - /** * Find a component with the given ID. The component tree is searched from this component * down (including this component) for the ID and the corresponding component is returned, @@ -2077,16 +2100,46 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } - // Primarily for debug use - public void dumpTree(final boolean includeHeader, final String prefix) { - if (includeHeader) { - System.err.println(" [Name] [Length] [Rel Pos] [Abs Pos] "); + // multi-line output + protected StringBuilder toDebugDetail() { + StringBuilder buf = new StringBuilder(); + StackTraceElement[] stackTrace = (new Exception()).getStackTrace(); + buf.append(" >> Dumping Detailed Information from: " + stackTrace[1].getMethodName() + "\n"); + buf.append(" current Component: " + this.getName() + " ofClass: " + this.getClass().getSimpleName() + "\n"); + buf.append(" offset: " + this.offset + " via: " + this.relativePosition.name() + " => " + this.getAxialOffset() + "\n"); + buf.append(" thisCenterX: " + this.position.x + "\n"); + buf.append(" this length: " + this.length + "\n"); + if (null == this.previousComponent) { + buf.append(" ..prevComponent: " + null + "\n"); + } else { + RocketComponent refComp = this.previousComponent; + buf.append(" >>prevCompName: " + refComp.getName() + "\n"); + buf.append(" ..prevCenterX: " + refComp.position.x + "\n"); + buf.append(" ..prevLength: " + refComp.getLength() + "\n"); } + return buf; + } + + // Primarily for debug use + public String toDebugTree() { + StringBuilder buffer = new StringBuilder(); + buffer.append("\n ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ======\n"); + buffer.append(" [Name] [Length] [Rel Pos] [Abs Pos] \n"); + this.dumpTreeHelper(buffer, ""); + return buffer.toString(); + } + + public void toDebugTreeNode(final StringBuilder buffer, final String prefix) { + buffer.append(String.format("%s %-24s %5.3f %24s %24s\n", prefix, this.getName(), this.getLength(), + this.getRelativePositionVector(), this.getAbsolutePositionVector())); + } + + public void dumpTreeHelper(StringBuilder buffer, final String prefix) { + this.toDebugTreeNode(buffer, prefix); - System.err.println(String.format("%s >> %-24s %5.3f %24s %24s", prefix, this.getName(), this.getLength(), this.getRelativePositionVector(), this.getAbsolutePositionVector())); Iterator iterator = this.children.iterator(); while (iterator.hasNext()) { - iterator.next().dumpTree(false, prefix + " "); + iterator.next().dumpTreeHelper(buffer, prefix + " "); } } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Stage.java b/core/src/net/sf/openrocket/rocketcomponent/Stage.java index c75a93517..56ede1182 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -17,76 +17,27 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon private FlightConfigurationImpl separationConfigurations; - private boolean outside = false; + private boolean centerline = true; private double angularPosition_rad = 0; private double radialPosition_m = 0; - private Stage stageRelativeTo = null; private int count = 1; private double angularSeparation = Math.PI; + private int stageNumber; + private static int stageCount; public Stage() { this.separationConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new StageSeparationConfiguration()); this.relativePosition = Position.AFTER; - } - - @Override - protected void componentChanged(ComponentChangeEvent e) { - checkState(); - - if (e.isAerodynamicChange() || e.isMassChange()) { - // System.err.println(">> in (" + this.getStageNumber() + ")" + this.getName()); - // update this component - this.updateBounds(); - this.updateCenter(); - - // now update children relative to this - int childIndex = 0; - int childCount = this.getChildCount(); - RocketComponent prevComp = null; - while (childIndex < childCount) { - RocketComponent curComp = this.getChild(childIndex); - // System.err.println(" updating position of " + curComp + " via (AFTER, O, " + prevComp + ")"); - if (0 == childIndex) { - curComp.setAxialOffset(Position.TOP, 0, this); - } else { - if (Position.AFTER != curComp.getRelativePositionMethod()) { - throw new IllegalStateException(" direct children of a Stage are expected to be positioned via AFTER."); - } - curComp.setAxialOffset(Position.AFTER, 0, prevComp); - } - // System.err.println(" position updated to: " + curComp.getAxialOffset()); - - prevComp = curComp; - childIndex++; - } - - - } + stageNumber = Stage.stageCount; + Stage.stageCount++; } protected String toPositionString() { return ">> " + this.getName() + " rel: " + this.getRelativePositionVector().x + " abs: " + this.getAbsolutePositionVector().x; } - protected void dumpDetail() { - StackTraceElement[] stackTrace = (new Exception()).getStackTrace(); - System.err.println(" >> Dumping Stage Detailed Information from: " + stackTrace[1].getMethodName()); - System.err.println(" curStageName: " + this.getName()); - System.err.println(" method: " + this.relativePosition.name()); - System.err.println(" thisCenterX: " + this.position.x); - System.err.println(" this length: " + this.length); - if (-1 == this.getRelativeToStage()) { - System.err.println(" ..refStageName: " + null); - } else { - Stage refStage = this.stageRelativeTo; - System.err.println(" ..refStageName: " + refStage.getName()); - System.err.println(" ..refCenterX: " + refStage.position.x); - System.err.println(" ..refLength: " + refStage.getLength()); - } - } - @Override public String getComponentName() { @@ -103,6 +54,30 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon return true; } + @Override + public void toDebugTreeNode(final StringBuilder buffer, final String prefix) { + + String thisLabel = this.getName() + " (" + this.getStageNumber() + ")"; + + buffer.append(String.format("%s %-24s %5.3f %24s %24s", prefix, thisLabel, this.getLength(), + this.getRelativePositionVector(), this.getAbsolutePositionVector())); + + if (this.isCenterline()) { + buffer.append("\n"); + } else { + buffer.append(String.format(" %4.1f//%s \n", this.getAxialOffset(), this.relativePosition.name())); + Coordinate componentAbsolutePosition = this.getAbsolutePositionVector(); + Coordinate[] instanceCoords = new Coordinate[] { componentAbsolutePosition }; + instanceCoords = this.shiftCoordinates(instanceCoords); + + for (int instance = 0; instance < this.count; instance++) { + Coordinate instanceAbsolutePosition = instanceCoords[instance]; + buffer.append(String.format("%s [instance %2d of %2d] %s\n", prefix, instance, count, instanceAbsolutePosition)); + } + } + + } + /** * Check whether the given type can be added to this component. A Stage allows * only BodyComponents to be added. @@ -113,7 +88,11 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon */ @Override public boolean isCompatible(Class type) { - return BodyComponent.class.isAssignableFrom(type); + if (type.equals(Stage.class)) { + return true; + } else { + return BodyComponent.class.isAssignableFrom(type); + } } @Override @@ -129,35 +108,34 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon return copy; } + @Override public boolean getOutside() { - return this.outside; + return !isCenterline(); } + /** + * Detects if this Stage is attached directly to the Rocket (and is thus centerline) + * Or if this stage is a parallel (external) stage. + * + * @return whether this Stage is along the center line of the Rocket. + */ @Override public boolean isCenterline() { - return !this.outside; + if (this.parent instanceof Rocket) { + this.centerline = true; + } else { + this.centerline = false; + } + return this.centerline; } + /** + * Stub. + * The actual value is set via 'isCenterline()' + */ @Override public void setOutside(final boolean _outside) { - if (this.outside == _outside) { - return; - } - - this.outside = _outside; - if (this.outside) { - this.relativePosition = Position.BOTTOM; - if (null == this.stageRelativeTo) { - this.stageRelativeTo = this.updatePrevAxialStage(); - } - } else { - this.relativePosition = Position.AFTER; - this.stageRelativeTo = this.updatePrevAxialStage(); - this.count = 1; - } - - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @Override @@ -172,67 +150,81 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon @Override public void setInstanceCount(final int _count) { mutex.verify(); + if (this.centerline) { + return; + } + this.count = _count; this.angularSeparation = Math.PI * 2 / this.count; - - if (this.outside) { - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); - } + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @Override public double getAngularOffset() { - if (this.outside) { - return this.angularPosition_rad; - } else { + if (this.centerline) { return 0.; + } else { + return this.angularPosition_rad; } - } @Override public void setAngularOffset(final double angle_rad) { - this.angularPosition_rad = angle_rad; - if (this.outside) { - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + if (this.centerline) { + return; } + + this.angularPosition_rad = angle_rad; + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @Override public double getRadialOffset() { - if (this.outside) { - return this.radialPosition_m; - } else { + if (this.centerline) { return 0.; + } else { + return this.radialPosition_m; } } @Override public void setRadialOffset(final double radius) { - this.radialPosition_m = radius; // log.error(" set radial position for: " + this.getName() + " to: " + this.radialPosition_m + " ... in meters?"); - if (this.outside) { + if (false == this.centerline) { + this.radialPosition_m = radius; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } } public void setRelativePositionMethod(final Position _newPosition) { - if (Position.AFTER != _newPosition) { - this.outside = true; + if (null == this.parent) { + throw new NullPointerException(" a Stage requires a parent before any positioning! "); + } + if (this.isCenterline()) { + // Centerline stages must be set via AFTER-- regardless of what was requested: + super.setRelativePosition(Position.AFTER); + } else if (this.parent instanceof Stage) { + if (Position.AFTER == _newPosition) { + log.warn("Stages cannot be relative to other stages via AFTER! Ignoring."); + super.setRelativePosition(Position.TOP); + } else { + super.setRelativePosition(_newPosition); + } } - - super.setRelativePosition(_newPosition); } @Override public double getPositionValue() { mutex.verify(); - if (null == this.stageRelativeTo) { - return super.asPositionValue(this.relativePosition, this.getParent()); - } else { - return getAxialOffset(); - } + return getAxialOffset(); + } + + /* + * @deprecated remove when the file is fixed.... + */ + public void setRelativeToStage(final int _relToStage) { + // no-op } /** @@ -242,46 +234,43 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon * @return the stage number which this stage is positioned relative to */ public int getRelativeToStage() { - if (null == this.stageRelativeTo) { + if (null == this.parent) { return -1; - } - - return this.getRocket().getChildPosition(this.stageRelativeTo); - } - - - /* - * - * @param _relTo the stage number which this stage is positioned relative to - */ - public Stage setRelativeToStage(final int _relTo) { - mutex.verify(); - if ((_relTo < 0) || (_relTo >= this.getRocket().getStageCount())) { - log.error("attempt to position this stage relative to a non-existent stage number. Ignoring."); - this.stageRelativeTo = null; - } else if (_relTo == this.getRocket().getChildPosition(this)) { - // self-referential: also an error - this.stageRelativeTo = null; + } else if (this.parent instanceof Stage) { + return this.parent.parent.getChildPosition(this.parent); } else if (this.isCenterline()) { - this.relativePosition = Position.AFTER; - updatePrevAxialStage(); - } else { - this.stageRelativeTo = (Stage) this.getRocket().getChild(_relTo); + if (0 < this.stageNumber) { + return --this.stageNumber; + } } - return this.stageRelativeTo; + return -1; } + public static void resetStageCount() { + Stage.stageCount = 0; + } + + @Override + public int getStageNumber() { + return this.stageNumber; + } @Override public double getAxialOffset() { double returnValue; - if (null == this.stageRelativeTo) { - returnValue = super.asPositionValue(Position.TOP, this.getParent()); - } else if (this.isCenterline()) { - returnValue = super.asPositionValue(Position.AFTER, this.stageRelativeTo); + + if (this.isCenterline()) { + if (Position.AFTER == this.relativePosition) { + returnValue = super.getAxialOffset(); + } else if (Position.TOP == this.relativePosition) { + this.relativePosition = Position.AFTER; + returnValue = super.getAxialOffset(); + } else { + throw new BugException("found a Stage on centerline, but not positioned as AFTER. Please fix this! " + this.getName() + " is " + this.getRelativePosition().name()); + } } else { - returnValue = super.asPositionValue(this.relativePosition, this.stageRelativeTo); + returnValue = super.asPositionValue(this.relativePosition); } if (0.000001 > Math.abs(returnValue)) { @@ -294,12 +283,10 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon @Override public void setAxialOffset(final double _pos) { this.updateBounds(); - super.setAxialOffset(this.relativePosition, _pos, this.stageRelativeTo); + super.setAxialOffset(this.relativePosition, _pos); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - // TOOD: unify with 'generate instanceOffsets()' - // what is the use of this again? @Override public Coordinate[] shiftCoordinates(Coordinate[] c) { checkState(); @@ -328,56 +315,74 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon return toReturn; } + @Override + protected StringBuilder toDebugDetail() { + StringBuilder buf = super.toDebugDetail(); + // if (-1 == this.getRelativeToStage()) { + // System.err.println(" >>refStageName: " + null + "\n"); + // } else { + // Stage refStage = (Stage) this.parent; + // System.err.println(" >>refStageName: " + refStage.getName() + "\n"); + // System.err.println(" ..refCenterX: " + refStage.position.x + "\n"); + // System.err.println(" ..refLength: " + refStage.getLength() + "\n"); + // } + return buf; + } @Override public void updateBounds() { - // currently only updates the length this.length = 0; Iterator childIterator = this.getChildren().iterator(); while (childIterator.hasNext()) { RocketComponent curChild = childIterator.next(); - this.length += curChild.getLength(); + if (curChild.isCenterline()) { + this.length += curChild.getLength(); + } } } - /** - * @Warning this will return the previous axial stage REGARDLESS of whether 'this' is in the centerline stack or not. - * @return previous axial stage (defined as above, in the direction of launch) - */ - protected Stage updatePrevAxialStage() { - if (null != this.getParent()) { - Rocket rocket = this.getRocket(); - int thisStageNumber = rocket.getChildPosition(this); - int curStageIndex = thisStageNumber - 1; - while (curStageIndex >= 0) { - Stage curStage = (Stage) rocket.getChild(curStageIndex); - if (curStage.isCenterline()) { - this.stageRelativeTo = curStage; - return this.stageRelativeTo; - } - curStageIndex--; - } + @Override + protected void update() { + if (null == this.parent) { + return; } - this.stageRelativeTo = null; - return null; + this.updateBounds(); + if (this.parent instanceof Rocket) { + int childNumber = this.parent.getChildPosition(this); + if (0 == childNumber) { + this.setAfter(null); + } else { + RocketComponent prevStage = this.parent.getChild(childNumber - 1); + this.setAfter(prevStage); + } + } else if (this.parent instanceof Stage) { + this.updateBounds(); + super.update(); + } + + + this.updateChildSequence(); + + return; } - protected void updateCenter() { - if (null == this.stageRelativeTo) { - this.updatePrevAxialStage(); - if (null == this.stageRelativeTo) { - // this stage is actually the topmost Stage, instead of just out-of-date - this.setAxialOffset(Position.ABSOLUTE, this.getLength() / 2, this.getRocket()); - return; + protected void updateChildSequence() { + Iterator childIterator = this.getChildren().iterator(); + RocketComponent prevComp = null; + while (childIterator.hasNext()) { + RocketComponent curChild = childIterator.next(); + if (curChild.isCenterline()) { + curChild.previousComponent = prevComp; + prevComp = curChild; + } else { + curChild.previousComponent = null; } } - - // general case: - double offset = super.asPositionValue(this.relativePosition, this.stageRelativeTo); - this.setAxialOffset(this.relativePosition, offset, this.stageRelativeTo); } + + } diff --git a/core/test/net/sf/openrocket/rocketcomponent/StageTest.java b/core/test/net/sf/openrocket/rocketcomponent/StageTest.java index bc2e5805d..601d5f97d 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/StageTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/StageTest.java @@ -2,6 +2,7 @@ package net.sf.openrocket.rocketcomponent; //import junit.framework.TestCase; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import net.sf.openrocket.rocketcomponent.RocketComponent.Position; import net.sf.openrocket.util.Coordinate; @@ -12,7 +13,7 @@ import org.junit.Test; public class StageTest extends BaseTestCase { // tolerance for compared double test results - protected final double EPSILON = 0.001; + protected final double EPSILON = 0.00001; protected final Coordinate ZERO = new Coordinate(0., 0., 0.); @@ -23,8 +24,8 @@ public class StageTest extends BaseTestCase { public Rocket createTestRocket() { double tubeRadius = 1; // setup - Rocket root = new Rocket(); - root.setName("Rocket"); + Rocket rocket = new Rocket(); + rocket.setName("Rocket"); Stage sustainer = new Stage(); sustainer.setName("Sustainer stage"); @@ -34,76 +35,48 @@ public class StageTest extends BaseTestCase { RocketComponent sustainerBody = new BodyTube(3.0, tubeRadius, 0.01); sustainerBody.setName("Sustainer Body "); sustainer.addChild(sustainerBody); - root.addChild(sustainer); + rocket.addChild(sustainer); Stage core = new Stage(); core.setName("Core stage"); - BodyTube coreBody = new BodyTube(6.0, tubeRadius, 0.01); - coreBody.setName("Core Body "); - core.addChild(coreBody); - root.addChild(core); - - return root; + rocket.addChild(core); + BodyTube coreUpperBody = new BodyTube(1.8, tubeRadius, 0.01); + coreUpperBody.setName("Core UpBody "); + core.addChild(coreUpperBody); + BodyTube coreLowerBody = new BodyTube(4.2, tubeRadius, 0.01); + coreLowerBody.setName("Core LoBody "); + core.addChild(coreLowerBody); + FinSet coreFins = new TrapezoidFinSet(4, 4, 2, 2, 4); + coreFins.setName("Core Fins"); + coreLowerBody.addChild(coreFins); + return rocket; } public Stage createBooster() { double tubeRadius = 0.8; Stage booster = new Stage(); - booster.setName("Booster Stage A"); + booster.setName("Booster Stage"); booster.setOutside(true); RocketComponent boosterNose = new NoseCone(Transition.Shape.CONICAL, 2.0, tubeRadius); - boosterNose.setName("Booster A Nosecone"); + boosterNose.setName("Booster Nosecone"); booster.addChild(boosterNose); RocketComponent boosterBody = new BodyTube(2.0, tubeRadius, 0.01); - boosterBody.setName("Booster A Body "); + boosterBody.setName("Booster Body "); booster.addChild(boosterBody); Transition boosterTail = new Transition(); - boosterTail.setName("Booster A Tail"); + boosterTail.setName("Booster Tail"); boosterTail.setForeRadius(1.0); boosterTail.setAftRadius(0.5); boosterTail.setLength(1.0); booster.addChild(boosterTail); + booster.setInstanceCount(3); + booster.setRadialOffset(1.8); + return booster; } - // // instantiate a rocket with realistic numbers, matching a file that exhibited errors - // public Rocket createDeltaIIRocket() { - // - // // setup - // Rocket root = new Rocket(); - // root.setName("Rocket"); - // - // Stage payloadFairing = new Stage(); - // root.addChild(payloadFairing); - // payloadFairing.setName("Payload Fairing"); - // NoseCone payloadNose = new NoseCone(Transition.Shape.POWER, 0.0535, 0.03); - // payloadNose.setShapeParameter(0.55); - // payloadNose.setName("Payload Nosecone"); - // payloadNose.setAftRadius(0.3); - // payloadNose.setThickness(0.001); - // payloadFairing.addChild(payloadNose); - // BodyTube payloadBody = new BodyTube(0.0833, 0.03, 0.001); - // payloadBody.setName("Payload Body "); - // payloadFairing.addChild(payloadBody); - // Transition payloadTransition = new Transition(); - // payloadTransition.setName("Payload Aft Transition"); - // payloadTransition.setForeRadius(0.03); - // payloadTransition.setAftRadius(0.024); - // payloadTransition.setLength(0.04); - // payloadFairing.addChild(payloadTransition); - // - // Stage core = new Stage(); - // core.setName("Delta Core stage"); - // BodyTube coreBody = new BodyTube(0.0833, 0.3, 0.001); - // coreBody.setName("Delta Core Body "); - // core.addChild(coreBody); - // root.addChild(core); - // - // return root; - // } - /* From OpenRocket Technical Documentation * * 3.1.4 Coordinate systems @@ -116,41 +89,45 @@ public class StageTest extends BaseTestCase { @Test public void testSetRocketPositionFail() { - RocketComponent root = createTestRocket(); + RocketComponent rocket = createTestRocket(); Coordinate expectedPosition; Coordinate targetPosition; Coordinate resultPosition; - // case 1: the Root Rocket should be stationary + // case 1: the rocket Rocket should be stationary expectedPosition = ZERO; targetPosition = new Coordinate(+4.0, 0.0, 0.0); - root.setAxialOffset(targetPosition.x); - resultPosition = root.getRelativePositionVector(); - assertThat(" Moved the rocket root itself-- this should not be enabled.", expectedPosition.x, equalTo(resultPosition.x)); + rocket.setAxialOffset(targetPosition.x); + resultPosition = rocket.getRelativePositionVector(); + assertThat(" Moved the rocket rocket itself-- this should not be enabled.", expectedPosition.x, equalTo(resultPosition.x)); } @Test - public void testAddTopStage() { - RocketComponent root = createTestRocket(); + public void testAddSustainerStage() { + RocketComponent rocket = createTestRocket(); // Sustainer Stage - Stage sustainer = (Stage) root.getChild(0); + Stage sustainer = (Stage) rocket.getChild(0); RocketComponent sustainerNose = sustainer.getChild(0); RocketComponent sustainerBody = sustainer.getChild(1); assertThat(" createTestRocket failed: is sustainer stage an ancestor of the sustainer stage? ", sustainer.isAncestor(sustainer), equalTo(false)); assertThat(" createTestRocket failed: is sustainer stage an ancestor of the sustainer nose? ", sustainer.isAncestor(sustainerNose), equalTo(true)); - assertThat(" createTestRocket failed: is the root rocket an ancestor of the sustainer Nose? ", root.isAncestor(sustainerNose), equalTo(true)); + assertThat(" createTestRocket failed: is the rocket rocket an ancestor of the sustainer Nose? ", rocket.isAncestor(sustainerNose), equalTo(true)); assertThat(" createTestRocket failed: is sustainer Body an ancestor of the sustainer Nose? ", sustainerBody.isAncestor(sustainerNose), equalTo(false)); + int relToExpected = -1; + int relToStage = sustainer.getRelativeToStage(); + assertThat(" createTestRocket failed: sustainer relative position: ", relToStage, equalTo(relToExpected)); + double expectedSustainerLength = 5.0; assertThat(" createTestRocket failed: Sustainer size: ", sustainer.getLength(), equalTo(expectedSustainerLength)); double expectedSustainerX = +2.5; double sustainerX; sustainerX = sustainer.getRelativePositionVector().x; - assertThat(" createTestRocket failed: Relative position: ", sustainerX, equalTo(expectedSustainerX)); + assertThat(" createTestRocket failed: sustainer Relative position: ", sustainerX, equalTo(expectedSustainerX)); sustainerX = sustainer.getRelativePositionVector().x; - assertThat(" createTestRocket failed: Absolute position: ", sustainerX, equalTo(expectedSustainerX)); + assertThat(" createTestRocket failed: sustainer Absolute position: ", sustainerX, equalTo(expectedSustainerX)); double expectedSustainerNoseX = -1.5; double sustainerNosePosition = sustainerNose.getRelativePositionVector().x; @@ -170,44 +147,67 @@ public class StageTest extends BaseTestCase { // WARNING: this test will not pass unless 'testAddTopStage' is passing as well -- that function tests the dependencies... @Test - public void testAddMiddleStage() { - RocketComponent root = createTestRocket(); + public void testAddCoreStage() { + // vvvv function under test vvvv ( which indirectly tests initialization code, and that the test setup creates the preconditions that we expect + RocketComponent rocket = createTestRocket(); + // ^^^^ function under test ^^^^ // Core Stage - Stage core = (Stage) root.getChild(1); + Stage core = (Stage) rocket.getChild(1); double expectedCoreLength = 6.0; assertThat(" createTestRocket failed: Core size: ", core.getLength(), equalTo(expectedCoreLength)); double expectedCoreX = +8.0; double coreX; - core.setRelativePosition(Position.AFTER); + + int relToExpected = 0; + int relToStage = core.getRelativeToStage(); + assertThat(" createTestRocket failed: corerelative position: ", relToStage, equalTo(relToExpected)); coreX = core.getRelativePositionVector().x; - assertThat(" createTestRocket failed: Relative position: ", coreX, equalTo(expectedCoreX)); + assertThat(" createTestRocket failed: core Relative position: ", coreX, equalTo(expectedCoreX)); coreX = core.getAbsolutePositionVector().x; - assertThat(" createTestRocket failed: Absolute position: ", coreX, equalTo(expectedCoreX)); + assertThat(" createTestRocket failed: core Absolute position: ", coreX, equalTo(expectedCoreX)); - RocketComponent coreBody = core.getChild(0); - double expectedCoreBodyX = 0.0; - double coreBodyX = coreBody.getRelativePositionVector().x; - assertThat(" createTestRocket failed: core body rel X: ", coreBodyX, equalTo(expectedCoreBodyX)); - expectedCoreBodyX = expectedCoreX; - coreBodyX = coreBody.getAbsolutePositionVector().x; - assertThat(" createTestRocket failed: core body abs X: ", coreBodyX, equalTo(expectedCoreBodyX)); + RocketComponent coreUpBody = core.getChild(0); + double expectedX = -2.1; + double resultantX = coreUpBody.getRelativePositionVector().x; + + assertThat(" createTestRocket failed: core body rel X: ", resultantX, equalTo(expectedX)); + expectedX = 5.9; + resultantX = coreUpBody.getAbsolutePositionVector().x; + assertThat(" createTestRocket failed: core body abs X: ", resultantX, equalTo(expectedX)); + + RocketComponent coreLoBody = core.getChild(1); + expectedX = 0.9; + resultantX = coreLoBody.getRelativePositionVector().x; + assertEquals(" createTestRocket failed: core body rel X: ", expectedX, resultantX, EPSILON); + expectedX = 8.9; + resultantX = coreLoBody.getAbsolutePositionVector().x; + assertEquals(" createTestRocket failed: core body abs X: ", expectedX, resultantX, EPSILON); + + RocketComponent coreFins = coreLoBody.getChild(0); + expectedX = 0.1; + resultantX = coreFins.getRelativePositionVector().x; + assertEquals(" createTestRocket failed: core Fins rel X: ", expectedX, resultantX, EPSILON); + expectedX = 9.0; + resultantX = coreFins.getAbsolutePositionVector().x; + assertEquals(" createTestRocket failed: core Fins abs X: ", expectedX, resultantX, EPSILON); } @Test public void testSetStagePosition_topOfStack() { // setup - RocketComponent root = createTestRocket(); - Stage sustainer = (Stage) root.getChild(0); + RocketComponent rocket = createTestRocket(); + Stage sustainer = (Stage) rocket.getChild(0); Coordinate expectedPosition = new Coordinate(+2.5, 0., 0.); // i.e. half the tube length Coordinate targetPosition = new Coordinate(+4.0, 0., 0.); + // without making the rocket 'external' and the Stage should be restricted to AFTER positioning. - sustainer.setOutside(false); sustainer.setRelativePositionMethod(Position.ABSOLUTE); - assertThat("Setting a stage's position method to anything other than AFTER flags it off-center", sustainer.getOutside(), equalTo(true)); + assertThat("Setting a centerline stage to anything other than AFTER is ignored.", sustainer.getOutside(), equalTo(false)); + assertThat("Setting a centerline stage to anything other than AFTER is ignored.", sustainer.getRelativePosition(), equalTo(Position.AFTER)); // vv function under test sustainer.setAxialOffset(targetPosition.x); @@ -215,69 +215,89 @@ public class StageTest extends BaseTestCase { Coordinate resultantRelativePosition = sustainer.getRelativePositionVector(); assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = sustainer.getAbsolutePositionVector(); assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedPosition.x)); } @Test - public void testFindPrevAxialStage() { - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); - Stage booster = createBooster(); - root.addChild(booster); + public void testBoosterInitialization() { + // setup + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); + Stage boosterSet = createBooster(); + core.addChild(boosterSet); - Stage booster2 = new Stage(); - booster2.setOutside(true); - booster2.setName("Booster Set 2"); - RocketComponent booster2Body = new BodyTube(2.0, 1.0, 0.01); - booster2Body.setName("Booster Body 2"); - booster2.addChild(booster2Body); - root.addChild(booster2); + double targetOffset = 0; + boosterSet.setAxialOffset(Position.BOTTOM, targetOffset); + // vv function under test + boosterSet.setInstanceCount(2); + boosterSet.setRadialOffset(4.0); + boosterSet.setAngularOffset(Math.PI / 2); + // ^^ function under test + String treeDump = rocket.toDebugTree(); - Stage booster3 = new Stage(); - booster3.setOutside(true); - booster3.setName("Booster Set 3"); - RocketComponent booster3Body = new BodyTube(4.0, 1.0, 0.01); - booster3Body.setName("Booster Body 3"); - booster3.addChild(booster3Body); - root.addChild(booster3); + int expectedInstanceCount = 2; + int instanceCount = boosterSet.getInstanceCount(); + assertThat(" 'setInstancecount(int)' failed: ", instanceCount, equalTo(expectedInstanceCount)); - Stage tail = new Stage(); - tail.setName("Tail"); - RocketComponent tailBody = new BodyTube(4.0, 1.0, 0.01); - tailBody.setName("TailBody"); - root.addChild(tail); - tail.addChild(tailBody); + double expectedAbsX = 8.5; + Coordinate resultantCenter = boosterSet.getAbsolutePositionVector(); + assertEquals(treeDump + "\n>>'setAxialOffset()' failed: ", expectedAbsX, resultantCenter.x, EPSILON); - Stage prevAxialStage; - prevAxialStage = booster3.updatePrevAxialStage(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", prevAxialStage, equalTo(core)); - - prevAxialStage = tail.updatePrevAxialStage(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", prevAxialStage, equalTo(core)); + double expectedRadialOffset = 4.0; + double radialOffset = boosterSet.getRadialOffset(); + assertEquals(" 'setRadialOffset(double)' failed. offset: ", expectedRadialOffset, radialOffset, EPSILON); + double expectedAngularOffset = Math.PI / 2; + double angularOffset = boosterSet.getAngularOffset(); + assertEquals(" 'setAngularOffset(double)' failed. offset: ", expectedAngularOffset, angularOffset, EPSILON); } + // because even though this is an "outside" stage, it's relative to itself -- i.e. an error. + // also an error with a well-defined failure result (i.e. just failover to AFTER placement as the first stage of a rocket. @Test - public void testSetStagePosition_inStack() { + public void testBoosterInstanceLocation_BOTTOM() { // setup - RocketComponent root = createTestRocket(); - Stage sustainer = (Stage) root.getChild(0); - Stage core = (Stage) root.getChild(1); - Coordinate expectedSustainerPosition = new Coordinate(+2.5, 0., 0.); // i.e. half the tube length - Coordinate expectedCorePosition = new Coordinate(+8.0, 0., 0.); - Coordinate targetPosition = new Coordinate(+17.0, 0., 0.); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); + Stage boosterSet = createBooster(); + core.addChild(boosterSet); - sustainer.setAxialOffset(targetPosition.x); - Coordinate sustainerPosition = sustainer.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", sustainerPosition.x, equalTo(expectedSustainerPosition.x)); + double targetOffset = 0; + boosterSet.setAxialOffset(Position.BOTTOM, targetOffset); + int targetInstanceCount = 3; + double targetRadialOffset = 1.8; + // vv function under test + boosterSet.setInstanceCount(targetInstanceCount); + boosterSet.setRadialOffset(targetRadialOffset); + // ^^ function under test + String treeDump = rocket.toDebugTree(); - core.setAxialOffset(targetPosition.x); - Coordinate resultantCorePosition = core.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantCorePosition.x, equalTo(expectedCorePosition.x)); + double expectedX = 8.5; + double angle = Math.PI * 2 / targetInstanceCount; + double radius = targetRadialOffset; + + Coordinate componentAbsolutePosition = boosterSet.getAbsolutePositionVector(); + Coordinate[] instanceCoords = new Coordinate[] { componentAbsolutePosition }; + instanceCoords = boosterSet.shiftCoordinates(instanceCoords); + + int inst = 0; + Coordinate expectedPosition0 = new Coordinate(expectedX, radius * Math.cos(angle * inst), radius * Math.sin(angle * inst)); + Coordinate resultantPosition0 = instanceCoords[0]; + assertEquals(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", expectedPosition0, resultantPosition0); + + inst = 1; + Coordinate expectedPosition1 = new Coordinate(expectedX, radius * Math.cos(angle * inst), radius * Math.sin(angle * inst)); + Coordinate resultantPosition1 = instanceCoords[1]; + assertEquals(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", expectedPosition1, resultantPosition1); + + inst = 2; + Coordinate expectedPosition2 = new Coordinate(expectedX, radius * Math.cos(angle * inst), radius * Math.sin(angle * inst)); + Coordinate resultantPosition2 = instanceCoords[2]; + assertEquals(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", expectedPosition2, resultantPosition2); } @@ -286,31 +306,28 @@ public class StageTest extends BaseTestCase { @Test public void testSetStagePosition_outsideABSOLUTE() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); - Coordinate targetPosition = new Coordinate(+17.0, 0., 0.); - double expectedX = targetPosition.x; + double targetX = +17.0; + double expectedX = targetX - core.getAbsolutePositionVector().x; - // when 'external' the stage should be freely movable - booster.setOutside(true); - booster.setRelativePositionMethod(Position.ABSOLUTE); - booster.setRelativeToStage(1); + // when subStages should be freely movable // vv function under test - booster.setAxialOffset(targetPosition.x); + booster.setAxialOffset(Position.ABSOLUTE, targetX); // ^^ function under test Coordinate resultantRelativePosition = booster.getRelativePositionVector(); assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); double resultantPositionValue = booster.getPositionValue(); - assertThat(" 'setAxialPosition(double)' failed. PositionValue: ", resultantPositionValue, equalTo(expectedX)); + assertThat(" 'setAxialPosition(double)' failed. PositionValue: ", resultantPositionValue, equalTo(targetX)); double resultantAxialPosition = booster.getAxialOffset(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantAxialPosition, equalTo(expectedX)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantAxialPosition, equalTo(targetX)); + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(targetX)); } // WARNING: @@ -319,17 +336,17 @@ public class StageTest extends BaseTestCase { @Test public void testSetStagePosition_outsideTopOfStack() { // setup - RocketComponent root = createTestRocket(); - Stage sustainer = (Stage) root.getChild(0); + RocketComponent rocket = createTestRocket(); + Stage sustainer = (Stage) rocket.getChild(0); Coordinate expectedPosition = new Coordinate(+2.5, 0., 0.); Coordinate targetPosition = new Coordinate(+4.0, 0., 0.); // when 'external' the stage should be freely movable - sustainer.setOutside(true); sustainer.setRelativePositionMethod(Position.TOP); - sustainer.setRelativeToStage(0); + int expectedRelativeIndex = -1; - assertThat(" 'setRelativeToStage(int)' failed. Relative stage index:", sustainer.getRelativeToStage(), equalTo(expectedRelativeIndex)); + int resultantRelativeIndex = sustainer.getRelativeToStage(); + assertThat(" 'setRelativeToStage(int)' failed. Relative stage index:", expectedRelativeIndex, equalTo(resultantRelativeIndex)); // vv function under test sustainer.setAxialOffset(targetPosition.x); @@ -344,31 +361,31 @@ public class StageTest extends BaseTestCase { double expectedAxialPosition = 0; double resultantAxialPosition = sustainer.getAxialOffset(); assertThat(" 'getAxialPosition()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = sustainer.getAbsolutePositionVector(); assertThat(" 'setAbsolutePositionVector()' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedPosition.x)); } @Test public void testSetStagePosition_outsideTOP() { - Rocket root = this.createTestRocket(); + Rocket rocket = this.createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); - booster.setOutside(true); - booster.setRelativePositionMethod(Position.TOP); - booster.setRelativeToStage(1); - // vv function under test double targetOffset = +2.0; - booster.setAxialOffset(targetOffset); + + // vv function under test + booster.setAxialOffset(Position.TOP, targetOffset); // ^^ function under test - double expectedX = +9.5; + double expectedAbsoluteX = +9.5; + double expectedRelativeX = 1.5; Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedRelativeX)); + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantAxialOffset = booster.getAxialOffset(); assertThat(" 'getAxialPosition()' failed. Relative position: ", resultantAxialOffset, equalTo(targetOffset)); @@ -380,25 +397,24 @@ public class StageTest extends BaseTestCase { @Test public void testSetStagePosition_outsideMIDDLE() { // setup - RocketComponent root = createTestRocket(); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); // when 'external' the stage should be freely movable - booster.setOutside(true); - booster.setRelativePositionMethod(Position.MIDDLE); - booster.setRelativeToStage(1); // vv function under test double targetOffset = +2.0; - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.MIDDLE, targetOffset); // ^^ function under test - double expectedX = +10.0; + double expectedRelativeX = +2.0; + double expectedAbsoluteX = +10.0; Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedRelativeX)); + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantPositionValue = booster.getPositionValue(); @@ -411,26 +427,23 @@ public class StageTest extends BaseTestCase { @Test public void testSetStagePosition_outsideBOTTOM() { // setup - RocketComponent root = createTestRocket(); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); - - // when 'external' the stage should be freely movable - booster.setOutside(true); - booster.setRelativePositionMethod(Position.BOTTOM); - booster.setRelativeToStage(1); // vv function under test double targetOffset = +4.0; - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.BOTTOM, targetOffset); // ^^ function under test - double expectedX = +12.5; + double expectedRelativeX = +4.5; + double expectedAbsoluteX = +12.5; Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); - // for all stages, the absolute position should equal the relative, because the direct parent is the root component (i.e. the Rocket) + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedRelativeX)); + // for all stages, the absolute position should equal the relative, because the direct parent is the rocket component (i.e. the Rocket) Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantPositionValue = booster.getPositionValue(); assertThat(" 'setPositionValue()' failed. Relative position: ", resultantPositionValue, equalTo(targetOffset)); @@ -442,186 +455,260 @@ public class StageTest extends BaseTestCase { @Test public void testAxial_setTOP_getABSOLUTE() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); double targetOffset = +4.50; - booster.setOutside(true); - booster.setRelativePositionMethod(Position.TOP); - booster.setRelativeToStage(1); - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.TOP, targetOffset); - double expectedAxialOffset = +12.0; + double expectedAxialOffset = +4.0; Coordinate resultantRelativePosition = booster.getRelativePositionVector(); assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedAxialOffset)); - Stage refStage = core; // vv function under test - double resultantAxialPosition = booster.asPositionValue(Position.ABSOLUTE, refStage); + double resultantAxialPosition = booster.asPositionValue(Position.ABSOLUTE); // ^^ function under test - assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialOffset)); + double expectedAbsoluteX = +12.0; + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAbsoluteX)); } @Test public void testAxial_setTOP_getAFTER() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); double targetOffset = +4.50; - booster.setOutside(true); - booster.setRelativePositionMethod(Position.TOP); - booster.setRelativeToStage(1); - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.TOP, targetOffset); - Coordinate expectedPosition = new Coordinate(+12.0, 0., 0.); - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); + double expectedRelativeX = +4.0; + double resultantX = booster.getRelativePositionVector().x; + + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantX, equalTo(expectedRelativeX)); - Stage refStage = core; - double resultantAxialPosition; - double expectedAxialPosition; // vv function under test - resultantAxialPosition = booster.asPositionValue(Position.AFTER, refStage); + // because this component is not initalized to + resultantX = booster.asPositionValue(Position.AFTER); // ^^ function under test - expectedAxialPosition = -1.5; - assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + + double expectedAfterX = 4.5; + assertEquals(" 'setPositionValue()' failed. Relative position: ", expectedAfterX, resultantX, EPSILON); } @Test public void testAxial_setTOP_getMIDDLE() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); - + core.addChild(booster); double targetOffset = +4.50; - booster.setOutside(true); - booster.setRelativePositionMethod(Position.TOP); - booster.setRelativeToStage(1); - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.TOP, targetOffset); - Coordinate expectedPosition = new Coordinate(+12.0, 0., 0.); - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); + double expectedRelativeX = +4.0; + double resultantX = booster.getRelativePositionVector().x; + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantX, equalTo(expectedRelativeX)); - Stage refStage = core; double resultantAxialPosition; double expectedAxialPosition = +4.0; - // vv function under test - resultantAxialPosition = booster.asPositionValue(Position.MIDDLE, refStage); + resultantAxialPosition = booster.asPositionValue(Position.MIDDLE); // ^^ function under test - assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + + assertEquals(" 'setPositionValue()' failed. Relative position: ", expectedAxialPosition, resultantAxialPosition, EPSILON); } @Test public void testAxial_setTOP_getBOTTOM() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); + core.addChild(booster); double targetOffset = +4.50; - booster.setOutside(true); - booster.setRelativePositionMethod(Position.TOP); - booster.setRelativeToStage(1); - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.TOP, targetOffset); - Coordinate expectedPosition = new Coordinate(+12.0, 0., 0.); - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); + double expectedRelativeX = +4.0; + double resultantX = booster.getRelativePositionVector().x; + assertEquals(" 'setAxialPosition(double)' failed. Relative position: ", expectedRelativeX, resultantX, EPSILON); - Stage refStage = core; - double resultantAxialPosition; - double expectedAxialPosition; // vv function under test - resultantAxialPosition = booster.asPositionValue(Position.BOTTOM, refStage); + double resultantAxialOffset = booster.asPositionValue(Position.BOTTOM); // ^^ function under test - expectedAxialPosition = +3.5; - assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + double expectedAxialOffset = +3.5; + assertEquals(" 'setPositionValue()' failed. Relative position: ", expectedAxialOffset, resultantAxialOffset, EPSILON); } @Test public void testAxial_setBOTTOM_getTOP() { // setup - RocketComponent root = createTestRocket(); - Stage core = (Stage) root.getChild(1); + RocketComponent rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); Stage booster = createBooster(); - root.addChild(booster); - + core.addChild(booster); double targetOffset = +4.50; - booster.setOutside(true); - booster.setRelativePositionMethod(Position.BOTTOM); - booster.setRelativeToStage(1); - booster.setAxialOffset(targetOffset); + booster.setAxialOffset(Position.BOTTOM, targetOffset); - Coordinate expectedPosition = new Coordinate(+13.0, 0., 0.); - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); - assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); - - Stage refStage = core; - double resultantAxialPosition; - double expectedAxialPosition; + double expectedRelativeX = +5.0; + double resultantX = booster.getRelativePositionVector().x; + assertEquals(" 'setAxialPosition(double)' failed. Relative position: ", expectedRelativeX, resultantX, EPSILON); // vv function under test - resultantAxialPosition = booster.asPositionValue(Position.TOP, refStage); + double resultantAxialOffset = booster.asPositionValue(Position.TOP); // ^^ function under test - expectedAxialPosition = 5.5; - assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + double expectedAxialOffset = 5.5; + assertEquals(" 'setPositionValue()' failed. Relative position: ", expectedAxialOffset, resultantAxialOffset, EPSILON); } @Test - public void testInitializationOrder() { - Rocket root = createTestRocket(); - Stage boosterA = createBooster(); - root.addChild(boosterA); - Stage boosterB = createBooster(); - root.addChild(boosterB); + public void testOutsideStageRepositionTOPAfterAdd() { + final double boosterRadius = 0.8; + final double targetOffset = +2.50; + final Position targetMethod = Position.TOP; + Rocket rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); - double targetOffset = +4.50; + Stage booster = new Stage(); + booster.setName("Booster Stage"); + core.addChild(booster); + booster.setAxialOffset(targetMethod, targetOffset); // requirement: regardless of initialization order (which we cannot control) - // two boosters with identical initialization commands should end up at the same place. + // a booster should retain it's positioning method and offset while adding on children + double expectedRelativeX = -0.5; + double resultantOffset = booster.getRelativePositionVector().x; + assertEquals(" init order error: Booster: initial relative X: ", expectedRelativeX, resultantOffset, EPSILON); + double expectedAxialOffset = targetOffset; + resultantOffset = booster.getAxialOffset(); + assertEquals(" init order error: Booster: initial axial offset: ", expectedAxialOffset, resultantOffset, EPSILON); - boosterA.setOutside(true); - boosterA.setRelativePositionMethod(Position.BOTTOM); - boosterA.setRelativeToStage(1); - boosterA.setAxialOffset(targetOffset); + // Body Component 2 + RocketComponent boosterBody = new BodyTube(4.0, boosterRadius, 0.01); + boosterBody.setName("Booster Body "); + booster.addChild(boosterBody); - boosterB.dumpDetail(); - boosterB.setRelativePositionMethod(Position.TOP); - System.err.println(" B: setMeth: " + boosterB.getRelativePositionVector().x); - boosterB.setRelativeToStage(1); - System.err.println(" B: setRelTo: " + boosterB.getRelativePositionVector().x); - boosterB.setAxialOffset(targetOffset); - System.err.println(" B: setOffs: " + boosterB.getRelativePositionVector().x); - boosterB.setRelativePositionMethod(Position.BOTTOM); - System.err.println(" B: setMeth: " + boosterB.getRelativePositionVector().x); - boosterB.setOutside(true); - System.err.println(" B: setOutside:" + boosterB.getRelativePositionVector().x); + expectedAxialOffset = targetOffset; + resultantOffset = booster.getAxialOffset(); + assertEquals(" init order error: Booster: populated axial offset: ", expectedAxialOffset, resultantOffset, EPSILON); + expectedRelativeX = 1.5; + resultantOffset = booster.getRelativePositionVector().x; + assertEquals(" init order error: Booster: populated relative X: ", expectedRelativeX, resultantOffset, EPSILON); + expectedAxialOffset = targetOffset; - root.dumpTree(true, ""); + } + + @Test + public void testOutsideStageRepositionBOTTOMAfterAdd() { + final double boosterRadius = 0.8; + final double targetOffset = +2.50; + final Position targetMethod = Position.BOTTOM; + Rocket rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); - double offsetA = boosterA.getAxialOffset(); - double offsetB = boosterB.getAxialOffset(); + Stage booster = new Stage(); + booster.setName("Booster Stage"); + core.addChild(booster); + booster.setAxialOffset(targetMethod, targetOffset); - assertThat(" init order error: Booster A: resultant positions: ", offsetA, equalTo(targetOffset)); - assertThat(" init order error: Booster B: resultant positions: ", offsetB, equalTo(targetOffset)); + // requirement: regardless of initialization order (which we cannot control) + // a booster should retain it's positioning method and offset while adding on children + double expectedRelativeX = 5.5; + double resultantOffset = booster.getRelativePositionVector().x; + assertEquals(" init order error: Booster: initial relative X: ", expectedRelativeX, resultantOffset, EPSILON); + double expectedAxialOffset = targetOffset; + resultantOffset = booster.getAxialOffset(); + assertEquals(" init order error: Booster: initial axial offset: ", expectedAxialOffset, resultantOffset, EPSILON); + // Body Component 2 + RocketComponent boosterBody = new BodyTube(4.0, boosterRadius, 0.01); + boosterBody.setName("Booster Body "); + booster.addChild(boosterBody); + + expectedAxialOffset = targetOffset; + resultantOffset = booster.getAxialOffset(); + assertEquals(" init order error: Booster: populated axial offset: ", expectedAxialOffset, resultantOffset, EPSILON); + expectedRelativeX = 3.5; + resultantOffset = booster.getRelativePositionVector().x; + assertEquals(" init order error: Booster: populated relative X: ", expectedRelativeX, resultantOffset, EPSILON); + expectedAxialOffset = targetOffset; } + @Test + public void testStageInitializationMethodValueOrder() { + Rocket rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); + Stage boosterA = createBooster(); + boosterA.setName("Booster A Stage"); + core.addChild(boosterA); + Stage boosterB = createBooster(); + boosterB.setName("Booster B Stage"); + core.addChild(boosterB); + + double targetOffset = +4.50; + double expectedOffset = +4.0; + // requirement: regardless of initialization order (which we cannot control) + // two boosters with identical initialization commands should end up at the same place. + + boosterA.setAxialOffset(Position.TOP, targetOffset); + + boosterB.setRelativePositionMethod(Position.TOP); + boosterB.setAxialOffset(targetOffset); + + double resultantOffsetA = boosterA.getRelativePositionVector().x; + double resultantOffsetB = boosterB.getRelativePositionVector().x; + + assertEquals(" init order error: Booster A: resultant positions: ", expectedOffset, resultantOffsetA, EPSILON); + assertEquals(" init order error: Booster B: resultant positions: ", expectedOffset, resultantOffsetB, EPSILON); + + } + + + @Test + public void testStageNumbering() { + Rocket rocket = createTestRocket(); + Stage sustainer = (Stage) rocket.getChild(0); + Stage core = (Stage) rocket.getChild(1); + Stage boosterA = createBooster(); + boosterA.setName("Booster A Stage"); + core.addChild(boosterA); + boosterA.setAxialOffset(Position.BOTTOM, 0.0); + Stage boosterB = createBooster(); + boosterB.setName("Booster B Stage"); + core.addChild(boosterB); + boosterB.setAxialOffset(Position.BOTTOM, 0); + + + int expectedStageNumber = 0; + int actualStageNumber = sustainer.getStageNumber(); + assertEquals(" init order error: sustainer: resultant positions: ", expectedStageNumber, actualStageNumber); + + expectedStageNumber = 1; + actualStageNumber = core.getStageNumber(); + assertEquals(" init order error: core: resultant positions: ", expectedStageNumber, actualStageNumber); + + expectedStageNumber = 2; + actualStageNumber = boosterA.getStageNumber(); + assertEquals(" init order error: Booster A: resultant positions: ", expectedStageNumber, actualStageNumber); + + expectedStageNumber = 3; + actualStageNumber = boosterB.getStageNumber(); + assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber); + + } + + + + } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/StageSelectModel.java b/swing/src/net/sf/openrocket/gui/adaptors/StageSelectModel.java deleted file mode 100644 index d6931bdce..000000000 --- a/swing/src/net/sf/openrocket/gui/adaptors/StageSelectModel.java +++ /dev/null @@ -1,130 +0,0 @@ -package net.sf.openrocket.gui.adaptors; - -import java.util.EventObject; -import java.util.Iterator; - -import javax.swing.AbstractListModel; -import javax.swing.ComboBoxModel; -import javax.swing.event.ListDataListener; - -import org.jfree.util.Log; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.util.ArrayList; -import net.sf.openrocket.util.ChangeSource; -import net.sf.openrocket.util.Reflection; -import net.sf.openrocket.util.StateChangeListener; - -public class StageSelectModel extends AbstractListModel implements ComboBoxModel, StateChangeListener { - private static final long serialVersionUID = 1311302134934033684L; - private static final Logger log = LoggerFactory.getLogger(StageSelectModel.class); - -// protected final String nullText; - - protected Stage sourceStage = null; - protected ArrayList displayValues = new ArrayList(); - protected Stage selectedStage = null; - - //@SuppressWarnings("unchecked") - public StageSelectModel( final Stage _stage) { - this.sourceStage = _stage; -// this.nullText = nullText; - - populateDisplayValues(); - - stateChanged(null); // Update current value - this.sourceStage.addChangeListener(this); - } - - private void populateDisplayValues(){ - Rocket rocket = this.sourceStage.getRocket(); - - this.displayValues.clear(); - Iterator stageIter = rocket.getChildren().iterator(); - while( stageIter.hasNext() ){ - RocketComponent curComp = stageIter.next(); - if( curComp instanceof Stage ){ - Stage curStage = (Stage)curComp; - if( curStage.equals( this.sourceStage )){ - continue; - }else{ - displayValues.add( curStage ); - } - }else{ - throw new IllegalStateException("Rocket has a child which is something other than a Stage: "+curComp.getClass().getCanonicalName()+"(called: "+curComp.getName()+")"); - } - - } - - } - - @Override - public int getSize() { - return this.displayValues.size(); - } - - @Override - public Stage getElementAt(int index) { - return this.displayValues.get(index); - } - - @Override - public void setSelectedItem(Object newItem) { - if (newItem == null) { - // Clear selection - huh? - return; - } - - if (newItem instanceof String) { - log.error("setStage to string? huh? (unexpected value type"); - return; - } - - if( newItem instanceof Stage ){ - Stage nextStage = (Stage) newItem; - - if (nextStage.equals(this.selectedStage)){ - return; // i.e. no change - } - - this.selectedStage = nextStage; - this.sourceStage.setRelativeToStage(nextStage.getStageNumber()); - return; - } - - } - - @Override - public Stage getSelectedItem() { - return this.selectedStage; - //return "StageSelectModel["+this.selectedIndex+": "+this.displayValues.get(this.selectedIndex).getName()+"]"; - } - - @Override - public void stateChanged(EventObject eo) { - if( null == this.sourceStage){ - return; - } - Rocket rkt = sourceStage.getRocket(); - int sourceRelToIndex = this.sourceStage.getRelativeToStage(); - int selectedStageIndex = -1; - if( null != this.selectedStage ){ - selectedStageIndex = this.selectedStage.getStageNumber(); - } - if ( selectedStageIndex != sourceRelToIndex){ - this.selectedStage = (Stage)rkt.getChild(sourceRelToIndex); - } - } - - @Override - public String toString() { - return "StageSelectModel["+this.selectedStage.getName()+" ("+this.selectedStage.getStageNumber()+")]"; - } - - - -} diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java index 91a1a751f..d881f9ac9 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java @@ -256,7 +256,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { if (!rings.isEmpty()) { FinSet.TabRelativePosition temp = (FinSet.TabRelativePosition) em.getSelectedItem(); em.setSelectedItem(FinSet.TabRelativePosition.FRONT); - double len = computeFinTabLength(rings, component.asPositionValue(RocketComponent.Position.TOP, parent), + double len = computeFinTabLength(rings, component.asPositionValue(RocketComponent.Position.TOP), component.getLength(), mts, parent); mtl.setValue(len); //Be nice to the user and set the tab relative position enum back the way they had it. @@ -305,8 +305,8 @@ public abstract class FinSetConfig extends RocketComponentConfig { Collections.sort(rings, new Comparator() { @Override public int compare(CenteringRing centeringRing, CenteringRing centeringRing1) { - return (int) (1000d * (centeringRing.asPositionValue(RocketComponent.Position.TOP, relativeTo) - - centeringRing1.asPositionValue(RocketComponent.Position.TOP, relativeTo))); + return (int) (1000d * (centeringRing.asPositionValue(RocketComponent.Position.TOP) - + centeringRing1.asPositionValue(RocketComponent.Position.TOP))); } }); @@ -315,7 +315,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { //Handle centering rings that overlap or are adjacent by synthetically merging them into one virtual ring. if (!positionsFromTop.isEmpty() && positionsFromTop.get(positionsFromTop.size() - 1).bottomSidePositionFromTop() >= - centeringRing.asPositionValue(RocketComponent.Position.TOP, relativeTo)) { + centeringRing.asPositionValue(RocketComponent.Position.TOP)) { SortableRing adjacent = positionsFromTop.get(positionsFromTop.size() - 1); adjacent.merge(centeringRing, relativeTo); } else { @@ -440,7 +440,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { */ SortableRing(CenteringRing r, RocketComponent relativeTo) { thickness = r.getLength(); - positionFromTop = r.asPositionValue(RocketComponent.Position.TOP, relativeTo); + positionFromTop = r.asPositionValue(RocketComponent.Position.TOP); } /** @@ -449,7 +449,7 @@ public abstract class FinSetConfig extends RocketComponentConfig { * @param adjacent the adjacent ring */ public void merge(CenteringRing adjacent, RocketComponent relativeTo) { - double v = adjacent.asPositionValue(RocketComponent.Position.TOP, relativeTo); + double v = adjacent.asPositionValue(RocketComponent.Position.TOP); if (positionFromTop < v) { thickness = (v + adjacent.getLength()) - positionFromTop; } else { diff --git a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java index 0540c6b36..cb9328ed4 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java @@ -1,11 +1,6 @@ package net.sf.openrocket.gui.configdialog; -import java.awt.Component; -import java.awt.Container; -import java.util.List; - import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -13,9 +8,6 @@ import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.SpinnerEditor; @@ -23,8 +15,6 @@ import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.IntegerModel; -import net.sf.openrocket.gui.adaptors.StageSelectModel; -import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.StyledLabel.Style; @@ -35,7 +25,6 @@ import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.ChangeSource; public class StageConfig extends RocketComponentConfig { private static final Translator trans = Application.getTranslator(); @@ -60,13 +49,6 @@ public class StageConfig extends RocketComponentConfig { private JPanel parallelTab( final Stage stage ){ JPanel motherPanel = new JPanel( new MigLayout("fill")); - // this stage is positioned relative to what stage? - JLabel relativeStageLabel = new JLabel(trans.get("Stage.parallel.componentname")); - motherPanel.add( relativeStageLabel); - ComboBoxModel relativeStageModel = new StageSelectModel( stage ); - JComboBox relToCombo = new JComboBox( relativeStageModel ); - motherPanel.add( relToCombo , "growx, wrap"); - // enable parallel staging motherPanel.add(new JSeparator(SwingConstants.HORIZONTAL), "spanx 3, growx, wrap"); BooleanModel parallelEnabledModel = new BooleanModel( component, "Outside"); @@ -146,6 +128,8 @@ public class StageConfig extends RocketComponentConfig { motherPanel.add(axialOffsetUnitSelector, "growx 1, wrap"); parallelEnabledModel.addEnableComponent( axialOffsetUnitSelector , true); + System.err.println(stage.getRocket().toDebugTree()); + return motherPanel; } diff --git a/swing/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java b/swing/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java index e1f69e140..cc78b8a24 100644 --- a/swing/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java +++ b/swing/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java @@ -86,8 +86,8 @@ public class CenteringRingStrategy extends AbstractPrintStrategy { * @return true if the two physically intersect, from which we infer that the centering ring supports the tube */ private boolean overlaps(CenteringRing one, InnerTube two) { - final double crTopPosition = one.asPositionValue(RocketComponent.Position.ABSOLUTE, one.getParent()); - final double mmTopPosition = two.asPositionValue(RocketComponent.Position.ABSOLUTE, two.getParent()); + final double crTopPosition = one.asPositionValue(RocketComponent.Position.ABSOLUTE); + final double mmTopPosition = two.asPositionValue(RocketComponent.Position.ABSOLUTE); final double crBottomPosition = one.getLength() + crTopPosition; final double mmBottomPosition = two.getLength() + mmTopPosition; diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java index f8e68304c..fe185712e 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.rocketfigure; +import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Transformation; @@ -21,7 +22,6 @@ public class BodyTubeShapes extends RocketComponentShape { Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )}; instanceOffsets = component.shiftCoordinates(instanceOffsets); -// System.err.println(">> Starting component "+component.getName()+" at: "+(instanceOffsets[0].x - length/2)); Shape[] s = new Shape[instanceOffsets.length]; for (int i=0; i < instanceOffsets.length; i++) { s[i] = new Rectangle2D.Double((instanceOffsets[i].x-length/2)*S, //x - the X coordinate of the upper-left corner of the newly constructed Rectangle2D diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java index 63838d8db..5769c1823 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java @@ -22,7 +22,7 @@ public class ShockCordShapes extends RocketComponentShape { double radius = massObj.getRadius(); double arc = Math.min(length, 2*radius) * 0.7; - Shape[] s = new Shape[0]; + Shape[] s = new Shape[1]; Coordinate start = componentAbsoluteLocation; s[0] = new RoundRectangle2D.Double((start.x-radius)*S,(start.y-radius)*S, length*S,2*radius*S,arc*S,arc*S); @@ -47,7 +47,7 @@ public class ShockCordShapes extends RocketComponentShape { double or = tube.getRadius(); - Shape[] s = new Shape[0]; + Shape[] s = new Shape[1]; Coordinate start = componentAbsoluteLocation; s[0] = new Ellipse2D.Double((start.z-or)*S,(start.y-or)*S,2*or*S,2*or*S); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java index c369c8d3d..d36024f0d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java @@ -22,7 +22,7 @@ public class StreamerShapes extends RocketComponentShape { double radius = massObj.getRadius(); double arc = Math.min(length, 2*radius) * 0.7; - Shape[] s = new Shape[0]; + Shape[] s = new Shape[1]; Coordinate center = componentAbsoluteLocation; s[0] = new RoundRectangle2D.Double((center.x-radius)*S,(center.y-radius)*S, length*S,2*radius*S,arc*S,arc*S); @@ -45,7 +45,7 @@ public class StreamerShapes extends RocketComponentShape { net.sf.openrocket.rocketcomponent.MassObject tube = (net.sf.openrocket.rocketcomponent.MassObject)component; double or = tube.getRadius(); - Shape[] s = new Shape[0]; + Shape[] s = new Shape[1]; Coordinate center = componentAbsoluteLocation; s[0] = new Ellipse2D.Double((center.z-or)*S,(center.y-or)*S,2*or*S,2*or*S); diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index deb8a6551..be5cb96d5 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -23,9 +23,11 @@ import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.OutsideComponent; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; @@ -442,27 +444,50 @@ public class RocketFigure extends AbstractScaleFigure { // Coordinate componentRelativeLocation = comp.getRelativePositionVector(); Coordinate componentAbsoluteLocation = parentOffset.add(comp.getRelativePositionVector()); - //System.err.println(">> Drawing component "+comp.getName()+" at relloc: "+componentAbsoluteLocation); - if( ( comp instanceof Rocket)||( comp instanceof Stage )){ - // these components don't have any shapes to generate / get - // No-Op + // generate shapes: + if( comp instanceof Rocket){ + // no-op. no shapes + }else if( comp instanceof Stage ){ + // no-op; no shapes here, either. }else{ -// if( comp instanceof FinSet ){ -// System.err.println(">> Drawing component "+comp.getName()+" at absloc: "+componentAbsoluteLocation); -// System.err.println(" (parent was at: "+parentOffset); -// } + // get all shapes for this component, add to return list. RocketComponentShape[] childShapes = getThisShape( viewType, comp, componentAbsoluteLocation, viewTransform); - for ( RocketComponentShape curShape : childShapes ){ allShapes.add( curShape ); } } - - // recurse to each child - for( RocketComponent child: comp.getChildren() ){ - getShapeTree( allShapes, child, componentAbsoluteLocation); - } + // recurse differently, depending on if this node has instances or not.... + if( comp.isCenterline() ){ + // recurse to each child with just the center + for( RocketComponent child: comp.getChildren() ){ + getShapeTree( allShapes, child, componentAbsoluteLocation); + } + + }else{ + + // DEBUG -- for external stages.... + System.err.println(">> Drawing pStage: "+comp.getName()+" at absloc: "+componentAbsoluteLocation); + Stage testStage = (Stage)comp; + // System.err.println(">> Starting component "+component.getName()+" at: "+(instanceOffsets[0])); + + // recurse to each child with each instance of this component + OutsideComponent outer = (OutsideComponent)comp; +// int instanceCount = outer.getInstanceCount(); + + // get the offsets for m instances + Coordinate[] instanceOffsets = new Coordinate[]{ componentAbsoluteLocation }; + instanceOffsets = comp.shiftCoordinates( instanceOffsets); + + // recurse to each child with each offset + for( RocketComponent child: comp.getChildren() ){ + for( Coordinate curInstanceCoordinate : instanceOffsets){ + getShapeTree( allShapes, child, curInstanceCoordinate); + } + } + + } + return; }