diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 55b9987c0..3bcbb0c0d 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -825,12 +825,6 @@ RocketCompCfg.tab.Override = Override RocketCompCfg.tab.MassandCGoverride = Mass and CG override options RocketCompCfg.tab.Parallel = Parallel RocketCompCfg.tab.ParallelComment = Options for locating stages parallel to other stages -RocketCompCfg.outside.stage = Make this Stage Parallel -RocketCompCfg.outside.radius = Radial Distance -RocketCompCfg.outside.angle = Angle -RocketCompCfg.outside.count = Number of Boosters -RocketCompCfg.outside.rotation = Rotation -RocketCompCfg.outside.componentname = Name of parent component RocketCompCfg.tab.Figure = Figure RocketCompCfg.tab.Figstyleopt = Figure style options RocketCompCfg.tab.Comment = Comment @@ -1388,6 +1382,14 @@ Stage.SeparationEvent.EJECTION = Current stage ejection charge Stage.SeparationEvent.LAUNCH = Launch Stage.SeparationEvent.NEVER = Never +Stage.parallel.toggle = Make this Stage Parallel +Stage.parallel.radius = Radial Distance +Stage.parallel.angle = Angle +Stage.parallel.count = Number of Boosters +Stage.parallel.rotation = Rotation +Stage.parallel.componentname = Relative to Stage +Stage.parallel.offset = Offset Value + ! BodyTube BodyTube.BodyTube = Body tube ! TubeCoupler diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java index dab8098b0..508604a16 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java @@ -400,7 +400,7 @@ public class FinSetCalc extends RocketComponentCalc { double y = i * dy; macLength += length * length; - logger.debug("macLength = {}, length = {}, i = {}", macLength, length, i); + //logger.debug("macLength = {}, length = {}, i = {}", macLength, length, i); macSpan += y * length; macLead += chordLead[i] * length; area += length; @@ -416,7 +416,7 @@ public class FinSetCalc extends RocketComponentCalc { } macLength *= dy; - logger.debug("macLength = {}", macLength); + //logger.debug("macLength = {}", macLength); macSpan *= dy; macLead *= dy; area *= dy; diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java index 5740faf50..76f51942f 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java @@ -408,9 +408,9 @@ class DocumentConfig { Reflection.findMethod(StageSeparationConfiguration.class, "setSeparationDelay", double.class))); setters.put("Stage:outside", new BooleanSetter(Reflection.findMethod(Stage.class, "setOutside", boolean.class))); setters.put("Stage:relativeto", new IntSetter(Reflection.findMethod(Stage.class, "setRelativeToStage", int.class))); - setters.put("Stage:instancecount", new IntSetter(Reflection.findMethod(Stage.class, "setCount", int.class))); - setters.put("Stage:radialoffset", new DoubleSetter(Reflection.findMethod(Stage.class, "setRadialPosition", double.class))); - setters.put("Stage:angleoffset", new DoubleSetter(Reflection.findMethod(Stage.class, "setAngularPosition", double.class))); + setters.put("Stage:instancecount", new IntSetter(Reflection.findMethod(Stage.class, "setInstanceCount", int.class))); + setters.put("Stage:radialoffset", new DoubleSetter(Reflection.findMethod(Stage.class, "setRadialOffset", double.class))); + setters.put("Stage:angleoffset", new DoubleSetter(Reflection.findMethod(Stage.class, "setAngularOffset", double.class))); } diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java index f976fe38e..77c689a60 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/PositionSetter.java @@ -35,19 +35,19 @@ class PositionSetter implements Setter { if (c instanceof FinSet) { ((FinSet) c).setRelativePosition(type); - c.setPositionValue(pos); + c.setAxialOffset(pos); } else if (c instanceof LaunchLug) { ((LaunchLug) c).setRelativePosition(type); - c.setPositionValue(pos); + c.setAxialOffset(pos); } else if (c instanceof InternalComponent) { ((InternalComponent) c).setRelativePosition(type); - c.setPositionValue(pos); + c.setAxialOffset(pos); } else if (c instanceof TubeFinSet) { ((TubeFinSet) c).setRelativePosition(type); - c.setPositionValue(pos); + c.setAxialOffset(pos); } else if (c instanceof Stage) { ((Stage) c).setRelativePositionMethod(type); - ((Stage) c).setPositionValue(pos); + c.setAxialOffset(pos); } else { warnings.add(Warning.FILE_INVALID_PARAMETER); } diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java index 3efe9f228..c07057051 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java @@ -83,7 +83,7 @@ public class RocketComponentSaver { if (c.getRelativePosition() != RocketComponent.Position.AFTER) { // The type names are currently equivalent to the enum names except for case. String type = c.getRelativePosition().name().toLowerCase(Locale.ENGLISH); - elements.add("" + c.getPositionValue() + ""); + elements.add("" + c.getAxialOffset() + ""); } 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 c237a7d3c..7144092d0 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java @@ -84,9 +84,9 @@ public class StageSaver extends ComponentAssemblySaver { elementsToReturn.add("<" + outside_tag + ">" + outsideFlag + ""); int instanceCount = currentStage.getInstanceCount(); elementsToReturn.add("<" + instCt_tag + ">" + instanceCount + ""); - double radialOffset = currentStage.getRadialPosition(); + double radialOffset = currentStage.getRadialOffset(); elementsToReturn.add("<" + radoffs_tag + ">" + radialOffset + ""); - double angularOffset = currentStage.getAngularPosition(); + double angularOffset = currentStage.getAngularOffset(); elementsToReturn.add("<" + startangle_tag + ">" + angularOffset + ""); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/MultipleComponent.java b/core/src/net/sf/openrocket/rocketcomponent/MultipleComponent.java deleted file mode 100644 index eaeaa2764..000000000 --- a/core/src/net/sf/openrocket/rocketcomponent/MultipleComponent.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.sf.openrocket.rocketcomponent; - -import net.sf.openrocket.util.Coordinate; - - -/** - * This interface is used to signal that the implementing interface contains multiple instances of its components. - * (Note: not all implementations replicate their children, but that is design intention.) - * - * @author teyrana ( Daniel Williams, equipoise@gmail.com ) - * - */ - -public interface MultipleComponent { - - - public int getInstanceCount(); - - // location of each instance relative to the component - // center-to-center vectors - public Coordinate[] getInstanceOffsets(); - - -} diff --git a/core/src/net/sf/openrocket/rocketcomponent/OutsideComponent.java b/core/src/net/sf/openrocket/rocketcomponent/OutsideComponent.java index 40265c1fa..4313cd170 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/OutsideComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/OutsideComponent.java @@ -23,28 +23,28 @@ public interface OutsideComponent { * * @return Angular position in radians. */ - public double getAngularPosition(); + public double getAngularOffset(); /** * Set the position of this component in polar coordinates * * @param phi Angular position in radians */ - public void setAngularPosition(final double phi); + public void setAngularOffset(final double phi); /** * Number of instances this stage represents * * @return number of instances this stage currently represents */ - public int getCount(); + public int getInstanceCount(); /** * Set the multiplicity of this component * * @param number of instances this component should represent */ - public void setCount(final int phi); + public void setInstanceCount(final int phi); /** @@ -52,13 +52,13 @@ public interface OutsideComponent { * * @return Radial position in radians (m) */ - public double getRadialPosition(); + public double getRadialOffset(); /** * Get the position of this component in polar coordinates * * @param radius Radial distance in standard units. (m) */ - public void setRadialPosition(final double radius); + public void setRadialOffset(final double radius); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 295b6f452..1bd4bc27b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -391,8 +391,6 @@ public class Rocket extends RocketComponent { return; } - log.debug("Firing rocket change event " + e); - // Notify all components first Iterator iterator = this.iterator(true); while (iterator.hasNext()) { @@ -636,8 +634,6 @@ public class Rocket extends RocketComponent { //////// Obligatory component information - - @Override public String getComponentName() { //// Rocket diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 67538f5b1..297154a85 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -106,8 +106,14 @@ 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 position = 0; + /** + * Position of this component relative to it's parent. + * In case (null == parent ): i.e. the Rocket/root component, the position is constrained to 0,0,0, and is the reference origin for the entire rocket. + * Defaults to (0,0,0) + */ + protected Coordinate position = new Coordinate(); // Color of the component, null means to use the default color private Color color = null; @@ -653,8 +659,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return isCGOverridden() || isMassOverridden(); } - - + /** + * placeholder. This allows code to generally test if this component represents multiple instances with just one function call. + * + * @return number of instances + */ + public int getInstanceCount() { + return 1; + } /** * Get the user-defined name of the component. @@ -844,6 +856,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return length; } + public RocketComponent.Position getRelativePositionMethod() { + return this.relativePosition; + } + /** * Get the positioning of the component relative to its parent component. * This is one of the enums of {@link Position}. A setter method is not provided, @@ -867,41 +883,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @param position the relative positioning. */ protected void setRelativePosition(RocketComponent.Position position) { - if (this.relativePosition == position) - return; - checkState(); - - // Update position so as not to move the component - if (this.parent != null) { - double thisPos = this.toRelative(Coordinate.NUL, this.parent)[0].x; - - switch (position) { - case ABSOLUTE: - this.position = this.toAbsolute(Coordinate.NUL)[0].x; - break; - - case TOP: - this.position = thisPos; - break; - - case MIDDLE: - this.position = thisPos - (this.parent.length - this.length) / 2; - break; - - case BOTTOM: - this.position = thisPos - (this.parent.length - this.length); - break; - - default: - throw new BugException("Unknown position type: " + 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; - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - /** * Determine position relative to given position argument. Note: This is a side-effect free method. No state * is modified. @@ -912,22 +899,34 @@ 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; + 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); + } + if (relativeTo != null) { - double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x; + double relLength = relativeTo.getLength(); switch (thePosition) { + case AFTER: + result = relativeAxialPosition + (-relLength - this.getLength()) / 2; + break; case ABSOLUTE: - result = this.toAbsolute(Coordinate.NUL)[0].x; + Coordinate curAbsPos = this.getAbsolutePositionVector(); + result = curAbsPos.x; break; case TOP: - result = thisPos; + result = relativeAxialPosition + (relLength - this.getLength()) / 2; break; case MIDDLE: - result = thisPos - (relativeTo.length - this.length) / 2; + result = relativeAxialPosition; break; case BOTTOM: - result = thisPos - (relativeTo.length - this.length); + result = relativeAxialPosition + (this.length - relLength) / 2; break; default: throw new BugException("Unknown position type: " + thePosition); @@ -942,59 +941,144 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * * @return the positional value. */ - public final double getPositionValue() { - mutex.verify(); - return position; + public double getPositionValue() { + return this.getAxialOffset(); } + public double getAxialOffset() { + mutex.verify(); + return this.asPositionValue(this.relativePosition, this.parent); + } + + /** + * + * @return always returns false for base components. This enables one-line testing if a component is on the rocket center-line or not. + */ + public boolean isCenterline() { + return true; + } + + public boolean isAncestor(final RocketComponent testComp) { + RocketComponent curComp = testComp.parent; + while (curComp != null) { + if (this == curComp) { + return true; + } + curComp = curComp.parent; + } + return false; + } + /** * Set the position value of the component. The exact meaning of the value * depends on the current relative positioning. *

- * The default implementation is of protected visibility, since many components - * do not support setting the relative position. A component that does support + * Mince many components do not support setting the relative position. A component that does support * it should override this with a public method that simply calls this * supermethod AND fire a suitable ComponentChangeEvent. - * + * + * @deprecated name is ambiguous in three-dimensional space: value may refer to any of the three dimensions. Please use 'setPositionX' instead. + * * @param value the position value of the component. */ public void setPositionValue(double value) { - if (MathUtil.equals(this.position, value)) + // if (MathUtil.equals(this.position.x, value)) + // return; + // // checkState(); + // // this.position = new Coordinate(value, 0, 0); + setAxialOffset(value); + } + + public void setAxialOffset(double value) { + this.setAxialOffset(this.relativePosition, value, this.getParent()); + 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 + + if ((null == this.parent) || (referenceComponent == null)) { return; + } + if (referenceComponent == this) { + throw new BugException("cannot move a component relative to itself!"); + } checkState(); - this.position = value; + // 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())); + // } + + double newAxialPosition = Double.NaN; + double refRelX = referenceComponent.position.x; + double refLength = referenceComponent.getLength(); + + if (referenceComponent.isAncestor(this)) { + referenceComponent = this.parent; + refRelX = 0; + } + + switch (positionMethod) { + case ABSOLUTE: + newAxialPosition = newOffset - this.parent.position.x; + break; + case AFTER: + newAxialPosition = refRelX + (refLength + this.length) / 2; + break; + case TOP: + newAxialPosition = refRelX + (-refLength + this.length) / 2 + newOffset; + break; + case MIDDLE: + newAxialPosition = refRelX + newOffset; + break; + case BOTTOM: + newAxialPosition = refRelX + (+refLength - this.length) / 2 + newOffset; + break; + default: + throw new BugException("Unknown position type: " + positionMethod); + } + + this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z); + + // 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)); + // } } - - /* - * - */ public Coordinate getRelativePositionVector() { - // ghetto version of this.... - return new Coordinate(this.getPositionValue(), 0, 0); + return this.position; } + // disabled + // public void setRelativePositionVector(final Coordinate _newPos) { + // // this.setPosition( this.relativePosition, _newPos ); + // } - /* - * - */ - public void setRelativePositionVector(final Coordinate _newPos) { - // ghetto version of this.... - this.position = _newPos.x; + public Coordinate getAbsolutePositionVector() { + if (null == this.parent) { // i.e. root / Rocket instance OR improperly initialized components + return new Coordinate(); + } else { + return this.parent.getAbsolutePositionVector().add(this.getRelativePositionVector()); + } } /////////// Coordinate changes /////////// /** - * Returns coordinate c in absolute coordinates. Equivalent to toComponent(c,null). + * Returns coordinate c in absolute/global/rocket coordinates. Equivalent to toComponent(c,null). + * + * @param c Coordinate in the component's coordinate system. + * @return an array of coordinates describing c in global coordinates. */ public Coordinate[] toAbsolute(Coordinate c) { - checkState(); - return toRelative(c, null); + // checkState(); + // return toRelative(c, null); + Coordinate absCoord = this.getAbsolutePositionVector().add(c); + return new Coordinate[] { absCoord }; } - /** * Return coordinate c described in the coordinate system of * dest. If dest is null returns @@ -1006,16 +1090,24 @@ 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) { checkState(); mutex.lock("toRelative"); + + if (null == dest) { + throw new BugException("calling toRelative(c,null) is being refactored. "); + } + try { double absoluteX = Double.NaN; + double relativeX = 0; double relativeY = 0; double relativeZ = 0; RocketComponent search = dest; @@ -1023,15 +1115,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab array[0] = c; RocketComponent component = this; - if (component instanceof OutsideComponent) { - OutsideComponent ext = (OutsideComponent) component; - double phi = ext.getAngularPosition(); - double r = ext.getRadialPosition(); - relativeY = r * Math.cos(phi); - relativeZ = r * Math.sin(phi); - array[0].setY(relativeY); - array[0].setZ(relativeZ); - } while ((component != search) && (component.parent != null)) { array = component.shiftCoordinates(array); @@ -1039,25 +1122,28 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab switch (component.relativePosition) { case TOP: for (int i = 0; i < array.length; i++) { - array[i] = array[i].add(component.position, relativeY, relativeZ); + array[i] = array[i].add(relativeX, relativeY, relativeZ); } break; case MIDDLE: + relativeX = component.position.x; for (int i = 0; i < array.length; i++) { - array[i] = array[i].add(component.position + - (component.parent.length - component.length) / 2, relativeY, relativeZ); + array[i] = array[i].add(relativeX + (component.parent.length - component.length) / 2, + relativeY, relativeZ); } break; case BOTTOM: + relativeX = component.position.x; for (int i = 0; i < array.length; i++) { - array[i] = array[i].add(component.position + - (component.parent.length - component.length), relativeY, relativeZ); + array[i] = array[i].add(relativeX + (component.parent.length - component.length), + relativeY, relativeZ); } break; case AFTER: + relativeX = component.position.x; // Add length of all previous brother-components with POSITION_RELATIVE_AFTER int index = component.parent.children.indexOf(component); assert (index >= 0); @@ -1069,7 +1155,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } for (int i = 0; i < array.length; i++) { - array[i] = array[i].add(component.position + component.parent.length, relativeY, relativeZ); + array[i] = array[i].add(relativeX + component.parent.length, relativeY, relativeZ); } break; @@ -1077,7 +1163,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab search = null; // Requires back-search if dest!=null if (Double.isNaN(absoluteX)) { // TODO: requires debugging if thsi component is an External Pods or stage - absoluteX = component.position; + absoluteX = relativeX; } break; @@ -1099,9 +1185,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab // Check whether destination has been found or whether to backtrack // TODO: LOW: Backtracking into clustered components uses only one component if ((dest != null) && (component != dest)) { - Coordinate[] origin = dest.toAbsolute(Coordinate.NUL); + + Coordinate origin = dest.getAbsolutePositionVector(); for (int i = 0; i < array.length; i++) { - array[i] = array[i].sub(origin[0]); + array[i] = array[i].sub(origin); } } @@ -1111,9 +1198,29 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } + // public final Coordinate[] toRelative(Coordinate[] coords, RocketComponent dest) { + // Coordinate[] toReturn = new Coordinate[coords.length]; + // + // + // // Coordinate[] array = new Coordinate[] { c }; + // // // if( dest.isCluster() ){ + // // // if( dest.multiplicity > 1){ + // // array = dest.shiftCoordinates(array); + // // return this.toRelative(array, dest); + // // // } + // + // Coordinate destCenter = dest.getAbsolutePositionVector(); + // Coordinate thisCenter = this.getAbsolutePositionVector(); + // Coordinate relVector = destCenter.sub(thisCenter); + // + // for (int coord_index = 0; coord_index < coords.length; coord_index++) { + // toReturn[coord_index] = coords[coord_index].add(relVector); + // } + // return toReturn; + // } /** - * Recursively sum the lengths of all subcomponents that have position + * Iteratively sum the lengths of all subcomponents that have position * Position.AFTER. * * @return Sum of the lengths. @@ -1227,7 +1334,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab addChild(component, children.size()); } - /** * Adds a child to the rocket component tree. The component is added to * the given position of the component's child list. @@ -1258,7 +1364,6 @@ 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; @@ -1282,6 +1387,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.checkComponentStructure(); component.checkComponentStructure(); + updateBounds(); fireAddRemoveEvent(component); } @@ -1303,6 +1409,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.checkComponentStructure(); component.checkComponentStructure(); + updateBounds(); fireAddRemoveEvent(component); return true; } @@ -1327,6 +1434,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.checkComponentStructure(); component.checkComponentStructure(); + updateBounds(); fireAddRemoveEvent(component); } } @@ -1737,6 +1845,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return id.hashCode(); } + /** + * the default implementation is mostly a placeholder here, however in inheriting classes, + * this function is useful to indicate adjacent placements and view sizes + */ + protected void updateBounds() { + return; + } + ///////////// Visitor pattern implementation public R accept(RocketComponentVisitor visitor) { visitor.visit(this); @@ -1961,4 +2077,16 @@ 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] "); + } + + 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 + " "); + } + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Stage.java b/core/src/net/sf/openrocket/rocketcomponent/Stage.java index d22d051e4..c75a93517 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -1,13 +1,16 @@ package net.sf.openrocket.rocketcomponent; +import java.util.Iterator; + import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Coordinate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Stage extends ComponentAssembly implements FlightConfigurableComponent, MultipleComponent, OutsideComponent { +public class Stage extends ComponentAssembly implements FlightConfigurableComponent, OutsideComponent { static final Translator trans = Application.getTranslator(); private static final Logger log = LoggerFactory.getLogger(Stage.class); @@ -17,7 +20,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon private boolean outside = false; private double angularPosition_rad = 0; private double radialPosition_m = 0; - private int stageRelativeTo = 0; + private Stage stageRelativeTo = null; private int count = 1; private double angularSeparation = Math.PI; @@ -25,15 +28,72 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon 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++; + } + + + } + } + + 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() { //// Stage return trans.get("Stage.Stage"); } - public FlightConfiguration getStageSeparationConfiguration() { return separationConfigurations; } @@ -74,18 +134,26 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon return this.outside; } - public boolean isInline() { + @Override + public boolean isCenterline() { return !this.outside; } @Override public void setOutside(final boolean _outside) { - this.outside = _outside; - if (Position.AFTER == this.relativePosition) { - this.relativePosition = Position.BOTTOM; - this.position = 0; + if (this.outside == _outside) { + return; } - if (!this.outside) { + + 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; } @@ -93,8 +161,8 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public int getCount() { - if (this.isInline()) { + public int getInstanceCount() { + if (this.isCenterline()) { return 1; } else { return this.count; @@ -102,7 +170,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public void setCount(final int _count) { + public void setInstanceCount(final int _count) { mutex.verify(); this.count = _count; this.angularSeparation = Math.PI * 2 / this.count; @@ -113,7 +181,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public double getAngularPosition() { + public double getAngularOffset() { if (this.outside) { return this.angularPosition_rad; } else { @@ -123,7 +191,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public void setAngularPosition(final double angle_rad) { + public void setAngularOffset(final double angle_rad) { this.angularPosition_rad = angle_rad; if (this.outside) { fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); @@ -131,7 +199,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public double getRadialPosition() { + public double getRadialOffset() { if (this.outside) { return this.radialPosition_m; } else { @@ -140,7 +208,7 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } @Override - public void setRadialPosition(final double radius) { + 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) { @@ -148,6 +216,25 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon } } + public void setRelativePositionMethod(final Position _newPosition) { + if (Position.AFTER != _newPosition) { + this.outside = true; + } + + super.setRelativePosition(_newPosition); + } + + @Override + public double getPositionValue() { + mutex.verify(); + + if (null == this.stageRelativeTo) { + return super.asPositionValue(this.relativePosition, this.getParent()); + } else { + return getAxialOffset(); + } + } + /** * Stages may be positioned relative to other stages. In that case, this will set the stage number * against which this stage is positioned. @@ -155,65 +242,142 @@ public class Stage extends ComponentAssembly implements FlightConfigurableCompon * @return the stage number which this stage is positioned relative to */ public int getRelativeToStage() { - return this.stageRelativeTo; + if (null == this.stageRelativeTo) { + return -1; + } + + return this.getRocket().getChildPosition(this.stageRelativeTo); } + /* * * @param _relTo the stage number which this stage is positioned relative to */ - public void setRelativeToStage(final int _relTo) { + 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."); - return; - } - this.stageRelativeTo = _relTo; - } - - public RocketComponent.Position getRelativePositionMethod() { - return this.relativePosition; - } - - public void setRelativePositionMethod(final Position position) { - super.setRelativePosition(position); - } - - public double getAxialPosition() { - return super.getPositionValue(); - } - - public void setAxialPosition(final double _pos) { - super.setPositionValue(_pos); - if (this.outside) { - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); - } - } - - @Override - public int getInstanceCount() { - return this.count; - } - - @Override - public Coordinate[] getInstanceOffsets() { - if (this.isInline()) { - return new Coordinate[] { new Coordinate(0, 0, 0) }; + this.stageRelativeTo = null; + } else if (_relTo == this.getRocket().getChildPosition(this)) { + // self-referential: also an error + this.stageRelativeTo = null; + } else if (this.isCenterline()) { + this.relativePosition = Position.AFTER; + updatePrevAxialStage(); + } else { + this.stageRelativeTo = (Stage) this.getRocket().getChild(_relTo); } - Coordinate[] toReturn = new Coordinate[this.count]; + return this.stageRelativeTo; + } + + + @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); + } else { + returnValue = super.asPositionValue(this.relativePosition, this.stageRelativeTo); + } + + if (0.000001 > Math.abs(returnValue)) { + returnValue = 0.0; + } + + return returnValue; + } + + @Override + public void setAxialOffset(final double _pos) { + this.updateBounds(); + super.setAxialOffset(this.relativePosition, _pos, this.stageRelativeTo); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + } + + // TOOD: unify with 'generate instanceOffsets()' + // what is the use of this again? + @Override + public Coordinate[] shiftCoordinates(Coordinate[] c) { + checkState(); + + if (this.isCenterline()) { + return c; + } + + if (1 < c.length) { + throw new BugException("implementation of 'shiftCoordinates' assumes the coordinate array has len == 1; this is not true, and may produce unexpected behavior! "); + } double radius = this.radialPosition_m; double angle0 = this.angularPosition_rad; double angleIncr = this.angularSeparation; - + Coordinate[] toReturn = new Coordinate[this.count]; + Coordinate thisOffset; double thisAngle = angle0; for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) { - toReturn[instanceNumber] = new Coordinate(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle)); + thisOffset = new Coordinate(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle)); + + toReturn[instanceNumber] = thisOffset.add(c[0]); thisAngle += angleIncr; } return toReturn; } + + @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(); + } + + } + + /** + * @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--; + } + } + + this.stageRelativeTo = null; + return null; + } + + 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; + } + } + + // general case: + double offset = super.asPositionValue(this.relativePosition, this.stageRelativeTo); + this.setAxialOffset(this.relativePosition, offset, this.stageRelativeTo); + } + } diff --git a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java index 9af60bff2..c34996e8f 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.toRelative(new Coordinate(0, 0, 0), s)[0].x; + double x = this.getRelativePositionVector().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 new file mode 100644 index 000000000..bc2e5805d --- /dev/null +++ b/core/test/net/sf/openrocket/rocketcomponent/StageTest.java @@ -0,0 +1,627 @@ +package net.sf.openrocket.rocketcomponent; + +//import junit.framework.TestCase; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; +import net.sf.openrocket.rocketcomponent.RocketComponent.Position; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; + +import org.junit.Test; + +public class StageTest extends BaseTestCase { + + // tolerance for compared double test results + protected final double EPSILON = 0.001; + + protected final Coordinate ZERO = new Coordinate(0., 0., 0.); + + public void test() { + // fail("Not yet implemented"); + } + + public Rocket createTestRocket() { + double tubeRadius = 1; + // setup + Rocket root = new Rocket(); + root.setName("Rocket"); + + Stage sustainer = new Stage(); + sustainer.setName("Sustainer stage"); + RocketComponent sustainerNose = new NoseCone(Transition.Shape.CONICAL, 2.0, tubeRadius); + sustainerNose.setName("Sustainer Nosecone"); + sustainer.addChild(sustainerNose); + RocketComponent sustainerBody = new BodyTube(3.0, tubeRadius, 0.01); + sustainerBody.setName("Sustainer Body "); + sustainer.addChild(sustainerBody); + root.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; + } + + public Stage createBooster() { + double tubeRadius = 0.8; + + Stage booster = new Stage(); + booster.setName("Booster Stage A"); + booster.setOutside(true); + RocketComponent boosterNose = new NoseCone(Transition.Shape.CONICAL, 2.0, tubeRadius); + boosterNose.setName("Booster A Nosecone"); + booster.addChild(boosterNose); + RocketComponent boosterBody = new BodyTube(2.0, tubeRadius, 0.01); + boosterBody.setName("Booster A Body "); + booster.addChild(boosterBody); + Transition boosterTail = new Transition(); + boosterTail.setName("Booster A Tail"); + boosterTail.setForeRadius(1.0); + boosterTail.setAftRadius(0.5); + boosterTail.setLength(1.0); + booster.addChild(boosterTail); + + 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 + * During calculation of the aerodynamic properties a coordinate system fixed to the rocket will be used. + * The origin of the coordinates is at the nose cone tip with the positive x-axis directed along the rocket + @@ -41,70 +35,302 @@ public class BodyTubeTest extends TestCase { + * when discussing the fins. During simulation, however, the y- and z-axes are fixed in relation to the rocket, + * and do not necessarily align with the plane of the pitching moments. + */ + + @Test + public void testSetRocketPositionFail() { + RocketComponent root = createTestRocket(); + Coordinate expectedPosition; + Coordinate targetPosition; + Coordinate resultPosition; + + // case 1: the Root 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)); + + } + + @Test + public void testAddTopStage() { + RocketComponent root = createTestRocket(); + + // Sustainer Stage + Stage sustainer = (Stage) root.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 sustainer Body an ancestor of the sustainer Nose? ", sustainerBody.isAncestor(sustainerNose), equalTo(false)); + + 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)); + sustainerX = sustainer.getRelativePositionVector().x; + assertThat(" createTestRocket failed: Absolute position: ", sustainerX, equalTo(expectedSustainerX)); + + double expectedSustainerNoseX = -1.5; + double sustainerNosePosition = sustainerNose.getRelativePositionVector().x; + assertThat(" createTestRocket failed: sustainer Nose X position: ", sustainerNosePosition, equalTo(expectedSustainerNoseX)); + expectedSustainerNoseX = +1; + sustainerNosePosition = sustainerNose.getAbsolutePositionVector().x; + assertThat(" createTestRocket failed: sustainer Nose X position: ", sustainerNosePosition, equalTo(expectedSustainerNoseX)); + + double expectedSustainerBodyX = +1; + double sustainerBodyX = sustainerBody.getRelativePositionVector().x; + assertThat(" createTestRocket failed: sustainer body rel X position: ", sustainerBodyX, equalTo(expectedSustainerBodyX)); + expectedSustainerBodyX = +3.5; + sustainerBodyX = sustainerBody.getAbsolutePositionVector().x; + assertThat(" createTestRocket failed: sustainer body abs X position: ", sustainerBodyX, equalTo(expectedSustainerBodyX)); + + } + + // WARNING: this test will not pass unless 'testAddTopStage' is passing as well -- that function tests the dependencies... + @Test + public void testAddMiddleStage() { + RocketComponent root = createTestRocket(); + + // Core Stage + Stage core = (Stage) root.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); + + coreX = core.getRelativePositionVector().x; + assertThat(" createTestRocket failed: Relative position: ", coreX, equalTo(expectedCoreX)); + coreX = core.getAbsolutePositionVector().x; + assertThat(" createTestRocket failed: 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)); + + } + + @Test + public void testSetStagePosition_topOfStack() { + // setup + RocketComponent root = createTestRocket(); + Stage sustainer = (Stage) root.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)); + + // vv function under test + sustainer.setAxialOffset(targetPosition.x); + // ^^ function under test + + 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) + 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); + + 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); + + 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); + + 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); + + 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)); + + } + + + @Test + public void testSetStagePosition_inStack() { + // 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.); + + sustainer.setAxialOffset(targetPosition.x); + Coordinate sustainerPosition = sustainer.getRelativePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", sustainerPosition.x, equalTo(expectedSustainerPosition.x)); + + core.setAxialOffset(targetPosition.x); + Coordinate resultantCorePosition = core.getRelativePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantCorePosition.x, equalTo(expectedCorePosition.x)); + + } + + // 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_outsideABSOLUTE() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + Coordinate targetPosition = new Coordinate(+17.0, 0., 0.); + double expectedX = targetPosition.x; + + // when 'external' the stage should be freely movable + booster.setOutside(true); + booster.setRelativePositionMethod(Position.ABSOLUTE); + booster.setRelativeToStage(1); + // vv function under test + booster.setAxialOffset(targetPosition.x); + // ^^ 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)); + 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) + Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + } + + // WARNING: + // Because even though this is an "outside" stage, it's relative to itself -- i.e. an error-condition + // 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_outsideTopOfStack() { + // setup + RocketComponent root = createTestRocket(); + Stage sustainer = (Stage) root.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)); + + // vv function under test + sustainer.setAxialOffset(targetPosition.x); + // ^^ function under test + + Coordinate resultantRelativePosition = sustainer.getRelativePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Relative position: ", resultantRelativePosition.x, equalTo(expectedPosition.x)); + double expectedPositionValue = 0; + double resultantPositionValue = sustainer.getPositionValue(); + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantPositionValue, equalTo(expectedPositionValue)); + + 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) + Coordinate resultantAbsolutePosition = sustainer.getAbsolutePositionVector(); + assertThat(" 'setAbsolutePositionVector()' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedPosition.x)); + } + + @Test + public void testSetStagePosition_outsideTOP() { + Rocket root = this.createTestRocket(); + Stage booster = createBooster(); + root.addChild(booster); + + booster.setOutside(true); + booster.setRelativePositionMethod(Position.TOP); + booster.setRelativeToStage(1); + // vv function under test + double targetOffset = +2.0; + booster.setAxialOffset(targetOffset); + // ^^ function under test + + double expectedX = +9.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) + Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + + double resultantAxialOffset = booster.getAxialOffset(); + assertThat(" 'getAxialPosition()' failed. Relative position: ", resultantAxialOffset, equalTo(targetOffset)); + + double resultantPositionValue = booster.getPositionValue(); + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantPositionValue, equalTo(targetOffset)); + } + + @Test + public void testSetStagePosition_outsideMIDDLE() { + // setup + RocketComponent root = createTestRocket(); + Stage booster = createBooster(); + root.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); + // ^^ function under test + + double expectedX = +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) + Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + + + double resultantPositionValue = booster.getPositionValue(); + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantPositionValue, equalTo(targetOffset)); + + double resultantAxialOffset = booster.getAxialOffset(); + assertThat(" 'getAxialPosition()' failed. Relative position: ", resultantAxialOffset, equalTo(targetOffset)); + } + + @Test + public void testSetStagePosition_outsideBOTTOM() { + // setup + RocketComponent root = createTestRocket(); + Stage booster = createBooster(); + root.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); + // ^^ function under test + + double expectedX = +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) + Coordinate resultantAbsolutePosition = booster.getAbsolutePositionVector(); + assertThat(" 'setAxialPosition(double)' failed. Absolute position: ", resultantAbsolutePosition.x, equalTo(expectedX)); + + double resultantPositionValue = booster.getPositionValue(); + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantPositionValue, equalTo(targetOffset)); + + double resultantAxialOffset = booster.getAxialOffset(); + assertThat(" 'getAxialPosition()' failed. Relative position: ", resultantAxialOffset, equalTo(targetOffset)); + } + + @Test + public void testAxial_setTOP_getABSOLUTE() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + double targetOffset = +4.50; + booster.setOutside(true); + booster.setRelativePositionMethod(Position.TOP); + booster.setRelativeToStage(1); + booster.setAxialOffset(targetOffset); + + double expectedAxialOffset = +12.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); + // ^^ function under test + + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialOffset)); + } + + @Test + public void testAxial_setTOP_getAFTER() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + double targetOffset = +4.50; + booster.setOutside(true); + booster.setRelativePositionMethod(Position.TOP); + booster.setRelativeToStage(1); + booster.setAxialOffset(targetOffset); + + Coordinate expectedPosition = new Coordinate(+12.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; + // vv function under test + resultantAxialPosition = booster.asPositionValue(Position.AFTER, refStage); + // ^^ function under test + expectedAxialPosition = -1.5; + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + } + + @Test + public void testAxial_setTOP_getMIDDLE() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + + double targetOffset = +4.50; + booster.setOutside(true); + booster.setRelativePositionMethod(Position.TOP); + booster.setRelativeToStage(1); + booster.setAxialOffset(targetOffset); + + Coordinate expectedPosition = new Coordinate(+12.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 = +4.0; + + // vv function under test + resultantAxialPosition = booster.asPositionValue(Position.MIDDLE, refStage); + // ^^ function under test + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + } + + @Test + public void testAxial_setTOP_getBOTTOM() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + + double targetOffset = +4.50; + booster.setOutside(true); + booster.setRelativePositionMethod(Position.TOP); + booster.setRelativeToStage(1); + booster.setAxialOffset(targetOffset); + + Coordinate expectedPosition = new Coordinate(+12.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; + // vv function under test + resultantAxialPosition = booster.asPositionValue(Position.BOTTOM, refStage); + // ^^ function under test + expectedAxialPosition = +3.5; + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + } + + + @Test + public void testAxial_setBOTTOM_getTOP() { + // setup + RocketComponent root = createTestRocket(); + Stage core = (Stage) root.getChild(1); + Stage booster = createBooster(); + root.addChild(booster); + + + double targetOffset = +4.50; + booster.setOutside(true); + booster.setRelativePositionMethod(Position.BOTTOM); + booster.setRelativeToStage(1); + booster.setAxialOffset(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; + + // vv function under test + resultantAxialPosition = booster.asPositionValue(Position.TOP, refStage); + // ^^ function under test + expectedAxialPosition = 5.5; + assertThat(" 'setPositionValue()' failed. Relative position: ", resultantAxialPosition, equalTo(expectedAxialPosition)); + } + + @Test + public void testInitializationOrder() { + Rocket root = createTestRocket(); + Stage boosterA = createBooster(); + root.addChild(boosterA); + Stage boosterB = createBooster(); + root.addChild(boosterB); + + double targetOffset = +4.50; + + // requirement: regardless of initialization order (which we cannot control) + // two boosters with identical initialization commands should end up at the same place. + + boosterA.setOutside(true); + boosterA.setRelativePositionMethod(Position.BOTTOM); + boosterA.setRelativeToStage(1); + boosterA.setAxialOffset(targetOffset); + + 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); + + root.dumpTree(true, ""); + + double offsetA = boosterA.getAxialOffset(); + double offsetB = boosterB.getAxialOffset(); + + assertThat(" init order error: Booster A: resultant positions: ", offsetA, equalTo(targetOffset)); + assertThat(" init order error: Booster B: resultant positions: ", offsetB, equalTo(targetOffset)); + + + } + + +} diff --git a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java index 614785ef4..55d2f4a1c 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java @@ -348,8 +348,8 @@ public class InnerTubeConfig extends RocketComponentConfig { document.addUndoPosition("Split cluster"); - Coordinate[] coords = { Coordinate.NUL }; - coords = component.shiftCoordinates(coords); + Coordinate[] coords = new Coordinate[]{Coordinate.NUL }; + coords = component.shiftCoordinates( coords); parent.removeChild(index); for (int i = 0; i < coords.length; i++) { InnerTube copy = InnerTube.makeIndividualClusterComponent(coords[i], component.getName() + " #" + (i + 1), component); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java index 7f171ed0b..0540c6b36 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/StageConfig.java @@ -58,21 +58,28 @@ public class StageConfig extends RocketComponentConfig { } private JPanel parallelTab( final Stage stage ){ - // enable parallel staging 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"); parallelEnabledModel.setValue( stage.getOutside()); JCheckBox parallelEnabled = new JCheckBox( parallelEnabledModel); - parallelEnabled.setText(trans.get("RocketCompCfg.outside.stage")); + parallelEnabled.setText(trans.get("Stage.parallel.toggle")); motherPanel.add(parallelEnabled, "wrap"); - motherPanel.add(new JSeparator(SwingConstants.HORIZONTAL), "spanx 3, growx, wrap"); - // set radial distance - JLabel radiusLabel = new JLabel(trans.get("RocketCompCfg.outside.radius")); + JLabel radiusLabel = new JLabel(trans.get("Stage.parallel.radius")); motherPanel.add( radiusLabel , "align left"); parallelEnabledModel.addEnableComponent( radiusLabel, true); - DoubleModel radiusModel = new DoubleModel( stage, "RadialPosition", UnitGroup.UNITS_LENGTH, 0); + DoubleModel radiusModel = new DoubleModel( stage, "RadialOffset", UnitGroup.UNITS_LENGTH, 0); //radiusModel.setCurrentUnit( UnitGroup.UNITS_LENGTH.getUnit("cm")); JSpinner radiusSpinner = new JSpinner( radiusModel.getSpinnerModel()); radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner )); @@ -83,10 +90,10 @@ public class StageConfig extends RocketComponentConfig { parallelEnabledModel.addEnableComponent( radiusUnitSelector , true); // set location angle around the primary stage - JLabel angleLabel = new JLabel(trans.get("RocketCompCfg.outside.angle")); + JLabel angleLabel = new JLabel(trans.get("Stage.parallel.angle")); motherPanel.add( angleLabel, "align left"); parallelEnabledModel.addEnableComponent( angleLabel, true); - DoubleModel angleModel = new DoubleModel( stage, "AngularPosition", 1.0, UnitGroup.UNITS_ANGLE, 0.0, Math.PI*2); + DoubleModel angleModel = new DoubleModel( stage, "AngularOffset", 1.0, UnitGroup.UNITS_ANGLE, 0.0, Math.PI*2); angleModel.setCurrentUnit( UnitGroup.UNITS_ANGLE.getUnit("rad")); JSpinner angleSpinner = new JSpinner(angleModel.getSpinnerModel()); angleSpinner.setEditor(new SpinnerEditor(angleSpinner)); @@ -97,11 +104,11 @@ public class StageConfig extends RocketComponentConfig { parallelEnabledModel.addEnableComponent( angleUnitSelector , true); // set multiplicity - JLabel countLabel = new JLabel(trans.get("RocketCompCfg.outside.count")); + JLabel countLabel = new JLabel(trans.get("Stage.parallel.count")); motherPanel.add( countLabel, "align left"); parallelEnabledModel.addEnableComponent( countLabel, true); - IntegerModel countModel = new IntegerModel( stage, "Count", 1 ); + IntegerModel countModel = new IntegerModel( stage, "InstanceCount", 1 ); JSpinner countSpinner = new JSpinner(countModel.getSpinnerModel()); countSpinner.setEditor(new SpinnerEditor(countSpinner)); motherPanel.add(countSpinner, "growx 1, wrap"); @@ -113,36 +120,31 @@ public class StageConfig extends RocketComponentConfig { parallelEnabledModel.addEnableComponent( positionLabel, true); // EnumModel(ChangeSource source, String valueName, Enum[] values) { - ComboBoxModel posRelModel = new EnumModel(component, "RelativePositionMethod", + ComboBoxModel relativePositionMethodModel = new EnumModel(component, "RelativePositionMethod", new RocketComponent.Position[] { RocketComponent.Position.TOP, RocketComponent.Position.MIDDLE, RocketComponent.Position.BOTTOM, - RocketComponent.Position.ABSOLUTE + RocketComponent.Position.ABSOLUTE, + RocketComponent.Position.AFTER }); - JComboBox positionMethodCombo = new JComboBox( posRelModel ); + JComboBox positionMethodCombo = new JComboBox( relativePositionMethodModel ); motherPanel.add(positionMethodCombo, "spanx 2, growx, wrap"); parallelEnabledModel.addEnableComponent( positionMethodCombo, true); - // setPositions relative to parent component - JLabel relativeStageLabel = new JLabel(trans.get("RocketCompCfg.outside.componentname")); - motherPanel.add( relativeStageLabel); - parallelEnabledModel.addEnableComponent( relativeStageLabel, true); - ComboBoxModel relativeStageModel = new StageSelectModel( stage ); - JComboBox relToCombo = new JComboBox( relativeStageModel ); - motherPanel.add( relToCombo , "growx, wrap"); - parallelEnabledModel.addEnableComponent( relToCombo, true ); - - // plus - JLabel positionPlusLabel = new JLabel(trans.get("LaunchLugCfg.lbl.plus")); + // relative offset labels + JLabel positionPlusLabel = new JLabel(trans.get("Stage.parallel.offset")); motherPanel.add( positionPlusLabel ); - parallelEnabledModel.addEnableComponent( positionPlusLabel, true ); - - DoubleModel axialPositionModel = new DoubleModel(component, "AxialPosition", UnitGroup.UNITS_LENGTH); - JSpinner axPosSpin= new JSpinner( axialPositionModel.getSpinnerModel()); + parallelEnabledModel.addEnableComponent( positionPlusLabel, true ); + DoubleModel axialOffsetModel = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); + axialOffsetModel.setCurrentUnit(UnitGroup.UNITS_LENGTH.getUnit("cm")); + JSpinner axPosSpin= new JSpinner( axialOffsetModel.getSpinnerModel()); axPosSpin.setEditor(new SpinnerEditor(axPosSpin)); motherPanel.add(axPosSpin, "growx"); parallelEnabledModel.addEnableComponent( axPosSpin, true ); + UnitSelector axialOffsetUnitSelector = new UnitSelector(axialOffsetModel); + motherPanel.add(axialOffsetUnitSelector, "growx 1, wrap"); + parallelEnabledModel.addEnableComponent( axialOffsetUnitSelector , true); return motherPanel; } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java index ab72c3a7b..f8e68304c 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/BodyTubeShapes.java @@ -13,17 +13,21 @@ public class BodyTubeShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube)component; double length = tube.getLength(); double radius = tube.getOuterRadius(); - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Rectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, - length*S,2*radius*S); + 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 + (instanceOffsets[i].y-radius)*S, // y - the Y coordinate of the upper-left corner of the newly constructed Rectangle2D + length*S, // w - the width of the newly constructed Rectangle2D + 2*radius*S); // h - the height of the newly constructed Rectangle2D } return RocketComponentShape.toArray(s, component); @@ -32,16 +36,17 @@ public class BodyTubeShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube)component; double or = tube.getOuterRadius(); - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); + Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )}; + instanceOffsets = component.shiftCoordinates(instanceOffsets); + + Shape[] s = new Shape[instanceOffsets.length]; + for (int i=0; i < instanceOffsets.length; i++) { + s[i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S,(instanceOffsets[i].y-or)*S,2*or*S,2*or*S); } return RocketComponentShape.toArray(s, component); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java index 8ef726cf0..780a4675b 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java @@ -15,18 +15,19 @@ public class FinSetShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.FinSet finset = (net.sf.openrocket.rocketcomponent.FinSet)component; int finCount = finset.getFinCount(); Transformation cantRotation = finset.getCantRotation(); - Transformation baseRotation = finset.getBaseRotationTransformation(); + Transformation baseRotation = finset.getBaseRotationTransformation(); // rotation about x-axis Transformation finRotation = finset.getFinRotationTransformation(); + double rootChord = finset.getLength(); + Coordinate finSetFront = componentAbsoluteLocation.sub( rootChord/2 , 0, 0); Coordinate finPoints[] = finset.getFinPointsWithTab(); - // TODO: MEDIUM: sloping radius double radius = finset.getBodyRadius(); @@ -36,17 +37,20 @@ public class FinSetShapes extends RocketComponentShape { finPoints[i] = baseRotation.transform(finPoints[i].add(0,radius,0)); } - + // Generate shapes - RocketComponentShape[] rcs = new RocketComponentShape[ finCount]; - for (int fin=0; fin= 0.0012) && (ir > 0)) { // Draw outer and inner - s = new Shape[start.length*2]; - for (int i=0; i < start.length; i++) { - s[2*i] = new Rectangle2D.Double(start[i].x*S,(start[i].y-or)*S, + s = new Shape[instanceOffsets.length*2]; + for (int i=0; i < instanceOffsets.length; i++) { + s[2*i] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-or)*S, length*S,2*or*S); - s[2*i+1] = new Rectangle2D.Double(start[i].x*S,(start[i].y-ir)*S, + s[2*i+1] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-ir)*S, length*S,2*ir*S); } } else { // Draw only outer - s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Rectangle2D.Double(start[i].x*S,(start[i].y-or)*S, + s = new Shape[instanceOffsets.length]; + for (int i=0; i < instanceOffsets.length; i++) { + s[i] = new Rectangle2D.Double(instanceOffsets[i].x*S,(instanceOffsets[i].y-or)*S, length*S,2*or*S); } } @@ -50,30 +50,31 @@ public class RingComponentShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.RingComponent tube = (net.sf.openrocket.rocketcomponent.RingComponent)component; Shape[] s; double or = tube.getOuterRadius(); double ir = tube.getInnerRadius(); - - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); + Coordinate[] instanceOffsets = new Coordinate[]{ transformation.transform( componentAbsoluteLocation )}; + instanceOffsets = component.shiftCoordinates(instanceOffsets); + if ((ir < or) && (ir > 0)) { // Draw inner and outer - s = new Shape[start.length*2]; - for (int i=0; i < start.length; i++) { - s[2*i] = new Ellipse2D.Double((start[i].z-or)*S, (start[i].y-or)*S, + s = new Shape[instanceOffsets.length*2]; + for (int i=0; i < instanceOffsets.length; i++) { + s[2*i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S, (instanceOffsets[i].y-or)*S, 2*or*S, 2*or*S); - s[2*i+1] = new Ellipse2D.Double((start[i].z-ir)*S, (start[i].y-ir)*S, + s[2*i+1] = new Ellipse2D.Double((instanceOffsets[i].z-ir)*S, (instanceOffsets[i].y-ir)*S, 2*ir*S, 2*ir*S); } } else { // Draw only outer - s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); + s = new Shape[instanceOffsets.length]; + for (int i=0; i < instanceOffsets.length; i++) { + s[i] = new Ellipse2D.Double((instanceOffsets[i].z-or)*S,(instanceOffsets[i].y-or)*S,2*or*S,2*or*S); } } return RocketComponentShape.toArray( s, component); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java index 1501bb5cd..63838d8db 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ShockCordShapes.java @@ -14,20 +14,26 @@ public class ShockCordShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { - net.sf.openrocket.rocketcomponent.MassObject tube = (net.sf.openrocket.rocketcomponent.MassObject)component; + net.sf.openrocket.rocketcomponent.MassObject massObj = (net.sf.openrocket.rocketcomponent.MassObject)component; - double length = tube.getLength(); - double radius = tube.getRadius(); + double length = massObj.getLength(); + double radius = massObj.getRadius(); double arc = Math.min(length, 2*radius) * 0.7; - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, + + Shape[] s = new Shape[0]; + 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); - } + +// Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); +// +// Shape[] s = new Shape[start.length]; +// for (int i=0; i < start.length; i++) { +// s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, +// length*S,2*radius*S,arc*S,arc*S); +// } return RocketComponentShape.toArray( addSymbol(s), component); } @@ -35,18 +41,22 @@ public class ShockCordShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.MassObject tube = (net.sf.openrocket.rocketcomponent.MassObject)component; double or = tube.getRadius(); - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); - } + Shape[] s = new Shape[0]; + Coordinate start = componentAbsoluteLocation; + s[0] = new Ellipse2D.Double((start.z-or)*S,(start.y-or)*S,2*or*S,2*or*S); + +// Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); +// +// Shape[] s = new Shape[start.length]; +// for (int i=0; i < start.length; i++) { +// s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); +// } return RocketComponentShape.toArray( s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java index b256d3920..c369c8d3d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/StreamerShapes.java @@ -14,20 +14,25 @@ public class StreamerShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation ) { - net.sf.openrocket.rocketcomponent.MassObject tube = (net.sf.openrocket.rocketcomponent.MassObject)component; + net.sf.openrocket.rocketcomponent.MassObject massObj = (net.sf.openrocket.rocketcomponent.MassObject)component; - double length = tube.getLength(); - double radius = tube.getRadius(); + double length = massObj.getLength(); + double radius = massObj.getRadius(); double arc = Math.min(length, 2*radius) * 0.7; - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, + + Shape[] s = new Shape[0]; + 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); - } + +// Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); +// Shape[] s = new Shape[start.length]; +// for (int i=0; i < start.length; i++) { +// s[i] = new RoundRectangle2D.Double(start[i].x*S,(start[i].y-radius)*S, +// length*S,2*radius*S,arc*S,arc*S); +// } return RocketComponentShape.toArray(addSymbol(s), component); } @@ -35,18 +40,21 @@ public class StreamerShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.MassObject tube = (net.sf.openrocket.rocketcomponent.MassObject)component; double or = tube.getRadius(); - - Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); - - Shape[] s = new Shape[start.length]; - for (int i=0; i < start.length; i++) { - s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); - } + Shape[] s = new Shape[0]; + Coordinate center = componentAbsoluteLocation; + s[0] = new Ellipse2D.Double((center.z-or)*S,(center.y-or)*S,2*or*S,2*or*S); + +// Coordinate[] start = transformation.transform(tube.toAbsolute(instanceOffset)); +// +// Shape[] s = new Shape[start.length]; +// for (int i=0; i < start.length; i++) { +// s[i] = new Ellipse2D.Double((start[i].z-or)*S,(start[i].y-or)*S,2*or*S,2*or*S); +// } return RocketComponentShape.toArray(s, component); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java index cf41fe141..f81042587 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java @@ -4,7 +4,6 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Transformation; -import java.awt.*; import java.awt.geom.Path2D; import java.util.ArrayList; @@ -20,22 +19,21 @@ public class SymmetricComponentShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { - return getShapesSide(component, transformation, instanceOffset, S); + return getShapesSide(component, transformation, componentAbsoluteLocation, S); } public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset, + Coordinate componentAbsoluteLocation, final double scaleFactor) { net.sf.openrocket.rocketcomponent.SymmetricComponent c = (net.sf.openrocket.rocketcomponent.SymmetricComponent) component; int i; final double delta = 0.0000001; double x; - ArrayList points = new ArrayList(); x = delta; @@ -71,11 +69,10 @@ public class SymmetricComponentShapes extends RocketComponentShape { //System.out.println("Final points: "+points.size()); - final int len = points.size(); - for (i = 0; i < len; i++) { - points.set(i, c.toAbsolute(points.get(i))[0]); - } +// for (i = 0; i < len; i++) { +// points.set(i, c.toAbsolute(points.get(i))[0]); +// } /* Show points: Shape[] s = new Shape[len+1]; @@ -87,18 +84,20 @@ public class SymmetricComponentShapes extends RocketComponentShape { //System.out.println("here"); - Coordinate center = instanceOffset; + final int len = points.size(); + Coordinate center = componentAbsoluteLocation; + Coordinate nose = center.sub( component.getLength()/2, 0, 0); // TODO: LOW: curved path instead of linear Path2D.Double path = new Path2D.Double(); - path.moveTo(points.get(len - 1).x * scaleFactor, (center.y+points.get(len - 1).y) * scaleFactor); + path.moveTo((nose.x + points.get(len - 1).x) * scaleFactor, (center.y+points.get(len - 1).y) * scaleFactor); for (i = len - 2; i >= 0; i--) { - path.lineTo(points.get(i).x * scaleFactor, (center.y+points.get(i).y) * scaleFactor); + path.lineTo((nose.x+points.get(i).x)* scaleFactor, (center.y+points.get(i).y) * scaleFactor); } for (i = 0; i < len; i++) { - path.lineTo(points.get(i).x * scaleFactor, (center.y-points.get(i).y) * scaleFactor); + path.lineTo((nose.x+points.get(i).x) * scaleFactor, (center.y-points.get(i).y) * scaleFactor); } - path.lineTo(points.get(len - 1).x * scaleFactor, (center.y+points.get(len - 1).y) * scaleFactor); + path.lineTo((nose.x+points.get(len - 1).x) * scaleFactor, (center.y+points.get(len - 1).y) * scaleFactor); path.closePath(); //s[len] = path; diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java index 25e3dba88..68d4869fb 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java @@ -1,6 +1,5 @@ package net.sf.openrocket.gui.rocketfigure; -import net.sf.openrocket.gui.scalefigure.RocketFigure; import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Transformation; @@ -8,7 +7,6 @@ import net.sf.openrocket.util.Transformation; import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; -import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -26,53 +24,55 @@ public class TransitionShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset, + Coordinate componentAbsoluteLocation, final double scaleFactor) { net.sf.openrocket.rocketcomponent.Transition transition = (net.sf.openrocket.rocketcomponent.Transition)component; RocketComponentShape[] mainShapes; + Coordinate center = transformation.transform( componentAbsoluteLocation ); + // this component type does not allow multiple instances + // Simpler shape for conical transition, others use the method from SymmetricComponent if (transition.getType() == Transition.Shape.CONICAL) { - double length = transition.getLength(); + double halflength = transition.getLength()/2; double r1 = transition.getForeRadius(); double r2 = transition.getAftRadius(); - Coordinate start = transformation.transform(transition. - toAbsolute(instanceOffset)[0]); Path2D.Float path = new Path2D.Float(); - path.moveTo( start.x* scaleFactor, (start.y+ r1)* scaleFactor); - path.lineTo( (start.x+length)* scaleFactor, (start.y+r2)* scaleFactor); - path.lineTo( (start.x+length)* scaleFactor, (start.y-r2)* scaleFactor); - path.lineTo( start.x* scaleFactor, (start.y-r1)* scaleFactor); + path.moveTo( (center.x-halflength)* scaleFactor, (center.y+ r1)* scaleFactor); + path.lineTo( (center.x+halflength)* scaleFactor, (center.y+r2)* scaleFactor); + path.lineTo( (center.x+halflength)* scaleFactor, (center.y-r2)* scaleFactor); + path.lineTo( (center.x-halflength)* scaleFactor, (center.y-r1)* scaleFactor); path.closePath(); mainShapes = new RocketComponentShape[] { new RocketComponentShape( path, component) }; } else { - mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, instanceOffset, scaleFactor); + mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, componentAbsoluteLocation, scaleFactor); } - Rectangle2D.Double shoulder1=null, shoulder2=null; + Rectangle2D.Double foreShoulder=null, aftShoulder=null; int arrayLength = mainShapes.length; if (transition.getForeShoulderLength() > 0.0005) { - Coordinate start = transformation.transform(transition. - toAbsolute(instanceOffset)[0]); - double r = transition.getForeShoulderRadius(); - double l = transition.getForeShoulderLength(); - shoulder1 = new Rectangle2D.Double((start.x-l)* scaleFactor, (start.y-r)* scaleFactor, l* scaleFactor, 2*r* scaleFactor); + Coordinate foreTransitionShoulderCenter = componentAbsoluteLocation.sub( (transition.getLength() + transition.getForeShoulderLength())/2, 0, 0); + center = transformation.transform( foreTransitionShoulderCenter); + + double rad = transition.getForeShoulderRadius(); + double len = transition.getForeShoulderLength(); + foreShoulder = new Rectangle2D.Double((center.x-len/2)* scaleFactor, (center.y-rad)* scaleFactor, len* scaleFactor, 2*rad* scaleFactor); arrayLength++; } if (transition.getAftShoulderLength() > 0.0005) { - Coordinate start = transformation.transform(transition. - toAbsolute(instanceOffset.add(transition.getLength(),0, 0))[0]); - - double r = transition.getAftShoulderRadius(); - double l = transition.getAftShoulderLength(); - shoulder2 = new Rectangle2D.Double(start.x* scaleFactor, (start.y-r)* scaleFactor, l* scaleFactor, 2*r* scaleFactor); + Coordinate aftTransitionShoulderCenter = componentAbsoluteLocation.add( (transition.getLength() + transition.getAftShoulderLength())/2, 0, 0); + center= transformation.transform( aftTransitionShoulderCenter ); + + double rad = transition.getAftShoulderRadius(); + double len = transition.getAftShoulderLength(); + aftShoulder = new Rectangle2D.Double((center.x-len/2)* scaleFactor, (center.y-rad)* scaleFactor, len* scaleFactor, 2*rad* scaleFactor); arrayLength++; } - if (shoulder1==null && shoulder2==null) + if (foreShoulder==null && aftShoulder==null) return mainShapes; Shape[] shapes = new Shape[arrayLength]; @@ -81,12 +81,12 @@ public class TransitionShapes extends RocketComponentShape { for (i=0; i < mainShapes.length; i++) { shapes[i] = mainShapes[i].shape; } - if (shoulder1 != null) { - shapes[i] = shoulder1; + if (foreShoulder != null) { + shapes[i] = foreShoulder; i++; } - if (shoulder2 != null) { - shapes[i] = shoulder2; + if (aftShoulder != null) { + shapes[i] = aftShoulder; } return RocketComponentShape.toArray( shapes, component); } @@ -95,14 +95,14 @@ public class TransitionShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.Transition transition = (net.sf.openrocket.rocketcomponent.Transition)component; double r1 = transition.getForeRadius(); double r2 = transition.getAftRadius(); - Coordinate center = instanceOffset; + Coordinate center = componentAbsoluteLocation; Shape[] s = new Shape[2]; s[0] = new Ellipse2D.Double((center.z-r1)*S,(center.y-r1)*S,2*r1*S,2*r1*S); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java index 56f9fe356..c34733435 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/TubeFinSetShapes.java @@ -13,30 +13,31 @@ public class TubeFinSetShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( net.sf.openrocket.rocketcomponent.RocketComponent component, Transformation transformation, - Coordinate instanceOffset) { + Coordinate componentAbsoluteLocation) { net.sf.openrocket.rocketcomponent.TubeFinSet finset = (net.sf.openrocket.rocketcomponent.TubeFinSet)component; int fins = finset.getFinCount(); double length = finset.getLength(); - double outerradius = finset.getOuterRadius(); - double bodyradius = finset.getBodyRadius(); + double outerRadius = finset.getOuterRadius(); + double bodyRadius = finset.getBodyRadius(); - Coordinate[] start = finset.toAbsolute(instanceOffset); + Coordinate[] start = new Coordinate[]{ transformation.transform( componentAbsoluteLocation.sub( length/2, 0, 0) )}; + start = component.shiftCoordinates( start); Transformation baseRotation = finset.getBaseRotationTransformation(); Transformation finRotation = finset.getFinRotationTransformation(); // Translate & rotate the coordinates for (int i=0; i childrenToReplicate = new ArrayList(); - for ( int instanceNumber = 0; instanceNumber < instanceCount; instanceNumber++ ){ - childrenToReplicate.clear(); - Coordinate curInstanceOffset = componentLocation.add( instanceOffsets[instanceNumber] ); - - // get n children shapes toReplicate - for ( int childNumber = 0; childNumber < childCount; childNumber++ ){ - RocketComponent curChildComp = comp.getChild( childNumber); - getShapeTree( childrenToReplicate, curChildComp, curInstanceOffset); - } - - for ( RocketComponentShape curShape : childrenToReplicate ){ - allShapes.add( curShape); - } - - } + //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 }else{ - if( comp instanceof Rocket){ - // the Rocket doesn't have any graphics to get. - // Noop - }else{ - // for most RocketComponents - // TODO: HIGH: TEST that getThisShape will actually relocate by the given offset - RocketComponentShape[] childShapes = getThisShape( viewType, comp, parentOffset, viewTransform); - - for ( RocketComponentShape curShape : childShapes ){ - allShapes.add( curShape ); - } +// if( comp instanceof FinSet ){ +// System.err.println(">> Drawing component "+comp.getName()+" at absloc: "+componentAbsoluteLocation); +// System.err.println(" (parent was at: "+parentOffset); +// } + 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, parentOffset); - } } + + // recurse to each child + for( RocketComponent child: comp.getChildren() ){ + getShapeTree( allShapes, child, componentAbsoluteLocation); + } return; }