[Bugfix] Fixed Core Positioning Code

- Launch Lugs correctly position themselves
    (used to default to the centerline of the rocket)
    added LaunchLugTest class
- Booster Sets automatically adjust radial distance
    - based on own, and others' body radius.
- Refactored shiftCoordinates(...) => getInstanceLocations()
This commit is contained in:
Daniel_M_Williams 2015-11-19 17:53:28 -05:00
parent afe56365f4
commit ec5a3119c5
15 changed files with 516 additions and 233 deletions

View File

@ -21,7 +21,7 @@ import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.LaunchButton;
import net.sf.openrocket.rocketcomponent.RailButton;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.MassObject;
@ -164,9 +164,9 @@ class DocumentConfig {
// LaunchButton
setters.put("LaunchButton:instancecount", new IntSetter(
Reflection.findMethod(LaunchButton.class, "setInstanceCount",int.class)));
Reflection.findMethod(RailButton.class, "setInstanceCount",int.class)));
setters.put("LaunchButton:instanceseparation", new DoubleSetter(
Reflection.findMethod( LaunchButton.class, "setInstanceSeparation", double.class)));
Reflection.findMethod( RailButton.class, "setInstanceSeparation", double.class)));
// LaunchLug
setters.put("LaunchLug:instancecount", new IntSetter(

View File

@ -14,7 +14,7 @@ import net.sf.openrocket.util.Coordinate;
public class BoosterSet extends AxialStage implements FlightConfigurableComponent, RingInstanceable {
private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(BoosterSet.class);
//private static final Logger log = LoggerFactory.getLogger(BoosterSet.class);
protected int count = 1;
@ -127,7 +127,24 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
@Override
public Coordinate[] getInstanceOffsets(){
return this.shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
checkState();
final double radius = this.radialPosition_m;
final double startAngle = this.angularPosition_rad;
final double angleIncr = this.angularSeparation;
Coordinate center = Coordinate.ZERO;
double curAngle = startAngle;
Coordinate[] toReturn = new Coordinate[this.count];
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
final double curY = radius * Math.cos(curAngle);
final double curZ = radius * Math.sin(curAngle);
toReturn[instanceNumber] = center.add(0, curY, curZ );
curAngle += angleIncr;
}
return toReturn;
}
@Override
@ -142,8 +159,12 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
"(assumed reason for getting multiple parent locations into an external stage.)");
}
parentInstances[0] = parentInstances[0].add( this.position);
Coordinate[] toReturn = this.shiftCoordinates(parentInstances);
final Coordinate center = parentInstances[0].add( this.position);
Coordinate[] instanceLocations = this.getInstanceOffsets();
Coordinate[] toReturn = new Coordinate[ instanceLocations.length];
for( int i = 0; i < toReturn.length; i++){
toReturn[i] = center.add( instanceLocations[i]);
}
return toReturn;
}
@ -185,30 +206,30 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] c) {
checkState();
if (1 < c.length) {
throw new BugException("implementation of 'shiftCoordinates' assumes the coordinate array has len == 1; The length here is "+c.length+"! ");
}
double radius = this.radialPosition_m;
double angle0 = this.angularPosition_rad;
double angleIncr = this.angularSeparation;
Coordinate center = c[0];
Coordinate[] toReturn = new Coordinate[this.count];
//Coordinate thisOffset;
double thisAngle = angle0;
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
toReturn[instanceNumber] = center.add(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle));
thisAngle += angleIncr;
}
return toReturn;
}
// @Override
// protected Coordinate[] shiftCoordinates(Coordinate[] c) {
// checkState();
//
// if (1 < c.length) {
// throw new BugException("implementation of 'shiftCoordinates' assumes the coordinate array has len == 1; The length here is "+c.length+"! ");
// }
//
// double radius = this.radialPosition_m;
// double angle0 = this.angularPosition_rad;
// double angleIncr = this.angularSeparation;
// Coordinate center = c[0];
// Coordinate[] toReturn = new Coordinate[this.count];
// //Coordinate thisOffset;
// double thisAngle = angle0;
// for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
// toReturn[instanceNumber] = center.add(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle));
//
// thisAngle += angleIncr;
// }
//
// return toReturn;
// }
//
@Override
@ -216,7 +237,7 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
buffer.append(String.format("%s %-24s (stage: %d)", prefix, this.getName(), this.getStageNumber()));
buffer.append(String.format(" (len: %5.3f offset: %4.1f via: %s )\n", this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { this.getOffset() });
Coordinate[] relCoords = this.getInstanceOffsets();
Coordinate[] absCoords = this.getLocations();
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
Coordinate instanceRelativePosition = relCoords[instanceNumber];

View File

@ -102,16 +102,14 @@ public class CenteringRing extends RadiusRingComponent implements LineInstanceab
@Override
public Coordinate[] getInstanceOffsets(){
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
toReturn[0] = Coordinate.ZERO;
for ( int index=1 ; index < this.getInstanceCount(); index++){
toReturn[index] = new Coordinate(index*this.instanceSeparation,0,0,0);
for ( int index=0 ; index < this.getInstanceCount(); index++){
toReturn[index] = this.position.setX( this.position.x + index*this.instanceSeparation );
}
return toReturn;
}
@Override
public int getInstanceCount(){
return this.instanceCount;

View File

@ -7,6 +7,7 @@ import net.sf.openrocket.material.Material;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.Coordinate;
/**
* Class of components with well-defined physical appearance and which have an effect on
@ -155,7 +156,6 @@ public abstract class ExternalComponent extends RocketComponent {
}
}
@Override
protected List<RocketComponent> copyFrom(RocketComponent c) {
ExternalComponent src = (ExternalComponent) c;

View File

@ -217,47 +217,73 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
@Override
public Coordinate[] getInstanceOffsets(){
return this.shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
}
@Override
public Coordinate[] getLocations(){
if (null == this.parent) {
throw new BugException(" Attempted to get absolute position Vector of a Stage without a parent. ");
}
Coordinate[] parentInstances = this.parent.getLocations();
for( int i=0; i< parentInstances.length; i++){
parentInstances[i] = parentInstances[i].add( this.position );
}
Coordinate[] toReturn = this.shiftCoordinates(parentInstances);
return toReturn;
}
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] array) {
array = super.shiftCoordinates(array);
int count = getClusterCount();
if (count == 1)
return array;
int instanceCount = getClusterCount();
if (instanceCount == 1)
return super.getInstanceOffsets();
List<Coordinate> points = getClusterPoints();
if (points.size() != count) {
throw new BugException("Inconsistent cluster configuration, cluster count=" + count +
" point count=" + points.size());
if (points.size() != instanceCount) {
throw new BugException("Inconsistent cluster configuration, cluster count(" + instanceCount +
") != point count(" + points.size()+")");
}
Coordinate[] newArray = new Coordinate[array.length * count];
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < count; j++) {
newArray[i * count + j] = array[i].add(points.get(j));
}
Coordinate[] newArray = new Coordinate[ instanceCount];
for (int instanceNumber = 0; instanceNumber < instanceCount; instanceNumber++) {
newArray[ instanceNumber] = this.position.add( points.get(instanceNumber));
}
return newArray;
}
// @Override
// public Coordinate[] getLocations(){
// if (null == this.parent) {
// throw new BugException(" Attempted to get absolute position Vector of a Stage without a parent. ");
// }
//
// final Coordinate center = parentInstances[0].add( this.position);
// Coordinate[] instanceLocations = this.getInstanceOffsets();
// Coordinate[] toReturn = new Coordinate[ instanceLocations.length];
// for( int i = 0; i < toReturn.length; i++){
// toReturn[i] = center.add( instanceLocations[i]);
// }
//
// return toReturn;
//
// Coordinate[] parentInstances = this.parent.getLocations();
// for( int i=0; i< parentInstances.length; i++){
// parentInstances[i] = parentInstances[i].add( this.position );
// }
// Coordinate[] toReturn = this.shiftCoordinates(parentInstances);
//
// return toReturn;
// }
// @Override
// protected Coordinate[] shiftCoordinates(Coordinate[] array) {
// array = super.shiftCoordinates(array);
//
// int count = getClusterCount();
// if (count == 1)
// return array;
//
// List<Coordinate> points = getClusterPoints();
// if (points.size() != count) {
// throw new BugException("Inconsistent cluster configuration, cluster count=" + count +
// " point count=" + points.size());
// }
// Coordinate[] newArray = new Coordinate[array.length * count];
// for (int i = 0; i < array.length; i++) {
// for (int j = 0; j < count; j++) {
// newArray[i * count + j] = array[i].add(points.get(j));
// }
// }
//
// return newArray;
// }
//////////////// Motor mount /////////////////
@Override
@ -388,19 +414,19 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
buffer.append(String.format("%s %-24s (cluster: %s)", prefix, this.getName(), this.getPatternName()));
buffer.append(String.format(" (len: %5.3f offset: %4.1f via: %s )\n", this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { this.getOffset() });
Coordinate[] relCoords = this.getInstanceOffsets();
Coordinate[] absCoords = this.getLocations();
FlightConfigurationID curId = this.getRocket().getDefaultConfiguration().getFlightConfigurationID();
int count = this.getInstanceCount();
final int intanceCount = this.getInstanceCount();
MotorInstance curInstance = this.motors.get(curId);
//if( curInstance.isEmpty() ){
{
// print just the tube locations
for (int instanceNumber = 0; instanceNumber < count; instanceNumber++) {
for (int instanceNumber = 0; instanceNumber < intanceCount; instanceNumber++) {
Coordinate tubeRelativePosition = relCoords[instanceNumber];
Coordinate tubeAbsolutePosition = absCoords[instanceNumber];
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber, count,
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber+1, intanceCount,
tubeRelativePosition, tubeAbsolutePosition));
}
}
@ -410,15 +436,15 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
}else{
// curInstance has a motor ...
Motor curMotor = curInstance.getMotor();
double motorOffset = this.getLength() - curMotor.getLength();
final double motorOffset = this.getLength() - curMotor.getLength();
buffer.append(String.format("%s %-24s Thrust: %f N; (Length: %f); \n",
prefix, curMotor.getDesignation(), curMotor.getMaxThrustEstimate(), curMotor.getLength() ));
for (int instanceNumber = 0; instanceNumber < count; instanceNumber++) {
for (int instanceNumber = 0; instanceNumber < intanceCount; instanceNumber++) {
Coordinate motorRelativePosition = new Coordinate(motorOffset, 0, 0);
Coordinate tubeAbs = absCoords[instanceNumber];
Coordinate motorAbsolutePosition = new Coordinate(tubeAbs.x+motorOffset,tubeAbs.y,tubeAbs.z);
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber, count,
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber+1, intanceCount,
motorRelativePosition, motorAbsolutePosition));
}

View File

@ -20,14 +20,11 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
private double thickness;
private double radialDirection = 0;
protected double radialDistance = 0;
private int instanceCount = 1;
private double instanceSeparation = 0; // front-front along the positive rocket axis. i.e. [1,0,0];
/* These are calculated when the component is first attached to any Rocket */
private double shiftY, shiftZ;
public LaunchLug() {
super(Position.MIDDLE);
@ -113,6 +110,7 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
@Override
public void setPositionValue(double value) {
System.err.println(" positioning "+getName()+" to: "+value);
super.setPositionValue(value);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@ -145,25 +143,29 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
@Override
public Coordinate[] getInstanceOffsets(){
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
toReturn[0] = Coordinate.ZERO;
for ( int index=1 ; index < this.getInstanceCount(); index++){
toReturn[index] = new Coordinate(index*this.instanceSeparation,0,0,0);
final double xOffset = this.position.x;
final double yOffset = Math.cos(radialDirection) * (radialDistance);
final double zOffset = Math.sin(radialDirection) * (radialDistance);
for ( int index=0; index < this.getInstanceCount(); index++){
toReturn[index] = new Coordinate(xOffset + index*this.instanceSeparation, yOffset, zOffset);
}
return toReturn;
}
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] array) {
array = super.shiftCoordinates(array);
for (int i = 0; i < array.length; i++) {
array[i] = array[i].add(0, shiftY, shiftZ);
}
return array;
}
// @Override
// protected Coordinate[] shiftCoordinates(Coordinate[] array) {
// array = super.shiftCoordinates(array);
//
// for (int i = 0; i < array.length; i++) {
// array[i] = new Coordinate(xOffset + index*this.instanceSeparation, yOffset, zOffset);
// array[i] = array[i].add(0, shiftY, shiftZ);
// }
//
// return array;
// }
@Override
@ -194,10 +196,7 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
parentRadius = Math.max(s.getRadius(x1), s.getRadius(x2));
}
shiftY = Math.cos(radialDirection) * (parentRadius + radius);
shiftZ = Math.sin(radialDirection) * (parentRadius + radius);
// System.out.println("Computed shift: y="+shiftY+" z="+shiftZ);
this.radialDistance = parentRadius + radius;
}

View File

@ -109,14 +109,14 @@ public abstract class MassObject extends InternalComponent {
/**
* Shift the coordinates according to the radial position and direction.
*/
@Override
protected
final Coordinate[] shiftCoordinates(Coordinate[] array) {
for (int i = 0; i < array.length; i++) {
array[i] = array[i].add(0, shiftY, shiftZ);
}
return array;
}
// @Override
// protected
// final Coordinate[] shiftCoordinates(Coordinate[] array) {
// for (int i = 0; i < array.length; i++) {
// array[i] = array[i].add(0, shiftY, shiftZ);
// }
// return array;
// }
@Override
public final Coordinate getComponentCG() {

View File

@ -78,9 +78,27 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
@Override
public Coordinate[] getInstanceOffsets(){
return shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
checkState();
final double radius = this.radialPosition_m;
final double startAngle = this.angularPosition_rad;
final double angleIncr = this.angularSeparation;
Coordinate center = Coordinate.ZERO;
double curAngle = startAngle;
Coordinate[] toReturn = new Coordinate[this.count];
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
final double curY = radius * Math.cos(curAngle);
final double curZ = radius * Math.sin(curAngle);
toReturn[instanceNumber] = center.add(0, curY, curZ );
curAngle += angleIncr;
}
return toReturn;
}
@Override
public Coordinate[] getLocations() {
if (null == this.parent) {
@ -96,7 +114,12 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
"(assumed reason for getting multiple parent locations into an external stage.)");
}
Coordinate[] toReturn = this.shiftCoordinates(parentInstances);
final Coordinate center = parentInstances[0].add( this.position);
Coordinate[] instanceLocations = this.getInstanceOffsets();
Coordinate[] toReturn = new Coordinate[ instanceLocations.length];
for( int i = 0; i < toReturn.length; i++){
toReturn[i] = center.add( instanceLocations[i]);
}
return toReturn;
}
@ -186,31 +209,6 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] c) {
checkState();
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 center = this.position;
Coordinate[] toReturn = new Coordinate[this.count];
Coordinate thisOffset;
double thisAngle = angle0;
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
thisOffset = center.add(0, radius * Math.cos(thisAngle), radius * Math.sin(thisAngle));
toReturn[instanceNumber] = thisOffset.add(c[0]);
thisAngle += angleIncr;
}
return toReturn;
}
@Override
protected StringBuilder toDebugDetail() {
StringBuilder buf = super.toDebugDetail();

View File

@ -5,10 +5,7 @@ import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
/**
* An inner component that consists of a hollow cylindrical component. This can be
* an inner tube, tube coupler, centering ring, bulkhead etc.
*
* The properties include the inner and outer radii, length and radial position.
* ???
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/

View File

@ -15,7 +15,7 @@ import net.sf.openrocket.util.MathUtil;
* @author widget (Daniel Williams)
*
*/
public abstract class LaunchButton extends ExternalComponent implements LineInstanceable {
public abstract class RailButton extends ExternalComponent implements LineInstanceable {
private static final Translator trans = Application.getTranslator();
@ -32,7 +32,7 @@ public abstract class LaunchButton extends ExternalComponent implements LineInst
public LaunchButton() {
public RailButton() {
super(Position.MIDDLE);
radius = 0.01 / 2;
thickness = 0.001;
@ -154,16 +154,16 @@ public abstract class LaunchButton extends ExternalComponent implements LineInst
}
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] array) {
array = super.shiftCoordinates(array);
for (int i = 0; i < array.length; i++) {
array[i] = array[i].add(0, shiftY, shiftZ);
}
return array;
}
// @Override
// protected Coordinate[] shiftCoordinates(Coordinate[] array) {
// array = super.shiftCoordinates(array);
//
// for (int i = 0; i < array.length; i++) {
// array[i] = array[i].add(0, shiftY, shiftZ);
// }
//
// return array;
// }
@Override

View File

@ -172,19 +172,6 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
return 1;
}
/**
* Shift the coordinates according to the radial position and direction.
*/
@Override
protected Coordinate[] shiftCoordinates(Coordinate[] array) {
for (int i = 0; i < array.length; i++) {
array[i] = array[i].add(0, shiftY, shiftZ);
}
return array;
}
@Override
public Collection<Coordinate> getComponentBounds() {
List<Coordinate> bounds = new ArrayList<Coordinate>();

View File

@ -276,7 +276,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* @return indicates if a component is positioned via AFTER
*/
public boolean isAfter(){
return (Position.AFTER == this.getRelativePositionMethod());
return (Position.AFTER == this.relativePosition);
}
@ -292,10 +292,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* @return an array of shifted coordinates. The method may modify the contents
* of the passed array and return the array itself.
*/
protected Coordinate[] shiftCoordinates(Coordinate[] c) {
checkState();
return c;
}
// protected Coordinate[] shiftCoordinates(Coordinate[] c) {
// checkState();
// return c;
// }
/**
@ -943,12 +943,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
throw new BugException("Unknown position type: " + thePosition);
}
// if ((this instanceof BoosterSet) && (Position.ABSOLUTE == thePosition)) {
// System.err.println("Fetching position Value for: " + this.getName() + " ( " + this.getClass().getSimpleName() + ")");
// System.err.println(" polling offset set to: " + this.position.x + " via: " + this.relativePosition.name());
// System.err.println(" resultant offset: " + result + " via: " + thePosition.name());
// }
//
return result;
}
@ -997,10 +991,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
setAxialOffset(value);
}
public void setAxialOffset(double _value) {
this.setAxialOffset(this.relativePosition, _value);
this.fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
protected void setAfter(RocketComponent referenceComponent) {
checkState();
@ -1028,23 +1018,29 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z);
}
protected void setAxialOffset(Position positionMethod, double newOffset) {
// if this is the root of a hierarchy, constrain the position to zero.
if (null == this.parent) {
return;
} else if ( this.isAfter()){
positionMethod = Position.AFTER;
}
checkState();
public void setAxialOffset(double _value) {
this.setAxialOffset(this.relativePosition, _value);
this.fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
this.relativePosition = positionMethod;
this.offset = newOffset;
protected void setAxialOffset(final Position positionMethod, final double newOffset) {
checkState();
if ( this.isAfter()){
relativePosition = Position.AFTER;
}else{
this.relativePosition = positionMethod;
}
if (null == this.parent) {
// if this is the root of a hierarchy, constrain the position to zero.
this.offset = newOffset;
this.position= Coordinate.ZERO;
return;
}
final double EPSILON = 0.000001;
double newAxialPosition = Double.NaN;
double refLength = this.parent.getLength();
switch (positionMethod) {
final double refLength = this.parent.getLength();
switch (this.relativePosition) {
case ABSOLUTE:
newAxialPosition = newOffset - this.parent.position.x;
break;
@ -1062,7 +1058,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
newAxialPosition = (refLength - this.length) + newOffset;
break;
default:
throw new BugException("Unknown position type: " + positionMethod);
throw new BugException("Unknown position type: " + this.relativePosition);
}
// snap to zero if less than the threshold 'EPSILON'
@ -1072,7 +1068,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
if (Double.NaN == newAxialPosition) {
throw new BugException("setAxialOffset is broken -- attempted to update as NaN: " + this.toDebugDetail());
}
this.offset = newOffset;
this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z);
// if( this instanceof CenteringRing ){
// System.err.println("Moving "+this.getName()+"("+this.getID().substring(0, 8)+") to:"+newOffset+" via:"+positionMethod.name());
// System.err.println(" new Position = "+this.position);
// if( positionMethod == Position.BOTTOM){
// StackTraceElement[] stack = Thread.currentThread().getStackTrace();
// for( int i = 0; i < 12 ; i++){
// System.err.println( stack[i]);
// }
// }
// }
}
protected void update() {
@ -1083,11 +1091,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return this.position;
}
// public Coordinate[] getOffset() {
// return new Coordinate[]{this.position};
// }
/**
* @deprecated kept around as example code. instead use getLocations
* @return
@ -1101,17 +1104,50 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
}
/**
* Returns coordinates of this component's instances in relation to this.parent.
* <p>
* For example, the absolute position of any given instance is the parent's position
* plus the instance position returned by this method
* <p>
* NOTE: this default implementation simply returns this.position
* NOTE: the length of this array returned always equals this.getInstanceCount()
*
* @param c an array of coordinates to shift.
* @return an array of shifted coordinates. The method may modify the contents
* of the passed array and return the array itself.
*/
// @Override Me !
public Coordinate[] getInstanceOffsets(){
return new Coordinate[]{this.position};
}
public Coordinate[] getLocations() {
if (null == this.parent) {
// == improperly initialized components OR the root Rocket instance
return new Coordinate[] { Coordinate.ZERO };
} else {
Coordinate[] parentPositions = this.parent.getLocations();
int instCount = parentPositions.length;
Coordinate[] thesePositions = new Coordinate[instCount];
int parentCount = parentPositions.length;
for (int pi = 0; pi < instCount; pi++) {
thesePositions[pi] = parentPositions[pi].add(this.getOffset());
// override <instance>.getInstanceOffsets() in the subclass you want to fix.
Coordinate[] instanceOffsets = this.getInstanceOffsets();
int instanceCount = instanceOffsets.length;
// usual case optimization
if((1 == parentCount)&&(1 == instanceCount)){
return new Coordinate[]{parentPositions[0].add(instanceOffsets[0])};
}
int thisCount = instanceCount*parentCount;
Coordinate[] thesePositions = new Coordinate[thisCount];
for (int pi = 0; pi < parentCount; pi++) {
for( int ii = 0; ii < instanceCount; ii++ ){
// System.err.println(" #"+pi+", "+ii+" = "+(pi + parentCount*ii));
// System.err.println(" "+parentPositions[pi]+" + "+instanceOffsets[ii]);
thesePositions[pi + parentCount*ii] = parentPositions[pi].add(instanceOffsets[ii]);
// System.err.println(" ="+thesePositions[pi+parentCount*ii]);
}
}
return thesePositions;
}
@ -1190,15 +1226,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return toReturn;
}
protected static final Coordinate[] rebase(final Coordinate toMove[], final Coordinate source, final Coordinate dest) {
final Coordinate delta = source.sub(dest);
Coordinate[] toReturn = new Coordinate[toMove.length];
for (int coordIndex = 0; coordIndex < toMove.length; coordIndex++) {
toReturn[coordIndex] = toMove[coordIndex].add(delta);
}
return toReturn;
}
// protected static final Coordinate[] rebase(final Coordinate toMove[], final Coordinate source, final Coordinate dest) {
// final Coordinate delta = source.sub(dest);
// Coordinate[] toReturn = new Coordinate[toMove.length];
// for (int coordIndex = 0; coordIndex < toMove.length; coordIndex++) {
// toReturn[coordIndex] = toMove[coordIndex].add(delta);
// }
//
// return toReturn;
// }
/**
@ -2085,8 +2121,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
public void toDebugTreeNode(final StringBuilder buffer, final String prefix) {
buffer.append(String.format("%s %-24s; %5.3f; %24s; %24s;\n", prefix, this.getName(), this.getLength(),
this.getOffset(), this.getLocations()[0]));
buffer.append(String.format("%-42s; %5.3f; %24s; %24s;\n", prefix+" "+this.getName(),
this.getLength(), this.getOffset(), this.getLocations()[0]));
}
public void dumpTreeHelper(StringBuilder buffer, final String prefix) {

View File

@ -27,6 +27,7 @@ import net.sf.openrocket.rocketcomponent.CenteringRing;
import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.rocketcomponent.FinSet.CrossSection;
@ -316,8 +317,9 @@ public class TestRockets {
public static final Rocket makeEstesAlphaIII(){
Rocket rocket = new Rocket();
rocket.setName("Estes Alpha III / Code Verification Rocket");
AxialStage stage = new AxialStage();
stage.setName("Stage1");
stage.setName("Stage");
rocket.addChild(stage);
double noseconeLength = 0.06985;
@ -325,14 +327,31 @@ public class TestRockets {
NoseCone nosecone = new NoseCone(Transition.Shape.ELLIPSOID, noseconeLength, noseconeRadius);
nosecone.setAftShoulderLength(0.02479);
nosecone.setAftShoulderRadius(0.011811);
nosecone.setName("Nose Cone");
stage.addChild(nosecone);
double bodytubeLength = 0.19685;
double bodytubeRadius = 0.012395;
double bodytubeThickness = 0.00033;
BodyTube bodytube = new BodyTube(bodytubeLength, bodytubeRadius, bodytubeThickness);
bodytube.setName("Body Tube");
stage.addChild(bodytube);
TrapezoidFinSet finset;
{
int finCount = 3;
double finRootChord = .05715;
double finTipChord = .03048;
double finSweep = 0.06985455;
double finHeight = 0.04064;
finset = new TrapezoidFinSet(finCount, finRootChord, finTipChord, finSweep, finHeight);
finset.setThickness( 0.003175);
finset.setRelativePosition(Position.BOTTOM);
finset.setName("3 Fin Set");
bodytube.addChild(finset);
LaunchLug lug = new LaunchLug();
lug.setName("Launch Lugs");
lug.setRelativePosition(Position.TOP);
lug.setAxialOffset(0.111125);
lug.setLength(0.0508);
@ -347,21 +366,40 @@ public class TestRockets {
inner.setOuterRadius(0.009347);
inner.setThickness(0.00033);
inner.setMotorMount(true);
inner.setName("Motor Mount Tube");
bodytube.addChild(inner);
// omit other internal components... this method does not return a flyable version of the Mk 2
}
stage.addChild(bodytube);
{
// MotorBlock
EngineBlock thrustBlock= new EngineBlock();
thrustBlock.setRelativePosition(Position.TOP);
thrustBlock.setAxialOffset(0.0);
thrustBlock.setLength(0.005004);
thrustBlock.setOuterRadius(0.0090169);
thrustBlock.setThickness(0.00075);
thrustBlock.setName("Engine Block");
inner.addChild(thrustBlock);
}
int finCount = 3;
double finRootChord = .05715;
double finTipChord = .03048;
double finSweep = 0.06985455;
double finHeight = 0.04064;
TrapezoidFinSet finset = new TrapezoidFinSet(finCount, finRootChord, finTipChord, finSweep, finHeight);
finset.setThickness( 0.003175);
finset.setRelativePosition(Position.BOTTOM);
bodytube.addChild(finset);
// parachute
Parachute chute = new Parachute();
chute.setRelativePosition(Position.TOP);
chute.setName("Parachute");
chute.setAxialOffset(0.028575);
chute.setOverrideMass(0.002041);
chute.setMassOverridden(true);
bodytube.addChild(chute);
// bulkhead x2
CenteringRing centerings = new CenteringRing();
centerings.setName("Centering Rings");
centerings.setRelativePosition(Position.TOP);
centerings.setAxialOffset(0.1397);
centerings.setLength(0.00635);
centerings.setInstanceCount(2);
centerings.setInstanceSeparation(0.035);
bodytube.addChild(centerings);
}
Material material = Application.getPreferences().getDefaultComponentMaterial(null, Material.Type.BULK);
nosecone.setMaterial(material);

View File

@ -0,0 +1,74 @@
package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.*;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.TestRockets;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test;
public class LaunchLugTest extends BaseTestCase {
protected final double EPSILON = MathUtil.EPSILON;
@Test
public void testLaunchLugLocationZeroAngle() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
BodyTube body= (BodyTube)rocket.getChild(0).getChild(1);
LaunchLug lug = (LaunchLug)rocket.getChild(0).getChild(1).getChild(1);
lug.setInstanceSeparation(0.05);
lug.setInstanceCount(2);
double expX = 0.111125 + body.getLocations()[0].x;
double expR = body.getOuterRadius()+lug.getOuterRadius();
Coordinate expPos = new Coordinate( expX, expR, 0, 0);
Coordinate actPos[] = lug.getLocations();
assertEquals(" LaunchLug has the wrong x value: ", expPos.x, actPos[0].x, EPSILON);
assertEquals(" LaunchLug has the wrong y value: ", expPos.y, actPos[0].y, EPSILON);
assertEquals(" LaunchLug has the wrong z value: ", expPos.z, actPos[0].z, EPSILON);
assertEquals(" LaunchLug has the wrong weight: ", 0, actPos[0].weight, EPSILON);
assertEquals(" LaunchLug #1 is in the wrong place: ", expPos, actPos[0]);
expPos = expPos.setX( expX+0.05 );
assertEquals(" LaunchLug #2 is in the wrong place: ", expPos, actPos[1]);
}
@Test
public void testLaunchLugLocationAtAngles() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
BodyTube body= (BodyTube)rocket.getChild(0).getChild(1);
LaunchLug lug = (LaunchLug)rocket.getChild(0).getChild(1).getChild(1);
double startAngle = Math.PI/2;
lug.setRadialDirection( startAngle );
lug.setInstanceSeparation(0.05);
lug.setInstanceCount(2);
System.err.println("..created lug: at : " + lug.getInstanceOffsets()[0]);
System.err.println(" angle: "+startAngle/Math.PI*180+" deg ");
//String treeDump = rocket.toDebugTree();
//System.err.println(treeDump);
double expX = 0.111125 + body.getLocations()[0].x;
double expR = 0.015376;
double expY = Math.cos(startAngle)*expR ;
double expZ = Math.sin(startAngle)*expR ;
Coordinate expPos = new Coordinate( expX, expY, expZ, 0);
Coordinate actPos[] = lug.getLocations();
assertEquals(" LaunchLug has the wrong x value: ", expPos.x, actPos[0].x, EPSILON);
assertEquals(" LaunchLug has the wrong y value: ", expPos.y, actPos[0].y, EPSILON);
assertEquals(" LaunchLug has the wrong z value: ", expPos.z, actPos[0].z, EPSILON);
assertEquals(" LaunchLug has the wrong weight: ", 0, actPos[0].weight, EPSILON);
assertEquals(" LaunchLug is in the wrong place: ", expPos, actPos[0]);
if( 1 < actPos.length){
expPos = expPos.setX( expX+0.05 );
assertEquals(" LaunchLug #2 is in the wrong place: ", expPos, actPos[1]);
}
}
}

View File

@ -1,5 +1,15 @@
package net.sf.openrocket.rocketcomponent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.TestRockets;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test;
@ -8,16 +18,115 @@ public class RocketTest extends BaseTestCase {
@Test
public void testCopyFrom() {
Rocket r1 = net.sf.openrocket.util.TestRockets.makeIsoHaisu();
Rocket r2 = net.sf.openrocket.util.TestRockets.makeBigBlue();
// Rocket r1 = net.sf.openrocket.util.TestRockets.makeIsoHaisu();
// Rocket r2 = net.sf.openrocket.util.TestRockets.makeBigBlue();
//
// Rocket copy = (Rocket) r2.copy();
//
// ComponentCompare.assertDeepEquality(r2, copy);
//
// r1.copyFrom(copy);
//
// ComponentCompare.assertDeepEquality(r1, r2);
fail("NYI");
}
Rocket copy = (Rocket) r2.copy();
@Test
public void testEstesAlphaIII(){
final double EPSILON = MathUtil.EPSILON;
Rocket rocket = TestRockets.makeEstesAlphaIII();
ComponentCompare.assertDeepEquality(r2, copy);
// String treeDump = rocket.toDebugTree();
// System.err.println(treeDump);
r1.copyFrom(copy);
AxialStage stage= (AxialStage)rocket.getChild(0);
ComponentCompare.assertDeepEquality(r1, r2);
NoseCone nose = (NoseCone)stage.getChild(0);
BodyTube body = (BodyTube)stage.getChild(1);
FinSet fins = (FinSet)body.getChild(0);
LaunchLug lug = (LaunchLug)body.getChild(1);
InnerTube mmt = (InnerTube)body.getChild(2);
EngineBlock block = (EngineBlock)mmt.getChild(0);
Parachute chute = (Parachute)body.getChild(3);
CenteringRing center = (CenteringRing)body.getChild(4);
RocketComponent cc;
Coordinate expLoc;
Coordinate actLoc;
{
cc = nose;
expLoc = new Coordinate(0,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
cc = body;
expLoc = new Coordinate(0.06985,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
{
cc = fins;
expLoc = new Coordinate(0.20955,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
cc = lug;
expLoc = new Coordinate(0.180975, 0.015376, 0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
cc = mmt;
expLoc = new Coordinate(0.2032,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
{
cc = block;
expLoc = new Coordinate(0.2032,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
}
}
cc = chute;
expLoc = new Coordinate(0.098425,0,0);
actLoc = cc.getLocations()[0];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
cc = center;
assertThat(cc.getName()+" not instanced correctly: ", cc.getInstanceCount(), equalTo(2));
// singleton instances follow different code paths
center.setInstanceCount(1);
expLoc = new Coordinate(0.20955,0,0);
actLoc = cc.getLocations()[0];
assertEquals(" position x fail: ", expLoc.x, actLoc.x, EPSILON);
assertEquals(" position y fail: ", expLoc.y, actLoc.y, EPSILON);
assertEquals(" position z fail: ", expLoc.z, actLoc.z, EPSILON);
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
cc = center;
center.setInstanceCount(2);
Coordinate actLocs[] = cc.getLocations();
expLoc = new Coordinate(0.20955,0,0);
actLoc = actLocs[0];
// assertEquals(" position x fail: ", expLoc.x, actLoc.x, EPSILON);
// assertEquals(" position y fail: ", expLoc.y, actLoc.y, EPSILON);
// assertEquals(" position z fail: ", expLoc.z, actLoc.z, EPSILON);
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
{ // second instance
expLoc = new Coordinate(0.24455, 0, 0);
actLoc = actLocs[1];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
}
{ // second instance
assertThat(cc.getName()+" not instanced correctly: ", cc.getInstanceCount(), equalTo(2));
expLoc = new Coordinate(0.24455, 0, 0);
actLoc = actLocs[1];
assertThat(cc.getName()+" not positioned correctly: ", actLoc, equalTo(expLoc));
}
}
}
}