fixed many display issues relating to refactoring of RocketComponent axialOffset code.

This commit is contained in:
Daniel_M_Williams 2015-07-21 14:16:13 -04:00
parent 3b1de9c291
commit 2f42594acb
24 changed files with 1326 additions and 419 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)));
}

View File

@ -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);
}

View File

@ -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("<position type=\"" + type + "\">" + c.getPositionValue() + "</position>");
elements.add("<position type=\"" + type + "\">" + c.getAxialOffset() + "</position>");
}

View File

@ -84,9 +84,9 @@ public class StageSaver extends ComponentAssemblySaver {
elementsToReturn.add("<" + outside_tag + ">" + outsideFlag + "</" + outside_tag + ">");
int instanceCount = currentStage.getInstanceCount();
elementsToReturn.add("<" + instCt_tag + ">" + instanceCount + "</" + instCt_tag + ">");
double radialOffset = currentStage.getRadialPosition();
double radialOffset = currentStage.getRadialOffset();
elementsToReturn.add("<" + radoffs_tag + ">" + radialOffset + "</" + radoffs_tag + ">");
double angularOffset = currentStage.getAngularPosition();
double angularOffset = currentStage.getAngularOffset();
elementsToReturn.add("<" + startangle_tag + ">" + angularOffset + "</" + startangle_tag + ">");
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -391,8 +391,6 @@ public class Rocket extends RocketComponent {
return;
}
log.debug("Firing rocket change event " + e);
// Notify all components first
Iterator<RocketComponent> iterator = this.iterator(true);
while (iterator.hasNext()) {
@ -636,8 +634,6 @@ public class Rocket extends RocketComponent {
//////// Obligatory component information
@Override
public String getComponentName() {
//// Rocket

View File

@ -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 <code>position</code>
*/
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.
* <p>
* 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 <code>c</code> 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 <code>c</code> described in the coordinate system of
* <code>dest</code>. If <code>dest</code> is <code>null</code> returns
@ -1006,16 +1090,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* <p>
* 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 <code>c</code> in coordinates
* relative to <code>dest</code>.
*/
@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> R accept(RocketComponentVisitor<R> 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<RocketComponent> iterator = this.children.iterator();
while (iterator.hasNext()) {
iterator.next().dumpTree(false, prefix + " ");
}
}
}

View File

@ -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<StageSeparationConfiguration>(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<StageSeparationConfiguration> 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<RocketComponent> 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);
}
}

View File

@ -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();

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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<Stage> relativeStageModel = new StageSelectModel( stage );
JComboBox<Stage> relToCombo = new JComboBox<Stage>( 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<T>[] values) {
ComboBoxModel<RocketComponent.Position> posRelModel = new EnumModel<RocketComponent.Position>(component, "RelativePositionMethod",
ComboBoxModel<RocketComponent.Position> relativePositionMethodModel = new EnumModel<RocketComponent.Position>(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<RocketComponent.Position>( posRelModel );
JComboBox<?> positionMethodCombo = new JComboBox<RocketComponent.Position>( 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<Stage> relativeStageModel = new StageSelectModel( stage );
JComboBox<Stage> relToCombo = new JComboBox<Stage>( 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;
}

View File

@ -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);

View File

@ -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<finCount; fin++) {
RocketComponentShape[] finShape = new RocketComponentShape[ finCount];
for (int finNum=0; finNum<finCount; finNum++) {
Coordinate a;
Path2D.Float p;
// Make polygon
p = new Path2D.Float();
for (int i=0; i<finPoints.length; i++) {
a = transformation.transform(finset.toAbsolute(finPoints[i])[0]);
// previous version
// a = transformation.transform(finset.toAbsolute(finPoints[i])[0]);
a = transformation.transform(finSetFront.add(finPoints[i]));
if (i==0)
p.moveTo(a.x*S, a.y*S);
else
@ -54,20 +58,20 @@ public class FinSetShapes extends RocketComponentShape {
}
p.closePath();
rcs[fin] = new RocketComponentShape( p, finset);
finShape[finNum] = new RocketComponentShape( p, finset);
// Rotate fin coordinates
for (int i=0; i<finPoints.length; i++)
finPoints[i] = finRotation.transform(finPoints[i]);
}
return rcs;
return finShape;
}
public static RocketComponentShape[] getShapesBack(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate instanceOffset) {
Coordinate componentAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.FinSet finset = (net.sf.openrocket.rocketcomponent.FinSet)component;
Shape[] toReturn;
@ -90,7 +94,7 @@ public class FinSetShapes extends RocketComponentShape {
double radius = finset.getBodyRadius();
double thickness = finset.getThickness();
double height = finset.getSpan();
Coordinate compCenter = finset.getAbsolutePositionVector();
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation finRotation = finset.getFinRotationTransformation();
@ -113,13 +117,13 @@ public class FinSetShapes extends RocketComponentShape {
// Make polygon
p = new Path2D.Double();
a = transformation.transform(finset.toAbsolute(c[0])[0]);
a = transformation.transform(compCenter.add( c[0] ));
p.moveTo(a.z*S, a.y*S);
a = transformation.transform(finset.toAbsolute(c[1])[0]);
a = transformation.transform(compCenter.add( c[1] ));
p.lineTo(a.z*S, a.y*S);
a = transformation.transform(finset.toAbsolute(c[2])[0]);
a = transformation.transform(compCenter.add( c[2] ));
p.lineTo(a.z*S, a.y*S);
a = transformation.transform(finset.toAbsolute(c[3])[0]);
a = transformation.transform(compCenter.add( c[3] ));
p.lineTo(a.z*S, a.y*S);
p.closePath();
s[fin] = p;
@ -222,10 +226,11 @@ public class FinSetShapes extends RocketComponentShape {
Transformation t) {
Path2D.Float p;
Coordinate compCenter = finset.getAbsolutePositionVector();
// Make polygon
p = new Path2D.Float();
for (int i=0; i < array.length; i++) {
Coordinate a = t.transform(finset.toAbsolute(array[i])[0]);
Coordinate a = t.transform(compCenter.add( array[i]) );
if (i==0)
p.moveTo(a.z*S, a.y*S);
else

View File

@ -14,7 +14,7 @@ public class RingComponentShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide(
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;
@ -23,23 +23,23 @@ public class RingComponentShapes extends RocketComponentShape {
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 ((or-ir >= 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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<Coordinate> points = new ArrayList<Coordinate>();
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;

View File

@ -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);

View File

@ -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<start.length; i++) {
start[i] = baseRotation.transform(transformation.transform(start[i].add(0,bodyradius+outerradius,0)));
start[i] = baseRotation.transform(transformation.transform(start[i].add(0,bodyRadius+outerRadius,0)));
}
//start = baseRotation.transform(start);
Shape[] s = new Shape[fins];
for (int i=0; i<fins; i++) {
s[i] = new Rectangle2D.Double(start[0].x*S,(start[0].y-outerradius)*S,length*S,2*outerradius*S);
s[i] = new Rectangle2D.Double(start[0].x*S,(start[0].y-outerRadius)*S,length*S,2*outerRadius*S);
start = finRotation.transform(start);
}
return RocketComponentShape.toArray(s, component);
@ -46,7 +47,7 @@ public class TubeFinSetShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesBack(
net.sf.openrocket.rocketcomponent.RocketComponent component,
Transformation transformation,
Coordinate instanceOffset) {
Coordinate componentAbsoluteLocation) {
net.sf.openrocket.rocketcomponent.TubeFinSet finset = (net.sf.openrocket.rocketcomponent.TubeFinSet)component;
@ -54,7 +55,8 @@ public class TubeFinSetShapes extends RocketComponentShape {
double outerradius = finset.getOuterRadius();
double bodyradius = finset.getBodyRadius();
Coordinate[] start = finset.toAbsolute( instanceOffset);
Coordinate[] start = new Coordinate[]{ transformation.transform( componentAbsoluteLocation.sub( 0, 0, 0) )};
start = component.shiftCoordinates( start);
Transformation baseRotation = finset.getBaseRotationTransformation();
Transformation finRotation = finset.getFinRotationTransformation();

View File

@ -24,10 +24,12 @@ import net.sf.openrocket.gui.util.ColorConversion;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.MultipleComponent;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.gui.rocketfigure.RocketComponentShape;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.startup.Application;
@ -186,9 +188,8 @@ public class RocketFigure extends AbstractScaleFigure {
calculateSize();
Rocket theRocket = configuration.getRocket();
Coordinate zero = new Coordinate(0,0,0);
getShapeTree( figureShapes, theRocket, zero);
getShapeTree( figureShapes, theRocket, zero);
// System.err.println(" updating the RocketFigure.");
repaint();
fireChangeEvent();
}
@ -348,27 +349,32 @@ public class RocketFigure extends AbstractScaleFigure {
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
Motor motor = mount.getMotor(motorID);
double length = motor.getLength();
double radius = motor.getDiameter() / 2;
double motorLength = motor.getLength();
double motorRadius = motor.getDiameter() / 2;
Coordinate[] position = ((RocketComponent) mount).toAbsolute(
new Coordinate(((RocketComponent) mount).getLength() +
mount.getMotorOverhang() - length));
RocketComponent mountComponent = ((RocketComponent) mount);
Coordinate mountPosition = mountComponent.getAbsolutePositionVector();
double mountLength = mountComponent.getLength();
Coordinate[] motorPositions;
Coordinate[] clusterTop = new Coordinate[]{mountPosition.add( mountLength/2 - motorLength + mount.getMotorOverhang() , 0, 0)};
for (int i = 0; i < position.length; i++) {
position[i] = transformation.transform(position[i]);
motorPositions = mountComponent.shiftCoordinates(clusterTop);
for (int i = 0; i < motorPositions.length; i++) {
motorPositions[i] = transformation.transform(motorPositions[i]);
}
for (Coordinate coord : position) {
for (Coordinate coord : motorPositions) {
Shape s;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) {
s = new Rectangle2D.Double(EXTRA_SCALE * coord.x,
EXTRA_SCALE * (coord.y - radius), EXTRA_SCALE * length,
EXTRA_SCALE * 2 * radius);
EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * motorLength,
EXTRA_SCALE * 2 * motorRadius);
} else {
s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - radius),
EXTRA_SCALE * (coord.y - radius), EXTRA_SCALE * 2 * radius,
EXTRA_SCALE * 2 * radius);
s = new Ellipse2D.Double(EXTRA_SCALE * (coord.z - motorRadius),
EXTRA_SCALE * (coord.y - motorRadius), EXTRA_SCALE * 2 * motorRadius,
EXTRA_SCALE * 2 * motorRadius);
}
g2.setColor(fillColor);
g2.fill(s);
@ -433,53 +439,29 @@ public class RocketFigure extends AbstractScaleFigure {
RocketPanel.VIEW_TYPE viewType = this.currentViewType;
Transformation viewTransform = this.transformation;
Coordinate componentLocation = comp.getRelativePositionVector();
// Coordinate componentRelativeLocation = comp.getRelativePositionVector();
Coordinate componentAbsoluteLocation = parentOffset.add(comp.getRelativePositionVector());
if( comp instanceof MultipleComponent ){
MultipleComponent multi = (MultipleComponent)comp;
int instanceCount;
instanceCount = multi.getInstanceCount();
// get the offsets for m instances
Coordinate[] instanceOffsets = multi.getInstanceOffsets();
// replicate n children m times each
int childCount = comp.getChildCount();
ArrayList<RocketComponentShape> childrenToReplicate = new ArrayList<RocketComponentShape>();
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;
}