diff --git a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java index 6a9b9ed13..007f3a296 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -311,7 +311,8 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial @Override public Collection getComponentBounds() { Collection bounds = new ArrayList(8); - Coordinate ref = this.getAbsolutePositionVector(); + // not exact, but should *usually* give the right bounds + Coordinate ref = this.getLocation()[0]; double r = getOuterRadius(); addBound(bounds, ref.x, r); addBound(bounds, ref.x + length, r); diff --git a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java index f01c8a646..93d039372 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java @@ -130,6 +130,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi public int[] getActiveStages() { // temporary hack fix //int stageCount = Stage.getStageCount(); + int stageCount = getRocket().getChildCount(); List active = new ArrayList(); int[] ret; diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 548beb572..8de1142ca 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -621,7 +621,7 @@ public abstract class FinSet extends ExternalComponent { @Override public Collection getComponentBounds() { List bounds = new ArrayList(); - double refx = this.getAbsolutePositionVector().x; + double refx = this.getLocation()[0].x; double r = getBodyRadius(); for (Coordinate point : getFinPoints()) { diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index a0c64a3e3..ab9d5f681 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -196,7 +196,23 @@ public class Rocket extends RocketComponent { return functionalModID; } + public ArrayList getStageList() { + ArrayList toReturn = new ArrayList(); + + toReturn = Rocket.getStages(toReturn, this); + + return toReturn; + } + private static ArrayList getStages(ArrayList accumulator, final RocketComponent parent) { + for (RocketComponent curChild : parent.getChildren()) { + if (curChild instanceof Stage) { + Stage curStage = (Stage) curChild; + accumulator.add(curStage); + } + } + return accumulator; // technically redundant, btw. + } public ReferenceType getReferenceType() { diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 7b5542b9e..20b3a1112 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -100,7 +100,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab /** * Positioning of this component relative to the parent component. */ - protected Position relativePosition; + protected Position relativePosition = Position.AFTER; /** * Offset of the position of this component relative to the normal position given by @@ -913,7 +913,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab result = thisX - relativeLength; break; case ABSOLUTE: - result = this.getAbsolutePositionVector().x; + Coordinate[] insts = this.getLocation(); + if (1 < insts.length) { + return Double.NaN; + } + result = insts[0].x; break; case TOP: result = thisX; @@ -941,7 +945,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return this.getAxialOffset(); } - public double getAxialOffset() { mutex.verify(); return this.asPositionValue(this.relativePosition); @@ -1008,7 +1011,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } else { refLength = referenceComponent.getLength(); - double refRelX = referenceComponent.getRelativePositionVector().x; + double refRelX = referenceComponent.getOffset().x; newAxialPosition = refRelX + refLength; } @@ -1065,16 +1068,37 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.setAxialOffset(this.relativePosition, this.offset); } - public Coordinate getRelativePositionVector() { + public Coordinate getOffset() { return this.position; } - public Coordinate getAbsolutePositionVector() { + + /** + * @deprecated kept around as example code. instead use + * @return + */ + private Coordinate getAbsoluteVector() { if (null == this.parent) { // == improperly initialized components OR the root Rocket instance - return new Coordinate(); + return Coordinate.ZERO; } else { - return this.parent.getAbsolutePositionVector().add(this.getRelativePositionVector()); + return this.getAbsoluteVector().add(this.getOffset()); + } + } + + public Coordinate[] getLocation() { + if (null == this.parent) { + // == improperly initialized components OR the root Rocket instance + return new Coordinate[] { Coordinate.ZERO }; + } else { + Coordinate[] parentPositions = this.parent.getLocation(); + int instCount = parentPositions.length; + Coordinate[] thesePositions = new Coordinate[instCount]; + + for (int pi = 0; pi < instCount; pi++) { + thesePositions[pi] = parentPositions[pi].add(this.getOffset()); + } + return thesePositions; } } @@ -1090,9 +1114,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public Coordinate[] toAbsolute(Coordinate c) { checkState(); + final String lockText = "toAbsolute"; + mutex.lock(lockText); + Coordinate[] thesePositions = this.getLocation(); - Coordinate absCoord = this.getAbsolutePositionVector().add(c); - return new Coordinate[] { absCoord }; + final int instanceCount = this.getInstanceCount(); + Coordinate[] toReturn = new Coordinate[instanceCount]; + for (int coordIndex = 0; coordIndex < instanceCount; coordIndex++) { + toReturn[coordIndex] = thesePositions[coordIndex].add(c); + } + + mutex.unlock(lockText); + return toReturn; } // public Coordinate[] toAbsolute(final Coordinate[] toMove) { @@ -1116,13 +1149,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab *

* The current implementation does not support rotating components. * - * @deprecated to test alternate interface... * @param c Coordinate in the component's coordinate system. * @param dest Destination component coordinate system. * @return an array of coordinates describing c in coordinates * relative to dest. */ - @Deprecated public final Coordinate[] toRelative(Coordinate c, RocketComponent dest) { if (null == dest) { throw new BugException("calling toRelative(c,null) is being refactored. "); @@ -1131,33 +1162,29 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); mutex.lock("toRelative"); - final Coordinate sourceLoc = this.getAbsolutePositionVector(); - final Coordinate destLoc = dest.getAbsolutePositionVector(); - Coordinate newCoord = c.add(sourceLoc).sub(destLoc); - Coordinate[] toReturn = new Coordinate[] { newCoord }; + // not sure if this will give us an answer, or THE answer... + //final Coordinate sourceLoc = this.getLocation()[0]; + final Coordinate[] destLocs = dest.getLocation(); + Coordinate[] toReturn = new Coordinate[destLocs.length]; + for (int coordIndex = 0; coordIndex < dest.getInstanceCount(); coordIndex++) { + toReturn[coordIndex] = this.getLocation()[0].add(c).sub(destLocs[coordIndex]); + } mutex.unlock("toRelative"); return toReturn; } - /* - * @deprecated ? is this used by anything? - */ protected static final Coordinate[] rebase(final Coordinate toMove[], final Coordinate source, final Coordinate dest) { - if ((null == toMove) || (null == source) || (null == dest)) { - throw new NullPointerException("rebase with any null pointer is out-of-spec."); - } - + final Coordinate delta = source.sub(dest); Coordinate[] toReturn = new Coordinate[toMove.length]; - - Coordinate translation = source.sub(dest); for (int coordIndex = 0; coordIndex < toMove.length; coordIndex++) { - toReturn[coordIndex] = toMove[coordIndex].add(translation); + toReturn[coordIndex] = toMove[coordIndex].add(delta); } return toReturn; } + /** * Iteratively sum the lengths of all subcomponents that have position * Position.AFTER. @@ -2047,10 +2074,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } public void toDebugTreeNode(final StringBuilder buffer, final String prefix) { - buffer.append(String.format("%s %-24s %5.3f %24f %24f\n", prefix, this.getName(), this.getLength(), - this.getRelativePositionVector().x, this.getAbsolutePositionVector().x)); - // buffer.append(String.format("%s %-24s %5.3f %24s %24s\n", prefix, this.getName(), this.getLength(), - // this.getRelativePositionVector(), this.getAbsolutePositionVector())); + buffer.append(String.format("%s %-24s %5.3f %24s %24s\n", prefix, this.getName(), this.getLength(), + this.getOffset(), this.getLocation()[0])); } public void dumpTreeHelper(StringBuilder buffer, final String prefix) { diff --git a/core/src/net/sf/openrocket/rocketcomponent/Stage.java b/core/src/net/sf/openrocket/rocketcomponent/Stage.java index d1e855d0a..26fd1b92e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -61,17 +61,26 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon @Override public Collection getComponentBounds() { Collection bounds = new ArrayList(8); - final double WAG_FACTOR = 1.05; - Coordinate center = this.getAbsolutePositionVector(); - double startx = center.x - this.length / 2; - double endx = center.x + this.length / 2; - double r = this.getRadialOffset() * WAG_FACTOR; + final double WAG_FACTOR = 1.1; + double x_min = Double.MAX_VALUE; + double x_max = Double.MIN_VALUE; + double r_max = 0; - if (!this.isCenterline()) { - System.err.println(">> .getComponentBounds(): r=" + r); + Coordinate[] instanceLocations = this.getLocation(); + + for (Coordinate currentInstanceLocation : instanceLocations) { + if (x_min > (currentInstanceLocation.x)) { + x_min = currentInstanceLocation.x; + } + if (x_max < (currentInstanceLocation.x + this.length)) { + x_max = currentInstanceLocation.x + this.length; + } + if (r_max < (this.getRadialOffset() * WAG_FACTOR)) { + r_max = this.getRadialOffset() * WAG_FACTOR; + } } - addBound(bounds, startx, r); - addBound(bounds, endx, r); + addBound(bounds, x_min, r_max); + addBound(bounds, x_max, r_max); return bounds; } @@ -106,6 +115,27 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon return copy; } + @Override + public Coordinate[] getLocation() { + if (null == this.parent) { + throw new BugException(" Attempted to get absolute position Vector of a Stage without a parent. "); + } + + if (this.isCenterline()) { + return super.getLocation(); + } else { + Coordinate[] parentInstances = this.parent.getLocation(); + if (1 != parentInstances.length) { + throw new BugException(" OpenRocket does not (yet) support external stages attached to external stages. " + + "(assumed reason for getting multiple parent locations into an external stage.)"); + } + + Coordinate[] toReturn = this.shiftCoordinates(parentInstances); + + return toReturn; + } + + } @Override public boolean getOutside() { @@ -151,6 +181,10 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon if (this.centerline) { return; } + if (_count < 1) { + // there must be at least one instance.... + return; + } this.count = _count; this.angularSeparation = Math.PI * 2 / this.count; @@ -209,13 +243,14 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon super.setRelativePosition(_newPosition); } } + fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE); } @Override public double getPositionValue() { mutex.verify(); - return getAxialOffset(); + return this.getAxialOffset(); } /* @@ -294,11 +329,12 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon double radius = this.radialPosition_m; double angle0 = this.angularPosition_rad; double angleIncr = this.angularSeparation; + Coordinate center = this.position; Coordinate[] toReturn = new Coordinate[this.count]; Coordinate thisOffset; double thisAngle = angle0; for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) { - thisOffset = new Coordinate(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle)); + thisOffset = center.add(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle)); toReturn[instanceNumber] = thisOffset.add(c[0]); thisAngle += angleIncr; @@ -326,26 +362,25 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon String thisLabel = this.getName() + " (" + this.getStageNumber() + ")"; - buffer.append(String.format("%s %-24s %5.3f %24s %24s", prefix, thisLabel, this.getLength(), - this.getRelativePositionVector(), this.getAbsolutePositionVector())); + buffer.append(String.format("%s %-24s %5.3f", prefix, thisLabel, this.getLength())); if (this.isCenterline()) { - buffer.append("\n"); + buffer.append(String.format(" %24s %24s\n", this.getOffset(), this.getLocation()[0])); } 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); + buffer.append(String.format(" %4.1f via: %s \n", this.getAxialOffset(), this.relativePosition.name())); + Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { Coordinate.ZERO }); + Coordinate[] absCoords = this.getLocation(); - 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)); + for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) { + Coordinate instanceRelativePosition = relCoords[instanceNumber]; + Coordinate instanceAbsolutePosition = absCoords[instanceNumber]; + buffer.append(String.format("%s [instance %2d of %2d] %32s %32s\n", prefix, instanceNumber, count, + instanceRelativePosition, instanceAbsolutePosition)); } } } - @Override public void updateBounds() { // currently only updates the length diff --git a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java index c34996e8f..e863c2a0e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java @@ -333,7 +333,7 @@ public class TubeFinSet extends ExternalComponent { s = this.getParent(); while (s != null) { if (s instanceof SymmetricComponent) { - double x = this.getRelativePositionVector().x; + double x = this.getOffset().x; return ((SymmetricComponent) s).getRadius(x); } s = s.getParent(); diff --git a/core/test/net/sf/openrocket/rocketcomponent/StageTest.java b/core/test/net/sf/openrocket/rocketcomponent/StageTest.java index 568bae930..1e18a2705 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/StageTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/StageTest.java @@ -98,7 +98,7 @@ public class StageTest extends BaseTestCase { expectedPosition = ZERO; targetPosition = new Coordinate(+4.0, 0.0, 0.0); rocket.setAxialOffset(targetPosition.x); - resultPosition = rocket.getRelativePositionVector(); + resultPosition = rocket.getOffset(); assertThat(" Moved the rocket rocket itself-- this should not be enabled.", expectedPosition.x, equalTo(resultPosition.x)); } @@ -126,23 +126,23 @@ public class StageTest extends BaseTestCase { assertThat(" createTestRocket failed: Sustainer size: ", sustainer.getLength(), equalTo(expectedSustainerLength)); double expectedSustainerX = 0; double sustainerX; - sustainerX = sustainer.getRelativePositionVector().x; + sustainerX = sustainer.getOffset().x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer Relative position: ", sustainerX, equalTo(expectedSustainerX)); - sustainerX = sustainer.getAbsolutePositionVector().x; + sustainerX = sustainer.getLocation()[0].x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer Absolute position: ", sustainerX, equalTo(expectedSustainerX)); double expectedSustainerNoseX = 0; - double sustainerNosePosition = sustainerNose.getRelativePositionVector().x; + double sustainerNosePosition = sustainerNose.getOffset().x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer Nose X position: ", sustainerNosePosition, equalTo(expectedSustainerNoseX)); expectedSustainerNoseX = 0; - sustainerNosePosition = sustainerNose.getAbsolutePositionVector().x; + sustainerNosePosition = sustainerNose.getLocation()[0].x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer Nose X position: ", sustainerNosePosition, equalTo(expectedSustainerNoseX)); double expectedSustainerBodyX = 2; - double sustainerBodyX = sustainerBody.getRelativePositionVector().x; + double sustainerBodyX = sustainerBody.getOffset().x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer body rel X position: ", sustainerBodyX, equalTo(expectedSustainerBodyX)); expectedSustainerBodyX = 2; - sustainerBodyX = sustainerBody.getAbsolutePositionVector().x; + sustainerBodyX = sustainerBody.getLocation()[0].x; assertThat(" createTestRocket failed:\n" + rocketTree + " sustainer body abs X position: ", sustainerBodyX, equalTo(expectedSustainerBodyX)); } @@ -166,37 +166,37 @@ public class StageTest extends BaseTestCase { int relToStage = core.getRelativeToStage(); assertThat(" createTestRocket failed:\n" + rocketTree + " core relative position: ", relToStage, equalTo(relToExpected)); - coreX = core.getRelativePositionVector().x; + coreX = core.getOffset().x; assertThat(" createTestRocket failed:\n" + rocketTree + " core Relative position: ", coreX, equalTo(expectedCoreX)); - coreX = core.getAbsolutePositionVector().x; + coreX = core.getLocation()[0].x; assertThat(" createTestRocket failed:\n" + rocketTree + " core Absolute position: ", coreX, equalTo(expectedCoreX)); RocketComponent coreUpperBody = core.getChild(0); double expectedX = 0; - double resultantX = coreUpperBody.getRelativePositionVector().x; + double resultantX = coreUpperBody.getOffset().x; assertThat(" createTestRocket failed:\n" + rocketTree + " core body rel X: ", resultantX, equalTo(expectedX)); expectedX = expectedCoreX; - resultantX = coreUpperBody.getAbsolutePositionVector().x; + resultantX = coreUpperBody.getLocation()[0].x; assertThat(" createTestRocket failed:\n" + rocketTree + " core body abs X: ", resultantX, equalTo(expectedX)); RocketComponent coreLowerBody = core.getChild(1); expectedX = coreUpperBody.getLength(); - resultantX = coreLowerBody.getRelativePositionVector().x; + resultantX = coreLowerBody.getOffset().x; assertEquals(" createTestRocket failed:\n" + rocketTree + " core body rel X: ", expectedX, resultantX, EPSILON); expectedX = expectedCoreX + coreUpperBody.getLength(); - resultantX = coreLowerBody.getAbsolutePositionVector().x; + resultantX = coreLowerBody.getLocation()[0].x; assertEquals(" createTestRocket failed:\n" + rocketTree + " core body abs X: ", expectedX, resultantX, EPSILON); RocketComponent coreFins = coreLowerBody.getChild(0); // default is offset=0, method=0 expectedX = 0.2; - resultantX = coreFins.getRelativePositionVector().x; + resultantX = coreFins.getOffset().x; assertEquals(" createTestRocket failed:\n" + rocketTree + " core Fins rel X: ", expectedX, resultantX, EPSILON); // 5 + 1.8 + 4.2 = 11 // 11 - 4 = 7; expectedX = 7.0; - resultantX = coreFins.getAbsolutePositionVector().x; + resultantX = coreFins.getLocation()[0].x; assertEquals(" createTestRocket failed:\n" + rocketTree + " core Fins abs X: ", expectedX, resultantX, EPSILON); } @@ -220,10 +220,10 @@ public class StageTest extends BaseTestCase { // ^^ function under test String rocketTree = rocket.toDebugTree(); - Coordinate resultantRelativePosition = sustainer.getRelativePositionVector(); + Coordinate resultantRelativePosition = sustainer.getOffset(); assertThat(" 'setAxialPosition(double)' failed:\n" + rocketTree + " Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); // 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(); + Coordinate resultantAbsolutePosition = sustainer.getLocation()[0]; assertThat(" 'setAxialPosition(double)' failed:\n" + rocketTree + " Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedPosition.x)); } @@ -250,8 +250,8 @@ public class StageTest extends BaseTestCase { assertThat(" 'setInstancecount(int)' failed: ", instanceCount, equalTo(expectedInstanceCount)); double expectedAbsX = 6.0; - Coordinate resultantCenter = boosterSet.getAbsolutePositionVector(); - assertEquals(treeDump + "\n>>'setAxialOffset()' failed:\n" + treeDump + " absolute position", expectedAbsX, resultantCenter.x, EPSILON); + double resultantX = boosterSet.getLocation()[0].x; + assertEquals(">>'setAxialOffset()' failed:\n" + treeDump + " 1st Inst absolute position", expectedAbsX, resultantX, EPSILON); double expectedRadialOffset = 4.0; double radialOffset = boosterSet.getRadialOffset(); @@ -286,24 +286,24 @@ public class StageTest extends BaseTestCase { double angle = Math.PI * 2 / targetInstanceCount; double radius = targetRadialOffset; - Coordinate componentAbsolutePosition = boosterSet.getAbsolutePositionVector(); - Coordinate[] instanceCoords = new Coordinate[] { componentAbsolutePosition }; - instanceCoords = boosterSet.shiftCoordinates(instanceCoords); + Coordinate[] instanceAbsoluteCoords = boosterSet.getLocation(); + // Coordinate[] instanceRelativeCoords = new Coordinate[] { componentAbsolutePosition }; + // instanceRelativeCoords = boosterSet.shiftCoordinates(instanceRelativeCoords); 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); + Coordinate resultantPosition0 = instanceAbsoluteCoords[inst]; + assertThat(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", resultantPosition0, equalTo(expectedPosition0)); 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); + Coordinate resultantPosition1 = instanceAbsoluteCoords[inst]; + assertThat(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", resultantPosition1, equalTo(expectedPosition1)); 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); + Coordinate resultantPosition2 = instanceAbsoluteCoords[inst]; + assertThat(treeDump + "\n>> Failed to generate Parallel Stage instances correctly: ", resultantPosition2, equalTo(expectedPosition2)); } @@ -318,7 +318,7 @@ public class StageTest extends BaseTestCase { core.addChild(booster); double targetX = +17.0; - double expectedX = targetX - core.getAbsolutePositionVector().x; + double expectedX = targetX - core.getLocation()[0].x; // when subStages should be freely movable // vv function under test @@ -326,14 +326,14 @@ public class StageTest extends BaseTestCase { // ^^ function under test String treeDump = rocket.toDebugTree(); - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); + Coordinate resultantRelativePosition = booster.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); double resultantPositionValue = booster.getPositionValue(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " PositionValue: ", resultantPositionValue, equalTo(targetX)); double resultantAxialPosition = booster.getAxialOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " 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(); + Coordinate resultantAbsolutePosition = booster.getLocation()[0]; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Absolute position: ", resultantAbsolutePosition.x, equalTo(targetX)); } @@ -359,7 +359,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedX = 0; - Coordinate resultantRelativePosition = sustainer.getRelativePositionVector(); + Coordinate resultantRelativePosition = sustainer.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Sustainer Relative position: ", resultantRelativePosition.x, equalTo(expectedX)); double expectedPositionValue = 0; double resultantPositionValue = sustainer.getPositionValue(); @@ -369,7 +369,7 @@ public class StageTest extends BaseTestCase { double resultantAxialOffset = sustainer.getAxialOffset(); assertThat(" 'getAxialPosition()' failed: \n" + treeDump + " Relative position: ", resultantAxialOffset, equalTo(expectedAxialOffset)); // 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(); + Coordinate resultantAbsolutePosition = sustainer.getLocation()[0]; assertThat(" 'setAbsolutePositionVector()' failed: \n" + treeDump + " Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); } @@ -389,10 +389,10 @@ public class StageTest extends BaseTestCase { double expectedRelativeX = 2; double expectedAbsoluteX = 7; - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); + Coordinate resultantRelativePosition = booster.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " 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(); + Coordinate resultantAbsolutePosition = booster.getLocation()[0]; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantAxialOffset = booster.getAxialOffset(); @@ -419,10 +419,10 @@ public class StageTest extends BaseTestCase { double expectedRelativeX = 2.5; double expectedAbsoluteX = 7.5; - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); + Coordinate resultantRelativePosition = booster.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " 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(); + Coordinate resultantAbsolutePosition = booster.getLocation()[0]; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantPositionValue = booster.getPositionValue(); @@ -448,10 +448,10 @@ public class StageTest extends BaseTestCase { double expectedRelativeX = 5; double expectedAbsoluteX = +10; - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); + Coordinate resultantRelativePosition = booster.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " 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(); + Coordinate resultantAbsolutePosition = booster.getLocation()[0]; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedAbsoluteX)); double resultantPositionValue = booster.getPositionValue(); @@ -474,7 +474,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedRelativePositionX = targetOffset; - Coordinate resultantRelativePosition = booster.getRelativePositionVector(); + Coordinate resultantRelativePosition = booster.getOffset(); assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", resultantRelativePosition.x, equalTo(expectedRelativePositionX)); // vv function under test @@ -498,7 +498,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedRelativeX = targetOffset; - double resultantX = booster.getRelativePositionVector().x; + double resultantX = booster.getOffset().x; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", resultantX, equalTo(expectedRelativeX)); // vv function under test @@ -522,7 +522,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedRelativeX = targetOffset; - double resultantX = booster.getRelativePositionVector().x; + double resultantX = booster.getOffset().x; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", resultantX, equalTo(expectedRelativeX)); double resultantAxialPosition; @@ -548,7 +548,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedRelativeX = targetOffset; - double resultantX = booster.getRelativePositionVector().x; + double resultantX = booster.getOffset().x; assertThat(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", resultantX, equalTo(expectedRelativeX)); // vv function under test @@ -572,7 +572,7 @@ public class StageTest extends BaseTestCase { String treeDump = rocket.toDebugTree(); double expectedRelativeX = +5.5; - double resultantX = booster.getRelativePositionVector().x; + double resultantX = booster.getOffset().x; assertEquals(" 'setAxialPosition(double)' failed: \n" + treeDump + " Relative position: ", expectedRelativeX, resultantX, EPSILON); // vv function under test @@ -599,7 +599,7 @@ public class StageTest extends BaseTestCase { // 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 = 2.5; - double resultantOffset = booster.getRelativePositionVector().x; + double resultantOffset = booster.getOffset().x; assertEquals(" init order error: Booster: " + treeDumpBefore + " initial relative X: ", expectedRelativeX, resultantOffset, EPSILON); double expectedAxialOffset = targetOffset; resultantOffset = booster.getAxialOffset(); @@ -613,7 +613,7 @@ public class StageTest extends BaseTestCase { String treeDumpAfter = rocket.toDebugTree(); expectedRelativeX = 2.5; // no change - resultantOffset = booster.getRelativePositionVector().x; + resultantOffset = booster.getOffset().x; assertEquals(" init order error: Booster: " + treeDumpBefore + " =======> " + treeDumpAfter + " populated relative X: ", expectedRelativeX, resultantOffset, EPSILON); expectedAxialOffset = targetOffset; // again, no change resultantOffset = booster.getAxialOffset(); @@ -642,8 +642,8 @@ public class StageTest extends BaseTestCase { boosterB.setAxialOffset(targetOffset); String treeDump = rocket.toDebugTree(); - double resultantOffsetA = boosterA.getRelativePositionVector().x; - double resultantOffsetB = boosterB.getRelativePositionVector().x; + double resultantOffsetA = boosterA.getOffset().x; + double resultantOffsetB = boosterB.getOffset().x; assertEquals(" init order error: " + treeDump + " Booster A: resultant positions: ", expectedOffset, resultantOffsetA, EPSILON); assertEquals(" init order error: " + treeDump + " Booster B: resultant positions: ", expectedOffset, resultantOffsetB, EPSILON); @@ -684,6 +684,44 @@ public class StageTest extends BaseTestCase { } + @Test + public void testToAbsolute() { + Rocket rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); + String treeDump = rocket.toDebugTree(); + + Coordinate input = new Coordinate(3, 0, 0); + Coordinate[] actual = core.toAbsolute(input); + + double expectedX = 8; + assertEquals(treeDump + " coordinate transform through 'core.toAbsolute(c)' failed: ", expectedX, actual[0].x, EPSILON); + } + + @Test + public void testToRelative() { + Rocket rocket = createTestRocket(); + Stage core = (Stage) rocket.getChild(1); + RocketComponent ubody = core.getChild(0); + RocketComponent lbody = core.getChild(1); + + String treeDump = rocket.toDebugTree(); + + Coordinate input = new Coordinate(1, 0, 0); + Coordinate actual = core.toAbsolute(input)[0]; + + double expectedX = 6; + assertEquals(treeDump + " coordinate transform through 'core.toAbsolute(c)' failed: ", expectedX, actual.x, EPSILON); + + input = new Coordinate(1, 0, 0); + actual = ubody.toRelative(input, lbody)[0]; + + expectedX = -0.8; + assertEquals(treeDump + " coordinate transform through 'core.toAbsolute(c)' failed: ", expectedX, actual.x, EPSILON); + + + + } + } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java index cb9328ed4..edb43fb2b 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java @@ -107,8 +107,7 @@ public class StageConfig extends RocketComponentConfig { RocketComponent.Position.TOP, RocketComponent.Position.MIDDLE, RocketComponent.Position.BOTTOM, - RocketComponent.Position.ABSOLUTE, - RocketComponent.Position.AFTER + RocketComponent.Position.ABSOLUTE }); JComboBox positionMethodCombo = new JComboBox( relativePositionMethodModel ); motherPanel.add(positionMethodCombo, "spanx 2, growx, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java index 348078d81..d3c2fbd52 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java @@ -24,6 +24,9 @@ public class RocketComponentShape { final public LineStyle lineStyle; final public RocketComponent component; + //fillColor); + //borderColor); + protected RocketComponentShape(){ this.hasShape = false; this.shape = null; diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 09049bd16..846a1eaa4 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -349,35 +349,41 @@ public class RocketFigure extends AbstractScaleFigure { Motor motor = mount.getMotor(motorID); double motorLength = motor.getLength(); double motorRadius = motor.getDiameter() / 2; - + + // Steps for instancing: + // 1) mountComponent has an instanced ancestor: + // 2) which may be an arbitrary number of levels up, so calling mount.parent.getInstances is not enough. + // 3) therefore .getLocation() will return all the instances of this owning component + // 4) Then, for each instance of the component, draw each cluster. RocketComponent mountComponent = ((RocketComponent) mount); - Coordinate mountPosition = mountComponent.getAbsolutePositionVector(); + Coordinate[] mountLocations = mountComponent.getLocation(); + + //Coordinate curInstancePosition = mountLocations[0]; // placeholder double mountLength = mountComponent.getLength(); - - Coordinate[] motorPositions; - Coordinate[] clusterTop = new Coordinate[]{mountPosition.add( mountLength - motorLength + mount.getMotorOverhang() , 0, 0)}; - - motorPositions = mountComponent.shiftCoordinates(clusterTop); - - for (int i = 0; i < motorPositions.length; i++) { - motorPositions[i] = transformation.transform(motorPositions[i]); - } - - for (Coordinate coord : motorPositions) { - Shape s; - if (currentViewType == RocketPanel.VIEW_TYPE.SideView) { - s = new Rectangle2D.Double(EXTRA_SCALE * coord.x, - EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * motorLength, - EXTRA_SCALE * 2 * motorRadius); - } else { - s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - motorRadius), - EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * 2 * motorRadius, - EXTRA_SCALE * 2 * motorRadius); + for ( Coordinate curInstanceLocation : mountLocations ){ + Coordinate[] motorPositions; + Coordinate[] clusterCenterTop = new Coordinate[]{ curInstanceLocation.add( mountLength - motorLength + mount.getMotorOverhang(), 0, 0)}; + motorPositions = mountComponent.shiftCoordinates(clusterCenterTop); + for (int i = 0; i < motorPositions.length; i++) { + motorPositions[i] = transformation.transform(motorPositions[i]); + } + + for (Coordinate coord : motorPositions) { + Shape s; + if (currentViewType == RocketPanel.VIEW_TYPE.SideView) { + s = new Rectangle2D.Double(EXTRA_SCALE * coord.x, + EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * motorLength, + EXTRA_SCALE * 2 * motorRadius); + } else { + s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - motorRadius), + EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * 2 * motorRadius, + EXTRA_SCALE * 2 * motorRadius); + } + g2.setColor(fillColor); + g2.fill(s); + g2.setColor(borderColor); + g2.draw(s); } - g2.setColor(fillColor); - g2.fill(s); - g2.setColor(borderColor); - g2.draw(s); } } @@ -432,14 +438,13 @@ public class RocketFigure extends AbstractScaleFigure { private void getShapeTree( ArrayList allShapes, // this is the output parameter final RocketComponent comp, - final Coordinate parentOffset){ + final Coordinate parentLocation){ RocketPanel.VIEW_TYPE viewType = this.currentViewType; Transformation viewTransform = this.transformation; -// Coordinate componentRelativeLocation = comp.getRelativePositionVector(); - Coordinate componentAbsoluteLocation = parentOffset.add(comp.getRelativePositionVector()); - + Coordinate componentAbsoluteLocation = parentLocation.add(comp.getOffset()); + // generate shapes: if( comp instanceof Rocket){ // no-op. no shapes @@ -461,9 +466,9 @@ public class RocketFigure extends AbstractScaleFigure { } }else{ // get the offsets for each component instance - Coordinate[] instanceOffsets = new Coordinate[]{ componentAbsoluteLocation }; + Coordinate[] instanceOffsets = new Coordinate[]{ parentLocation }; instanceOffsets = comp.shiftCoordinates( instanceOffsets); - + // recurse to each child with each instance of this component for( RocketComponent child: comp.getChildren() ){ for( Coordinate curInstanceCoordinate : instanceOffsets){