[fixes #471] Refactoring FreeformFinSet to accept root points outside of the base. (which generates bridge-points on-load)

This commit is contained in:
Daniel_M_Williams 2018-11-18 18:12:01 -05:00
parent 151a4d864d
commit 72c6f1d64d
2 changed files with 757 additions and 411 deletions

View File

@ -20,10 +20,11 @@ public class FreeformFinSet extends FinSet {
private static final Logger log = LoggerFactory.getLogger(FreeformFinSet.class); private static final Logger log = LoggerFactory.getLogger(FreeformFinSet.class);
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
public static final double MIN_ROOT_CHORD=0.01; // enforce this to prevent erroneous 'intersection' exceptions.
private List<Coordinate> points = new ArrayList<>(); private List<Coordinate> points = new ArrayList<>();
private static final double SNAP_SMALLER_THAN = 1e-6;
private static final double IGNORE_SMALLER_THAN = 1e-12;
public FreeformFinSet() { public FreeformFinSet() {
points.add(Coordinate.ZERO); points.add(Coordinate.ZERO);
points.add(new Coordinate(0.025, 0.05)); points.add(new Coordinate(0.025, 0.05));
@ -33,10 +34,6 @@ public class FreeformFinSet extends FinSet {
this.length = 0.05; this.length = 0.05;
} }
public FreeformFinSet(Coordinate[] finpoints) {
setPoints(finpoints);
}
/** /**
* Convert an existing fin set into a freeform fin set. The specified * Convert an existing fin set into a freeform fin set. The specified
* fin set is taken out of the rocket tree (if any) and the new component * fin set is taken out of the rocket tree (if any) and the new component
@ -68,8 +65,9 @@ public class FreeformFinSet extends FinSet {
} }
// Create the freeform fin set // Create the freeform fin set
Coordinate[] finpoints = finset.getFinPoints(); Coordinate[] finPoints = finset.getFinPoints();
freeform = new FreeformFinSet(finpoints); freeform = new FreeformFinSet();
freeform.setPoints(Arrays.asList(finPoints));
freeform.setAxialOffset(finset.getAxialMethod(), finset.getAxialOffset()); freeform.setAxialOffset(finset.getAxialMethod(), finset.getAxialOffset());
// Copy component attributes // Copy component attributes
@ -135,7 +133,7 @@ public class FreeformFinSet extends FinSet {
List<Coordinate> copy = new ArrayList<>(this.points); List<Coordinate> copy = new ArrayList<>(this.points);
this.points.remove(index); this.points.remove(index);
if( ! validate()){ if (!validate()) {
// if error, rollback. // if error, rollback.
this.points = copy; this.points = copy;
} }
@ -148,22 +146,18 @@ public class FreeformFinSet extends FinSet {
return points.size(); return points.size();
} }
/** /** maintained just for backwards compatibility:
* The first point is assumed to be at the origin. If it isn't, it will be moved there.
*
* @param newPoints new fin points ; replaces previous fin points
*/ */
public void setPoints(Coordinate[] newPoints) { public void setPoints(Coordinate[] newPoints) {
// move to zero, if applicable
if( ! Coordinate.ZERO.equals(newPoints[0])) { if( ! Coordinate.ZERO.equals(newPoints[0])) {
final Coordinate p0 = newPoints[0]; final Coordinate p0 = newPoints[0];
newPoints = translatePoints(newPoints, p0.x, p0.y); newPoints = translatePoints( newPoints, -p0.x, -p0.y);
} }
ArrayList<Coordinate> newList = new ArrayList<>(Arrays.asList( newPoints)); setPoints(new ArrayList<>(Arrays.asList(newPoints)));
setPoints( newList );
} }
/** /**
* The first point is assumed to be at the origin. If it isn't, it will be moved there. * The first point is assumed to be at the origin. If it isn't, it will be moved there.
* *
@ -172,33 +166,39 @@ public class FreeformFinSet extends FinSet {
public void setPoints( List<Coordinate> newPoints) { public void setPoints( List<Coordinate> newPoints) {
// copy the old points, in case validation fails // copy the old points, in case validation fails
List<Coordinate> copy = new ArrayList<>(this.points); List<Coordinate> copy = new ArrayList<>(this.points);
this.points = newPoints; this.points = newPoints;
this.length = newPoints.get(newPoints.size() -1).x;
update(); update();
// StackTraceElement[] stacktrack = Thread.currentThread().getStackTrace();
if("Canard fins, mounted to transition".equals(this.getName())) {
log.error(String.format("starting to set %d points @ %s", newPoints.size(), this.getName()), new NullPointerException());
System.err.println( toDebugDetail());
}
if( ! validate()){ if( ! validate()){
// on error, reset to the old points // on error, reset to the old points
this.points = copy; this.points = copy;
} }
this.length = points.get(points.size() - 1).x;
fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE); fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE);
} }
private double y_body( final double x){ private double y_body(final double x) {
return y_body( x, 0.0 ); return y_body(x, 0.0);
} }
private double y_body( final double x_target, final double x_ref){ private double y_body(final double x_target, final double x_ref) {
final SymmetricComponent sym = (SymmetricComponent)getParent(); final SymmetricComponent sym = (SymmetricComponent) getParent();
return ( sym.getRadius(x_target) - sym.getRadius( x_ref)); return (sym.getRadius(x_target) - sym.getRadius(x_ref));
} }
public void setPointRelToFin( final int index, final double x_request_fin, final double y_request_fin) throws IllegalFinPointException { public void setPointRelToFin(final int index, final double x_request_fin, final double y_request_fin) throws IllegalFinPointException {
final double x_finStart_body = getAxialFront(); // x @ fin start, body frame final double x_finStart_body = getAxialFront(); // x @ fin start, body frame
final double y_finStart_body = y_body( x_finStart_body); final double y_finStart_body = y_body(x_finStart_body);
setPoint( index, x_request_fin + x_finStart_body , y_request_fin + y_finStart_body); setPoint(index, x_request_fin + x_finStart_body, y_request_fin + y_finStart_body);
} }
/** /**
@ -222,68 +222,25 @@ public class FreeformFinSet extends FinSet {
* @param xRequest the x-coordinate. * @param xRequest the x-coordinate.
* @param yRequest the y-coordinate. * @param yRequest the y-coordinate.
*/ */
public void setPoint( final int index, final double xRequest, final double yRequest) { public void setPoint(final int index, final double xRequest, final double yRequest) {
final SymmetricComponent body = (SymmetricComponent)getParent();
final int lastPointIndex = this.points.size() - 1; if(null != this.getParent()) {
final double xFinEnd = points.get(lastPointIndex).x; if (0 == index) {
final double xFinStart = getAxialFront(); // x of fin start, body-frame clampFirstPoint(new Coordinate(xRequest, yRequest));
final double yFinStart = body.getRadius( xFinStart); // y of fin start, body-frame } else if ((this.points.size() - 1) == index) {
final double xBodyStart = -xFinStart; // x-offset from fin to body; fin-frame Coordinate priorPoint = points.get(index);
points.set(index, new Coordinate(xRequest, yRequest));
// initial guess at these values; further checks follow. clampLastPoint(priorPoint);
double xAgreed = xRequest; } else {
double yAgreed = yRequest; // interior points can never change the
points.set(index, new Coordinate(xRequest, yRequest));
// clamp x coordinates: clampInteriorPoint(index);
// within bounds, and consistent with the rest of the fin (at this time).
if( 0 == index ) {
// restrict the first point to be between the parent's start, and the last fin point
xAgreed = Math.max( xBodyStart, Math.min( xAgreed, xFinEnd - MIN_ROOT_CHORD ));
}else if( lastPointIndex == index ){
// restrict the last point to be between the first fin point, and the parent's end length.
xAgreed = Math.max( MIN_ROOT_CHORD, Math.min( xAgreed, xBodyStart + body.getLength()));
}
// adjust y-value to be consistent with body
final double yBody_atPoint= body.getRadius( xFinStart + xAgreed) - yFinStart;
if (index == 0 || index == lastPointIndex) {
// for the first and last points: set y-value to *exactly* match parent body:
yAgreed = yBody_atPoint;
}else{
// for all other points, merely insist that the point is outside the body...
yAgreed = Math.max( yAgreed, yBody_atPoint);
}
// if moving either begin or end points, we'll probably have to update the position, as well.
final AxialMethod locationMethod = getAxialMethod();
final double priorXOffset = getAxialOffset();
if( 0 == index){
movePoints( xAgreed);
this.length = points.get( lastPointIndex ).x;
if( AxialMethod.TOP == locationMethod){
setAxialOffset( AxialMethod.TOP, priorXOffset + xAgreed );
}else if(AxialMethod.MIDDLE == locationMethod){
setAxialOffset( AxialMethod.MIDDLE, priorXOffset + xAgreed/2 );
} }
}else if( lastPointIndex == index ){
points.set(index, new Coordinate( xAgreed, yAgreed ));
this.length = xAgreed;
if( AxialMethod.MIDDLE == locationMethod){
setAxialOffset( AxialMethod.MIDDLE, priorXOffset + (xAgreed - xFinEnd)/2 );
}else if(AxialMethod.BOTTOM== locationMethod){
setAxialOffset( AxialMethod.BOTTOM, priorXOffset + (xAgreed - xFinEnd) );
}
}else{
points.set(index, new Coordinate( xAgreed, yAgreed ));
} }
// this maps the last index and the next-to-last-index to the same 'testIndex' // this maps the last index and the next-to-last-index to the same 'testIndex'
int testIndex = Math.min( index, (points.size() - 2)); int testIndex = Math.min(index, (points.size() - 2));
if( intersects( testIndex)){ if (intersects(testIndex)) {
// intersection found! log error and abort! // intersection found! log error and abort!
log.error(String.format("ERROR: found an intersection while setting fin point #%d to [%6.4g, %6.4g] <body frame> : ABORTING setPoint(..) !! ", index, xRequest, yRequest)); log.error(String.format("ERROR: found an intersection while setting fin point #%d to [%6.4g, %6.4g] <body frame> : ABORTING setPoint(..) !! ", index, xRequest, yRequest));
return; return;
@ -292,12 +249,12 @@ public class FreeformFinSet extends FinSet {
fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE); fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE);
} }
private void movePoints( final double delta_x){ private void movePoints(final double delta_x, final double delta_y) {
// skip 0th index -- it's the local origin and is always (0,0) // skip 0th index -- it's the local origin and is always (0,0)
for( int index=1; index < points.size(); ++index){ for (int index = 1; index < points.size(); ++index) {
final Coordinate oldPoint = this.points.get( index); final Coordinate oldPoint = this.points.get(index);
final Coordinate newPoint = oldPoint.sub( delta_x, 0.0f, 0.0f); final Coordinate newPoint = oldPoint.add(delta_x, delta_y, 0.0f);
points.set( index, newPoint); points.set(index, newPoint);
} }
} }
@ -322,7 +279,6 @@ public class FreeformFinSet extends FinSet {
return trans.get("FreeformFinSet.FreeformFinSet"); return trans.get("FreeformFinSet.FreeformFinSet");
} }
@SuppressWarnings("unchecked")
@Override @Override
protected RocketComponent copyWithOriginalID() { protected RocketComponent copyWithOriginalID() {
RocketComponent c = super.copyWithOriginalID(); RocketComponent c = super.copyWithOriginalID();
@ -333,10 +289,10 @@ public class FreeformFinSet extends FinSet {
} }
@Override @Override
public void setAxialOffset( final AxialMethod newAxialMethod, final double newOffsetRequest){ public void setAxialOffset(final AxialMethod newAxialMethod, final double newOffsetRequest) {
super.setAxialOffset( newAxialMethod, newOffsetRequest); super.setAxialOffset(newAxialMethod, newOffsetRequest);
if( null != parent ) { if (null != parent) {
// if the new position would cause fin overhang, only allow movement up to the end of the parent component. // if the new position would cause fin overhang, only allow movement up to the end of the parent component.
// N.B. if you want a fin to overhang, add & adjust interior points. // N.B. if you want a fin to overhang, add & adjust interior points.
final double backOverhang = getAxialOffset(AxialMethod.BOTTOM); final double backOverhang = getAxialOffset(AxialMethod.BOTTOM);
@ -353,96 +309,181 @@ public class FreeformFinSet extends FinSet {
} }
@Override @Override
public void update(){ public void update() {
final int lastPointIndex = this.points.size() - 1; this.setAxialOffset(this.axialMethod, this.axialOffset);
this.length = points.get(lastPointIndex).x;
this.setAxialOffset( this.axialMethod, this.axialOffset); if(null != this.getParent()) {
clampFirstPoint(points.get(0));
for(int i=1; i < points.size()-1; i++) {
clampInteriorPoint(i);
}
clampFirstPoint(); clampLastPoint(null);
clampInteriorPoints();
clampLastPoint();
validateFinTab(); validateFinTab();
}
// if we translate the points, correct the first point, because it may be inconsistent
private void clampFirstPoint(){
double xFinStart = getAxialFront(); // x @ fin start, body frame
final double xFinOffset = getAxialOffset();
if( 0 > xFinStart ){
setAxialOffset( xFinOffset - xFinStart);
} }
} }
private void clampInteriorPoints(){ private void clampFirstPoint(final Coordinate newPoint) {
if( null == this.parent ){ final SymmetricComponent body = (SymmetricComponent) getParent();
// this is bad, but seems to only occur during unit tests.
return;
}
final SymmetricComponent symmetricParent = (SymmetricComponent)this.getParent();
final Coordinate finFront = getFinFront(); final Coordinate finFront = getFinFront();
final double xFinFront = finFront.x; // x of fin start, body-frame
final double yFinFront = finFront.y; // y of fin start, body-frame
final double xBodyStart = -getAxialFront(); // x-offset from start-to-start; fin-frame
// omit end points index double xDelta;
for( int index=1; index < (points.size()-1); ++index){ double yDelta;
final Coordinate oldPoint = this.points.get( index);
final double yBody = symmetricParent.getRadius( oldPoint.x + finFront.x); if(IGNORE_SMALLER_THAN > Math.abs(newPoint.x)){
final double yFinPoint = finFront.y+ oldPoint.y; return;
}else if (xBodyStart > newPoint.x) {
// attempt to place point in front of the start of the body
if( yBody > yFinPoint ){ // delta for new zeroth point
final Coordinate newPoint = oldPoint.setY( yBody - finFront.y ); xDelta = xBodyStart;
points.set( index, newPoint); yDelta = body.getForeRadius() - yFinFront;
points.set(0, newPoint);
points.add(0, Coordinate.ZERO);
movePoints(-xDelta, -yDelta);
//System.err.println(String.format(".... @[0]//A: delta= %f, %f", xDelta, yDelta));
}else if (xFinFront > body.getLength()) {
final double xNew = body.getLength();
final double yNew = yFinFront - body.getAftRadius();
points.set(0, points.set(0, new Coordinate(xNew, yNew)));
xDelta = xNew - xFinFront;
yDelta = yNew - yFinFront;
movePoints(-xDelta, -yDelta);
//System.err.println(String.format(".... @[0]//B: delta= %f, %f", xDelta, yDelta));
}else {
// distance to move the entire fin by:
xDelta = newPoint.x;
yDelta = body.getRadius(xFinFront + xDelta) - yFinFront;
movePoints(-xDelta, -yDelta);
//System.err.println(String.format(".... @[0]//C: delta= %f, %f", xDelta, yDelta));
}
final int lastIndex = points.size()-1;
this.length = points.get(lastIndex).x;
if (AxialMethod.TOP == getAxialMethod()) {
setAxialOffset(AxialMethod.TOP, getAxialOffset() + xDelta);
} else if (AxialMethod.MIDDLE == getAxialMethod()) {
setAxialOffset(AxialMethod.MIDDLE, getAxialOffset() + xDelta / 2);
}
}
private void clampInteriorPoint(final int index) {
final SymmetricComponent sym = (SymmetricComponent) this.getParent();
final double xPrior = points.get(index).x;
final double yPrior = points.get(index).y;
final Coordinate finFront = getFinFront();
final double xFinFront = finFront.x; // x of fin start, body-frame
final double yFinFront = finFront.y; // y of fin start, body-frame
final double yBody = sym.getRadius(xPrior + xFinFront) - yFinFront;
// ensure that an interior point is outside of its mounting body:
if (yBody > yPrior) {
points.set(index, points.get(index).setY(yBody));
}
}
private void clampLastPoint(final Coordinate prior) {
final SymmetricComponent body = (SymmetricComponent) getParent();
final double xFinStart = getAxialFront(); // x of fin start, body-frame
final double yFinStart = body.getRadius(xFinStart); // y of fin start, body-frame
final double xBodyStart = -getAxialFront(); // x-offset from start-to-start; fin-frame
final double xBodyEnd = xBodyStart + body.getLength(); /// x-offset from start-to-body; fin-frame
int lastIndex = points.size() - 1;
final Coordinate cur = points.get(lastIndex);
double xDelta=0;
if (xBodyEnd < cur.x) {
if(SNAP_SMALLER_THAN > Math.abs(xBodyEnd - cur.x)){
points.set( lastIndex, new Coordinate(xBodyEnd, body.getAftRadius() - yFinStart));
}else {
// the last point is placed after the end of the mount-body
points.add(new Coordinate(xBodyEnd, body.getAftRadius() - yFinStart));
}
if(null != prior) {
xDelta = xBodyEnd - prior.x;
}else{
xDelta = xBodyEnd - cur.x;
}
//System.err.println(String.format(".... @[-1]//A: delta= %f", xDelta));
}else if (cur.x < 0) {
// the last point is positioned ahead of the first point.
points.set(lastIndex, Coordinate.ZERO);
xDelta = cur.x;
//System.err.println(String.format(".... @[-1]//B: delta= %f", xDelta));
} else {
if(null != prior) {
xDelta = cur.x - prior.x;
}
double yBody = body.getRadius(xFinStart + cur.x) - yFinStart;
if(IGNORE_SMALLER_THAN < Math.abs(yBody - cur.y)) {
// for the first and last points: set y-value to *exactly* match parent body:
points.set(lastIndex, new Coordinate(cur.x, yBody));
}
//System.err.println(String.format(".... @[-1]//C: delta = %f", xDelta));
}
if(IGNORE_SMALLER_THAN < Math.abs(xDelta)) {
lastIndex = points.size()-1;
this.length = points.get(lastIndex).x;
if (AxialMethod.MIDDLE == getAxialMethod()) {
setAxialOffset(AxialMethod.MIDDLE, getAxialOffset() + xDelta / 2);
} else if (AxialMethod.BOTTOM == getAxialMethod()) {
setAxialOffset(AxialMethod.BOTTOM, getAxialOffset() + xDelta);
} }
} }
} }
// if we translate the points, the final point may become inconsistent
private void clampLastPoint(){
if( null == this.parent ){
// this is bad, but seems to only occur during unit tests.
return;
}
final SymmetricComponent body = (SymmetricComponent)getParent();
// clamp the final x coord to the end of the parent body.
final int lastPointIndex = points.size() - 1;
final Coordinate oldPoint = points.get( lastPointIndex);
final double xFinStart_body = getAxialFront(); // x @ fin start, body frame
final double xBodyEnd_fin = body.getLength() - xFinStart_body;
double x_clamped = Math.min( oldPoint.x, xBodyEnd_fin);
double y_clamped = body.getRadius( x_clamped+xFinStart_body) - body.getRadius( xFinStart_body);
points.set( lastPointIndex, new Coordinate( x_clamped, y_clamped, 0));
}
private boolean validate() { private boolean validate() {
final Coordinate firstPoint = this.points.get(0); final Coordinate firstPoint = this.points.get(0);
if (firstPoint.x != 0 || firstPoint.y != 0 ){ if (firstPoint.x != 0 || firstPoint.y != 0) {
log.error("Start point illegal -- not located at (0,0): "+firstPoint+ " ("+ getName()+")"); log.error("Start point illegal -- not located at (0,0): " + firstPoint + " (" + getName() + ")");
return false; return false;
} }
final Coordinate lastPoint = this.points.get( points.size() -1); final Coordinate lastPoint = this.points.get(points.size() - 1);
if( lastPoint.x < 0){ if (lastPoint.x < 0) {
log.error("End point illegal: end point starts in front of start point: "+lastPoint.x); log.error("End point illegal: end point starts in front of start point: " + lastPoint.x);
return false; return false;
} }
// the last point *is* restricted to be on the surface of its owning component: // the last point *is* restricted to be on the surface of its owning component:
SymmetricComponent symBody = (SymmetricComponent)this.getParent(); SymmetricComponent symBody = (SymmetricComponent) this.getParent();
if( null != symBody ){ if (null != symBody) {
final double startOffset = this.getAxialFront(); final double startOffset = this.getAxialFront();
final Coordinate finStart = new Coordinate( startOffset, symBody.getRadius(startOffset) ); final Coordinate finStart = new Coordinate(startOffset, symBody.getRadius(startOffset));
// campare x-values // campare x-values
final Coordinate finAtLast = lastPoint.add(finStart); final Coordinate finAtLast = lastPoint.add(finStart);
if( symBody.getLength() < finAtLast.x ){ if (symBody.getLength() < finAtLast.x) {
log.error("End point falls after parent body ends: ["+symBody.getName()+"]. Exception: ", new IllegalFinPointException("Fin ends after its parent body \""+symBody.getName()+"\". Ignoring.")); log.error("End point falls after parent body ends: [" + symBody.getName() + "]. Exception: ",
new IllegalFinPointException("Fin ends after its parent body \"" + symBody.getName() + "\". Ignoring."));
log.error(String.format(" ..fin position: (x: %12.10f via: %s)", this.axialOffset, this.axialMethod.name())); log.error(String.format(" ..fin position: (x: %12.10f via: %s)", this.axialOffset, this.axialMethod.name()));
log.error(String.format(" ..Body Length: %12.10f finLength: %12.10f", symBody.getLength(), this.getLength())); log.error(String.format(" ..Body Length: %12.10f finLength: %12.10f", symBody.getLength(), this.getLength()));
log.error(String.format(" ..fin endpoint: (x: %12.10f, y: %12.10f)", finAtLast.x, finAtLast.y)); log.error(String.format(" ..fin endpoint: (x: %12.10f, y: %12.10f)", finAtLast.x, finAtLast.y));
@ -450,16 +491,17 @@ public class FreeformFinSet extends FinSet {
} }
// compare the y-values // compare the y-values
final Coordinate bodyAtLast = finAtLast.setY( symBody.getRadius( finAtLast.x ) ); final Coordinate bodyAtLast = finAtLast.setY(symBody.getRadius(finAtLast.x));
if( 0.0001 < Math.abs( finAtLast.y - bodyAtLast.y) ){ if (0.0001 < Math.abs(finAtLast.y - bodyAtLast.y)) {
String numbers = String.format( "finStart=(%6.2g,%6.2g) // fin_end=(%6.2g,%6.2g) // body=(%6.2g,%6.2g)", finStart.x, finStart.y, finAtLast.x, finAtLast.y, bodyAtLast.x, bodyAtLast.y ); String numbers = String.format("finStart=(%6.2g,%6.2g) // fin_end=(%6.2g,%6.2g) // body=(%6.2g,%6.2g)", finStart.x, finStart.y, finAtLast.x, finAtLast.y, bodyAtLast.x, bodyAtLast.y);
log.error("End point does not touch its parent body ["+symBody.getName()+"]. exception: ", new IllegalFinPointException("End point does not touch its parent body! Expected: "+numbers)); log.error("End point does not touch its parent body [" + symBody.getName() + "]. exception: ",
log.error(" .."+numbers); new IllegalFinPointException("End point does not touch its parent body! Expected: " + numbers));
log.error(" .." + numbers);
return false; return false;
} }
} }
if( intersects()){ if (intersects()) {
log.error("found intersection in finset points!"); log.error("found intersection in finset points!");
return false; return false;
} }
@ -481,9 +523,9 @@ public class FreeformFinSet extends FinSet {
* *
* @return true if an intersection is found * @return true if an intersection is found
*/ */
public boolean intersects( ){ public boolean intersects() {
for( int index=0; index < (this.points.size()-1); ++index ){ for (int index = 0; index < (this.points.size() - 1); ++index) {
if( intersects( index )){ if (intersects(index)) {
return true; return true;
} }
} }
@ -493,31 +535,37 @@ public class FreeformFinSet extends FinSet {
/** /**
* Check if the line segment from targetIndex to targetIndex+1 intersects with any other part of the fin. * Check if the line segment from targetIndex to targetIndex+1 intersects with any other part of the fin.
* *
*
*
* @return true if an intersection was found * @return true if an intersection was found
*/ */
private boolean intersects( final int targetIndex){ private boolean intersects(final int targetIndex) {
if( (points.size()-2) < targetIndex ){ if ((points.size() - 2) < targetIndex) {
throw new IndexOutOfBoundsException("request validate of non-existent fin edge segment: "+ targetIndex + "/"+points.size()); throw new IndexOutOfBoundsException("request validate of non-existent fin edge segment: " + targetIndex + "/" + points.size());
} }
// (pre-check the indices above.) // (pre-check the indices above.)
Point2D.Double p1 = new Point2D.Double( points.get(targetIndex).x, points.get(targetIndex).y); final Point2D.Double pt1 = new Point2D.Double(points.get(targetIndex).x, points.get(targetIndex).y);
Point2D.Double p2 = new Point2D.Double( points.get(targetIndex+1).x, points.get(targetIndex+1).y); final Point2D.Double pt2 = new Point2D.Double(points.get(targetIndex + 1).x, points.get(targetIndex + 1).y);
Line2D.Double targetLine = new Line2D.Double( p1, p2); final Line2D.Double targetLine = new Line2D.Double(pt1, pt2);
for (int comparisonIndex = 0; comparisonIndex < (points.size()-1); ++comparisonIndex ) { for (int comparisonIndex = targetIndex+1; comparisonIndex < (points.size() - 1); ++comparisonIndex) {
if( 2 > Math.abs( targetIndex - comparisonIndex) ){ if (2 > Math.abs(targetIndex - comparisonIndex)) {
// a line segment will trivially not intersect with itself // a line segment will trivially not intersect with itself
// nor can adjacent line segments intersect with each other, because they share a common endpoint. // nor can adjacent line segments intersect with each other, because they share a common endpoint.
continue; continue;
} }
final Point2D.Double pc1 = new Point2D.Double(points.get(comparisonIndex).x, points.get(comparisonIndex).y); // p1
final Point2D.Double pc2 = new Point2D.Double(points.get(comparisonIndex + 1).x, points.get(comparisonIndex + 1).y); // p2
Line2D.Double comparisonLine = new Line2D.Double( points.get(comparisonIndex).x, points.get(comparisonIndex).y, // p1 // special case for when the first and last points are co-located.
points.get(comparisonIndex+1).x, points.get(comparisonIndex+1).y); // p2 if((0==targetIndex)&&(points.size()==comparisonIndex+2)&&(IGNORE_SMALLER_THAN > Math.abs(pt1.distance(pc2)))){
continue;
}
if ( targetLine.intersectsLine( comparisonLine ) ) { final Line2D.Double comparisonLine = new Line2D.Double(pc1, pc2);
if (targetLine.intersectsLine(comparisonLine)) {
log.error(String.format("Found intersection at %d-%d and %d-%d", targetIndex, targetIndex+1, comparisonIndex, comparisonIndex+1));
log.error(String.format(" between (%g, %g) => (%g, %g)", pt1.x, pt1.y, pt2.x, pt2.y));
log.error(String.format(" and (%g, %g) => (%g, %g)", pc1.x, pc1.y, pc2.x, pc2.y));
return true; return true;
} }
} }

View File

@ -117,7 +117,7 @@ public class FreeformFinSetTest extends BaseTestCase {
new Coordinate( 0.0, 0.0), new Coordinate( 0.0, 0.0),
new Coordinate( 0.4, 1.0), new Coordinate( 0.4, 1.0),
new Coordinate( 0.6, 1.0), new Coordinate( 0.6, 1.0),
new Coordinate( 0.8, 0.9) new Coordinate( 0.8, 0.9) // y-value should be automaticaly adjusted to snap to body
}; };
fins.setPoints(points); fins.setPoints(points);
@ -325,13 +325,15 @@ public class FreeformFinSetTest extends BaseTestCase {
assertEquals(Shape.ELLIPSOID, body.getType()); assertEquals(Shape.ELLIPSOID, body.getType());
assertEquals(1.0, body.getLength(), EPSILON); assertEquals(1.0, body.getLength(), EPSILON);
assertEquals(AxialMethod.TOP, fins.getAxialMethod());
assertEquals(0.02, fins.getAxialOffset(), EPSILON);
assertEquals(0.8, fins.getLength(), EPSILON); assertEquals(0.8, fins.getLength(), EPSILON);
final Coordinate[] finPoints = fins.getFinPoints(); final Coordinate[] finPoints = fins.getFinPoints();
assertEquals(4, finPoints.length); assertEquals(4, finPoints.length);
assertEquals(finPoints[0], Coordinate.ZERO); assertEquals(finPoints[0], Coordinate.ZERO);
assertEquals(finPoints[1], new Coordinate(0.4, 1.0)); assertEquals(finPoints[1], new Coordinate(0.4, 1.0));
assertEquals(finPoints[2], new Coordinate(0.6, 1.0)); assertEquals(finPoints[2], new Coordinate(0.6, 1.0));
assertEquals(finPoints[3], new Coordinate(0.8, 0.78466912)); assertEquals(finPoints[3], new Coordinate(0.8, 0.78466912));
// [1] [2] // [1] [2]
// +======+ // +======+
// / \ [3] // / \ [3]
@ -347,10 +349,10 @@ public class FreeformFinSetTest extends BaseTestCase {
final double expectedWettedArea = 0.13397384; final double expectedWettedArea = 0.13397384;
final double actualWettedArea = fins.getPlanformArea(); final double actualWettedArea = fins.getPlanformArea();
Coordinate wcg = fins.getCG(); // relative to parent Coordinate wcg = fins.getCG(); // relative to parent
assertEquals("Calculated fin area is wrong: ", expectedWettedArea, actualWettedArea, EPSILON); assertEquals("Calculated fin area is wrong: ", expectedWettedArea, actualWettedArea, EPSILON);
assertEquals("Calculated fin centroid is wrong! ", 0.4793588, wcg.x, EPSILON); assertEquals("Calculated fin centroid is wrong! ", 0.4793588, wcg.x, EPSILON);
assertEquals("Calculated fin centroid is wrong! ", 0.996741, wcg.y, EPSILON); assertEquals("Calculated fin centroid is wrong! ", 0.996741, wcg.y, EPSILON);
} }
@ -425,30 +427,6 @@ public class FreeformFinSetTest extends BaseTestCase {
assertEquals(0.8, added.y, 0.1); assertEquals(0.8, added.y, 0.1);
} }
@Test
public void testSetPoint_firstPoint_boundsCheck() throws IllegalFinPointException {
// more transitions trigger more complicated positioning math:
Rocket rkt = createTemplateRocket();
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(0).getChild(0);
final int startIndex = 0;
final int lastIndex = fins.getPointCount()-1;
// assert pre-conditions
assertEquals( 1, fins.getFinCount());
assertEquals( 3, lastIndex);
fins.setPoint( startIndex, -1, -1);
final Coordinate act_p_0 = fins.getFinPoints()[0];
{ // first point x is restricted to the front of the parent body:
assertEquals( 0.0, act_p_0.x, EPSILON);
assertEquals( AxialMethod.TOP, fins.getAxialMethod() );
assertEquals( 0.0, fins.getAxialOffset(), EPSILON);
}
{// first point y is restricted to the body
assertEquals( 0.0, act_p_0.y, EPSILON);
}
}
@Test @Test
public void testSetFirstPoint() throws IllegalFinPointException { public void testSetFirstPoint() throws IllegalFinPointException {
// more transitions trigger more complicated positioning math: // more transitions trigger more complicated positioning math:
@ -458,7 +436,6 @@ public class FreeformFinSetTest extends BaseTestCase {
final Coordinate[] initialPoints = fins.getFinPoints(); final Coordinate[] initialPoints = fins.getFinPoints();
// assert pre-conditions: // assert pre-conditions:
assertEquals(0.4, fins.getAxialFront(), EPSILON);
assertEquals(0.4, fins.getLength(), EPSILON); assertEquals(0.4, fins.getLength(), EPSILON);
assertEquals(initialPoints[0], Coordinate.ZERO); assertEquals(initialPoints[0], Coordinate.ZERO);
assertEquals(initialPoints[1], new Coordinate(0.4, 0.2)); assertEquals(initialPoints[1], new Coordinate(0.4, 0.2));
@ -466,48 +443,146 @@ public class FreeformFinSetTest extends BaseTestCase {
assertEquals(1.0, finMount.getLength(), EPSILON); assertEquals(1.0, finMount.getLength(), EPSILON);
assertEquals(0.8, finMount.getRadius(fins.getAxialFront()), EPSILON); assertEquals(0.8, finMount.getRadius(fins.getAxialFront()), EPSILON);
// for a fin at these positions: { // case 1:
final AxialMethod[] inputMethods = { AxialMethod.TOP, AxialMethod.TOP, AxialMethod.MIDDLE, AxialMethod.MIDDLE, AxialMethod.BOTTOM, AxialMethod.BOTTOM}; fins.setAxialOffset( AxialMethod.TOP, 0.1);
final double[] inputOffsets = { 0.1, 0.1, 0.0, 0.0, 0.0, 0.0}; fins.setPoints(initialPoints);
// move first by this delta... // vvvv function under test vvvv
final double[] xDelta = { 0.2, -0.2, 0.1, -0.1, 0.1, -0.1}; fins.setPoint( 0, 0.2, 0.1f);
// and check against these expected values:
final double[] expectedFinStartx = { 0.3, 0.0, 0.4, 0.2, 0.7, 0.5};
final double[] expectedFinStarty = { 0.85, 1.0, 0.8, 0.9, 0.65, 0.75};
final double[] expectedFinLength = { 0.2, 0.5, 0.3, 0.5, 0.3, 0.5};
final double[] expectedFinOffset = { 0.3, 0.0, 0.05, -0.05, 0.0, 0.0};
final double[] expectedMidpointOffset = { 0.2, 0.5, 0.3, 0.5, 0.3, 0.5};
final double[] expectedFinalPointOffset = { 0.2, 0.5, 0.3, 0.5, 0.3, 0.5};
for( int caseIndex=0; caseIndex < inputMethods.length; ++caseIndex ){
// this builds a string to describe this particular iteration, and provide useful output, should it fail.
StringBuilder buf = new StringBuilder();
buf.append(String.format("\n## For Case #%d: [%s]@%4.2f \n", caseIndex, inputMethods[caseIndex].name(), inputOffsets[caseIndex]));
buf.append(String.format(" >> setting initial point to: ( %6.4f, %6.4f) \n", xDelta[caseIndex], 0.0f));
final String dump = buf.toString();
fins.setAxialOffset( inputMethods[caseIndex], inputOffsets[caseIndex]);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( 0, xDelta[caseIndex], 0.1f);
// ^^^^ function under test ^^^^ // ^^^^ function under test ^^^^
//System.err.println(String.format("@[%d]:", caseIndex)); assertEquals(0.3, fins.getFinFront().x, EPSILON);
//System.err.println(fins.toDebugDetail()); assertEquals(0.85, fins.getFinFront().y, EPSILON);
assertEquals("Fin front not updated as expected..."+dump, expectedFinStartx[caseIndex], fins.getAxialFront(), EPSILON); final Coordinate[] postPoints = fins.getFinPoints();
assertEquals("Fin front not updated as expected..."+dump, expectedFinStarty[caseIndex], fins.getFinFront().y, EPSILON); assertEquals(postPoints.length, 3);
final Coordinate[] postPoints = fins.getFinPoints(); // middle point:
assertEquals("Middle fin point has moved!: "+dump, expectedMidpointOffset[caseIndex], postPoints[1].x, EPSILON); assertEquals(0.2, postPoints[1].x, EPSILON);
assertEquals("Final fin point has moved!: "+dump, expectedFinalPointOffset[caseIndex], postPoints[2].x, EPSILON); assertEquals(0.3, postPoints[1].y, EPSILON);
assertEquals(0.3f, fins.getAxialOffset(), EPSILON);
assertEquals(0.2f, fins.getLength(), EPSILON);
}{ // case 2:
fins.setAxialOffset( AxialMethod.TOP, 0.1);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( 0, -0.2, 0.1f);
// ^^^^ function under test ^^^^
assertEquals(0.0, fins.getFinFront().x, EPSILON);
assertEquals(1.0, fins.getFinFront().y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 4);
// pseudo-front point
assertEquals(-0.1, postPoints[1].x, EPSILON);
assertEquals(0.05, postPoints[1].y, EPSILON);
assertEquals(0.5, postPoints[2].x, EPSILON);
assertEquals(0.15, postPoints[2].y, EPSILON);
assertEquals(0.0f, fins.getAxialOffset(), EPSILON);
assertEquals(0.5f, fins.getLength(), EPSILON);
}{ // case 3:
fins.setAxialOffset( AxialMethod.MIDDLE, 0.0);
fins.setPoints(initialPoints);
assertEquals(0.3, fins.getFinFront().x, EPSILON);
// vvvv function under test vvvv
fins.setPoint( 0, 0.1, 0.1f);
// ^^^^ function under test ^^^^
assertEquals(0.4, fins.getFinFront().x, EPSILON);
assertEquals(0.8, fins.getFinFront().y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid-point
assertEquals(0.3, postPoints[1].x, EPSILON);
assertEquals(0.25, postPoints[1].y, EPSILON);
assertEquals(0.3, postPoints[2].x, EPSILON);
assertEquals(-0.15, postPoints[2].y, EPSILON);
assertEquals(0.05f, fins.getAxialOffset(), EPSILON);
assertEquals(0.3f, fins.getLength(), EPSILON);
}{ // case 4:
fins.setAxialOffset( AxialMethod.MIDDLE, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( 0, -0.1, 0.1f);
// ^^^^ function under test ^^^^
assertEquals(0.2, fins.getFinFront().x, EPSILON);
assertEquals(0.9, fins.getFinFront().y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid point
assertEquals(0.5, postPoints[1].x, EPSILON);
//assertEquals(0.15, postPoints[1].y, EPSILON);
assertEquals(0.5, postPoints[2].x, EPSILON);
//assertEquals(0.15, postPoints[2].y, EPSILON);
assertEquals(-0.05f, fins.getAxialOffset(), EPSILON);
assertEquals(0.5f, fins.getLength(), EPSILON);
}{ // case 5:
fins.setAxialOffset( AxialMethod.BOTTOM, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( 0, 0.1, 0.1f);
// ^^^^ function under test ^^^^
assertEquals(0.7, fins.getFinFront().x, EPSILON);
assertEquals(0.65, fins.getFinFront().y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid-point
assertEquals(0.3, postPoints[1].x, EPSILON);
//assertEquals(0.05, postPoints[1].y, EPSILON);
assertEquals(0.3, postPoints[2].x, EPSILON);
//assertEquals(0.15, postPoints[2].y, EPSILON);
assertEquals(0.0f, fins.getAxialOffset(), EPSILON);
assertEquals(0.3f, fins.getLength(), EPSILON);
}{ // case 6:
fins.setAxialOffset( AxialMethod.BOTTOM, 0.0);
fins.setPoints(initialPoints);
assertEquals(3, fins.getPointCount());
// vvvv function under test vvvv
fins.setPoint( 0, -0.1, 0.1f);
// ^^^^ function under test ^^^^
assertEquals(0.5, fins.getFinFront().x, EPSILON);
assertEquals(0.75, fins.getFinFront().y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(3, postPoints.length);
// mid-point
assertEquals(0.5, postPoints[1].x, EPSILON);
assertEquals(0.15, postPoints[1].y, EPSILON);
assertEquals(0.5, postPoints[2].x, EPSILON);
assertEquals(-0.25, postPoints[2].y, EPSILON);
assertEquals(0.0f, fins.getAxialOffset(), EPSILON);
assertEquals(0.5f, fins.getLength(), EPSILON);
}
assertEquals("Fin offset not updated as expected..."+dump, expectedFinOffset[caseIndex], fins.getAxialOffset(), EPSILON);
assertEquals("Fin length not updated as expected..."+dump, expectedFinLength[caseIndex], fins.getLength(), EPSILON);
}
} }
@Test @Test
@ -516,142 +591,299 @@ public class FreeformFinSetTest extends BaseTestCase {
Transition finMount = (Transition) rkt.getChild(0).getChild(2); Transition finMount = (Transition) rkt.getChild(0).getChild(2);
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0); FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
final Coordinate[] initialPoints = fins.getFinPoints(); final Coordinate[] initialPoints = fins.getFinPoints();
final int lastIndex = initialPoints.length - 1;
final double xf = initialPoints[lastIndex].x;
// assert pre-conditions: // assert pre-conditions:
assertEquals(0.4, fins.getLength(), EPSILON); assertEquals(0.4, fins.getLength(), EPSILON);
assertEquals(0.4, fins.getAxialFront(), EPSILON);
assertEquals(initialPoints[0], Coordinate.ZERO); assertEquals(initialPoints[0], Coordinate.ZERO);
assertEquals(initialPoints[1], new Coordinate(0.4, 0.2)); assertEquals(initialPoints[1], new Coordinate(0.4, 0.2));
assertEquals(initialPoints[2], new Coordinate(0.4, -0.2)); assertEquals(initialPoints[2], new Coordinate(0.4, -0.2));
assertEquals(1.0, finMount.getLength(), EPSILON); assertEquals(1.0, finMount.getLength(), EPSILON);
assertEquals(0.8, finMount.getRadius(fins.getAxialFront()), EPSILON); assertEquals(0.8, finMount.getRadius(fins.getAxialFront()), EPSILON);
final int lastIndex = initialPoints.length - 1; { // case 1:
fins.setAxialOffset( AxialMethod.TOP, 0.1);
fins.setPoints(initialPoints);
// set to these positions // vvvv function under test vvvv
final AxialMethod[] inputMethods = {AxialMethod.TOP, AxialMethod.TOP, AxialMethod.MIDDLE, AxialMethod.MIDDLE, AxialMethod.BOTTOM, AxialMethod.BOTTOM}; fins.setPoint( lastIndex, xf+0.2, -0.3f);
final double[] inputOffsets = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // ^^^^ function under test ^^^^
// set first point to this location... final Coordinate[] postPoints = fins.getFinPoints();
final double[] xDelta= { 0.1, -0.1, 0.1, -0.1, 0.1, -0.1}; assertEquals(postPoints.length, 3);
// and check against these expected values: // middle point:
final double[] expectedFinStartx = { 0.0, 0.0, 0.3, 0.3, 0.6, 0.6}; assertEquals(0.4, postPoints[1].x, EPSILON);
final double[] expectedFinStarty = { 1.0, 1.0, 0.85, 0.85, 0.7, 0.7}; assertEquals(0.2, postPoints[1].y, EPSILON);
final double[] expectedFinOffset = { 0.0, 0.0, 0.05, -0.05, 0.0, -0.1};
final double[] expectedFinalPointOffset = { 0.5, 0.3, 0.5, 0.3, 0.4, 0.3};
for( int caseIndex=0; caseIndex < inputMethods.length; ++caseIndex ){ // last point:
final double xRequest = initialPoints[lastIndex].x + xDelta[caseIndex]; assertEquals(0.6, postPoints[2].x, EPSILON);
final double yRequest = Double.NaN; // irrelevant; will be clamped to the body regardless assertEquals(-0.3, postPoints[2].y, EPSILON);
// this builds a string to describe this particular iteration, and provide useful output, should it fail. assertEquals(0.1, fins.getFinFront().x, EPSILON);
StringBuilder buf = new StringBuilder(); assertEquals(0.95, fins.getFinFront().y, EPSILON);
buf.append(String.format("\n## For Case #%d: [%s]@%4.2f \n", caseIndex, inputMethods[caseIndex].name(), inputOffsets[caseIndex])); assertEquals(0.6, fins.getLength(), EPSILON);
buf.append(String.format(" >> setting last point to: ( %6.4f, %6.4f) \n", xRequest, yRequest));
final String dump = buf.toString();
fins.setAxialOffset( inputMethods[caseIndex], inputOffsets[caseIndex]); }{ // case 2:
fins.setPoints(initialPoints); fins.setAxialOffset( AxialMethod.TOP, 0.1);
fins.setPoints(initialPoints);
// vvvv function under test vvvv // vvvv function under test vvvv
fins.setPoint( lastIndex, xRequest, yRequest); fins.setPoint( lastIndex, xf - 0.2, 0.1f);
// ^^^^ function under test ^^^^ // ^^^^ function under test ^^^^
// System.err.println(String.format("@[%d]:", caseIndex)); final Coordinate[] postPoints = fins.getFinPoints();
// System.err.println(fins.toDebugDetail()); assertEquals(postPoints.length, 3);
assertEquals("Fin offset not updated as expected..."+dump, expectedFinOffset[caseIndex], fins.getAxialOffset(), EPSILON); // middle point:
assertEquals("Fin front not updated as expected..."+dump, expectedFinStartx[caseIndex], fins.getAxialFront(), EPSILON); assertEquals(0.4, postPoints[1].x, EPSILON);
assertEquals("Fin front not updated as expected..."+dump, expectedFinStarty[caseIndex], fins.getFinFront().y, EPSILON); assertEquals(0.2, postPoints[1].y, EPSILON);
final Coordinate[] postPoints = fins.getFinPoints(); // last point:
assertEquals("Middle fin point has moved!: "+dump, initialPoints[1].x, postPoints[1].x, EPSILON); assertEquals(0.2, postPoints[2].x, EPSILON);
assertEquals("Final fin point has moved!: "+dump, expectedFinalPointOffset[caseIndex], postPoints[2].x, EPSILON); assertEquals(-0.1, postPoints[2].y, EPSILON);
assertEquals("Fin length not updated as expected..."+dump, expectedFinalPointOffset[caseIndex], fins.getLength(), EPSILON);
} assertEquals(0.1, fins.getFinFront().x, EPSILON);
assertEquals(0.95, fins.getFinFront().y, EPSILON);
assertEquals(0.2f, fins.getLength(), EPSILON);
}{ // case 3:
fins.setAxialOffset( AxialMethod.MIDDLE, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( lastIndex, xf + 0.1, 0.1f);
// ^^^^ function under test ^^^^
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid-point
assertEquals(0.4, postPoints[1].x, EPSILON);
assertEquals(0.2, postPoints[1].y, EPSILON);
// last point
assertEquals(0.5, postPoints[2].x, EPSILON);
assertEquals(-0.25, postPoints[2].y, EPSILON);
assertEquals(0.3, fins.getFinFront().x, EPSILON);
assertEquals(0.85, fins.getFinFront().y, EPSILON);
assertEquals(0.05, fins.getAxialOffset(), EPSILON);
assertEquals(0.5, fins.getLength(), EPSILON);
}{ // case 4:
fins.setAxialOffset( AxialMethod.MIDDLE, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( lastIndex, xf - 0.1, 0.1f);
// ^^^^ function under test ^^^^
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid point
assertEquals(0.4, postPoints[1].x, EPSILON);
assertEquals(0.2, postPoints[1].y, EPSILON);
// last point
assertEquals(0.3, postPoints[2].x, EPSILON);
assertEquals(-0.15, postPoints[2].y, EPSILON);
assertEquals(0.3, fins.getFinFront().x, EPSILON);
assertEquals(0.85, fins.getFinFront().y, EPSILON);
assertEquals(-0.05, fins.getAxialOffset(), EPSILON);
assertEquals(0.3, fins.getLength(), EPSILON);
}{ // case 5:
fins.setAxialOffset( AxialMethod.BOTTOM, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( lastIndex, xf + 0.1, 0.1f);
// ^^^^ function under test ^^^^
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 4);
// mid-point
assertEquals(0.4, postPoints[1].x, EPSILON);
assertEquals(0.2, postPoints[1].y, EPSILON);
// pseudo last point
assertEquals(0.5, postPoints[2].x, EPSILON);
assertEquals(0.1, postPoints[2].y, EPSILON);
// last point
assertEquals(0.4, postPoints[3].x, EPSILON);
assertEquals(-0.2, postPoints[3].y, EPSILON);
assertEquals(0.6, fins.getFinFront().x, EPSILON);
assertEquals(0.7, fins.getFinFront().y, EPSILON);
assertEquals(0.0, fins.getAxialOffset(), EPSILON);
assertEquals(0.4f, fins.getLength(), EPSILON);
}{ // case 6:
fins.setAxialOffset( AxialMethod.BOTTOM, 0.0);
fins.setPoints(initialPoints);
// vvvv function under test vvvv
fins.setPoint( lastIndex, xf - 0.1, 0.1f);
// ^^^^ function under test ^^^^
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// mid-point
assertEquals(0.4, postPoints[1].x, EPSILON);
assertEquals(0.2, postPoints[1].y, EPSILON);
// last point
assertEquals(0.3, postPoints[2].x, EPSILON);
assertEquals(-0.15, postPoints[2].y, EPSILON);
assertEquals(0.6, fins.getFinFront().x, EPSILON);
assertEquals(0.7, fins.getFinFront().y, EPSILON);
assertEquals(-0.1, fins.getAxialOffset(), EPSILON);
assertEquals(0.3, fins.getLength(), EPSILON);
}
}
@Test
public void testSetInteriorPoint() {
final Rocket rkt = createTemplateRocket();
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
{ // preconditions // initial points
final Coordinate[] initialPoints = fins.getFinPoints();
assertEquals(initialPoints[0], Coordinate.ZERO);
assertEquals(initialPoints[1], new Coordinate(0.4, 0.2));
assertEquals(initialPoints[2], new Coordinate(0.4, -0.2));
assertEquals(0.4, fins.getLength(), EPSILON);
}{ // preconditions // mount
Transition finMount = (Transition) rkt.getChild(0).getChild(2);
assertEquals(0.4, fins.getFinFront().x, EPSILON);
assertEquals(0.8, fins.getFinFront().y, EPSILON);
assertEquals(0.8, finMount.getRadius(fins.getAxialFront()), EPSILON);
assertEquals(AxialMethod.TOP, fins.getAxialMethod());
assertEquals(0.4, fins.getAxialOffset(), EPSILON);
}{ // test target
final Coordinate p1 = fins.getFinPoints()[1];
// vvvv function under test vvvv
fins.setPoint( 1, p1.x + 0.1, p1.y + 0.1f);
// ^^^^ function under test ^^^^
}{ // postconditions
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(postPoints.length, 3);
// middle point:
assertEquals(0.5, postPoints[1].x, EPSILON);
assertEquals(0.3, postPoints[1].y, EPSILON);
// last point:
assertEquals(0.4, postPoints[2].x, EPSILON);
assertEquals(-0.2, postPoints[2].y, EPSILON);
assertEquals(0.4, fins.getLength(), EPSILON);
assertEquals(0.4, fins.getFinFront().x, EPSILON);
assertEquals(0.8, fins.getFinFront().y, EPSILON);
}
}
@Test
public void testSetAllPoints() {
final Rocket rkt = createTemplateRocket();
final AxialStage stage = (AxialStage) rkt.getChild(0);
{ // setup // mount
BodyTube body = new BodyTube(0.0, 1.0, 0.002);
body.setName("Phantom Body Tube");
body.setOuterRadiusAutomatic(true);
stage.addChild(body, 2);
assertEquals(1.0, body.getOuterRadius(), EPSILON);
assertEquals(0.0, body.getLength(), EPSILON);
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(4);
Coordinate[] points = new Coordinate[]{
new Coordinate(0.0, 0.0),
new Coordinate(-0.0508, 0.007721),
new Coordinate(0.0, 0.01544),
new Coordinate(0.0254, 0.007721),
new Coordinate(1.1e-4, 0.0) // final point is within the testing thresholds :/
};
fins.setPoints(points);
body.addChild(fins);
}{ // postconditions
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
final Coordinate[] postPoints = fins.getFinPoints();
assertEquals(6, postPoints.length);
// p1
assertEquals(-0.0508, postPoints[1].x, EPSILON);
assertEquals(0.007721, postPoints[1].y, EPSILON);
// p2
assertEquals(0.0, postPoints[2].x, EPSILON);
assertEquals(0.01544, postPoints[2].y, EPSILON);
// p3
assertEquals(0.0254, postPoints[3].x, EPSILON);
assertEquals(0.007721, postPoints[3].y, EPSILON);
// p4
assertEquals(0.00011, postPoints[4].x, EPSILON);
assertEquals(0.0, postPoints[4].y, EPSILON);
// p/last: generated by loading code:
assertEquals(0.0, postPoints[5].x, EPSILON);
assertEquals(0.0, postPoints[5].y, EPSILON);
assertEquals(0.0, fins.getLength(), EPSILON);
assertEquals(0.0, fins.getFinFront().x, EPSILON);
assertEquals(1.0, fins.getFinFront().y, EPSILON);
}
} }
@Test @Test
public void testSetFirstPoint_testNonIntersection() { public void testSetFirstPoint_testNonIntersection() {
final Rocket rkt = createTemplateRocket(); final Rocket rkt = createTemplateRocket();
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0); final FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
final Transition mount = (Transition) rkt.getChild(0).getChild(2);
assertEquals( 1, fins.getFinCount()); assertEquals( 1, fins.getFinCount());
final int lastIndex = fins.getPointCount()-1; assertEquals( 3, fins.getPointCount());
assertEquals( 2, lastIndex); assertEquals( AxialMethod.TOP, fins.getAxialMethod());
final double initialOffset = fins.getAxialOffset(); assertEquals( 0.4, fins.getAxialOffset(), EPSILON); // pre-condition
assertEquals( 0.4, initialOffset, EPSILON); // pre-condition assertEquals( 1.0, mount.getLength(), EPSILON);
final double attemptedDelta = 0.6;
fins.setPoint( 0, attemptedDelta, 0);
// fin offset: 0.4 -> 0.59 (just short of prev fin end) // fin offset: 0.4 -> 0.59 (just short of prev fin end)
// fin end: 0.4 ~> min root chord // fin end: 0.4 ~> min root chord
// vv Test Target vv
fins.setPoint( 0, 0.6, 0);
// ^^ Test Target ^^
assertEquals(fins.getFinPoints()[ 0], Coordinate.ZERO); assertEquals(fins.getFinPoints()[ 0], Coordinate.ZERO);
// setting the first point actually offsets the whole fin by that amount: // setting the first point actually offsets the whole fin by that amount:
final double expFinOffset = 0.79; final double expFinOffset = 1.0;
assertEquals("Resultant fin offset does not match!", expFinOffset, fins.getAxialOffset(), EPSILON); assertEquals("Resultant fin offset does not match!", expFinOffset, fins.getAxialOffset(), EPSILON);
// SHOULD NOT CHANGE (in this case): assertEquals( 3, fins.getPointCount());
Coordinate actualLastPoint = fins.getFinPoints()[ lastIndex]; Coordinate actualLastPoint = fins.getFinPoints()[2];
assertEquals("last point did not adjust correctly: ", FreeformFinSet.MIN_ROOT_CHORD, actualLastPoint.x, EPSILON); assertEquals("last point did not adjust correctly: ", 0f, actualLastPoint.x, EPSILON);
assertEquals("last point did not adjust correctly: ", -0.005, actualLastPoint.y, EPSILON); // magic number assertEquals("last point did not adjust correctly: ", 0f, actualLastPoint.y, EPSILON);
assertEquals("New fin length is wrong: ", FreeformFinSet.MIN_ROOT_CHORD, fins.getLength(), EPSILON); assertEquals("New fin length is wrong: ", 0.0, fins.getLength(), EPSILON);
} }
@Test
public void testSetLastPoint_TubeBody() throws IllegalFinPointException {
Rocket rkt = createTemplateRocket();
// combine the simple case with the complicated to ensure that the simple case is
// flagged, tested, and debugged before running the more complicated case...
{ // setting points on a Tube Body is the simpler case. Test this first:
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(1).getChild(0);
// verify preconditions
assertEquals(AxialMethod.BOTTOM, fins.getAxialMethod());
assertEquals(0.0, fins.getAxialOffset(), EPSILON);
// last point is restricted to the body
final int lastIndex = fins.getPointCount()-1;
final Coordinate expectedFinalPoint = new Coordinate( 1.0, 0.0, 0.0);
// vvvv function under test vvvv
fins.setPoint( lastIndex, 10.0, Double.NaN);
// ^^^^ function under test ^^^^
Coordinate actualFinalPoint = fins.getFinPoints()[lastIndex];
assertEquals( expectedFinalPoint.x, actualFinalPoint.x, EPSILON);
assertEquals( expectedFinalPoint.y, actualFinalPoint.y, EPSILON);
}
{ // a transitions will trigger more complicated positioning math:
FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
final int lastIndex = fins.getPointCount()-1;
Coordinate expectedLastPoint;
Coordinate actualLastPoint;
{ // this is where the point starts off at:
actualLastPoint = fins.getFinPoints()[lastIndex];
assertEquals( 0.4, actualLastPoint.x, EPSILON);
assertEquals( -0.2, actualLastPoint.y, EPSILON);
}
{ // (1): move point within bounds
// move last point, and verify that its y-value is still clamped to the body ( at the new location)
expectedLastPoint = new Coordinate( 0.6, -0.3, 0.0);
fins.setPoint(lastIndex, 0.6, 0.0); // w/ incorrect y-val. The function should correct the y-value as above.
actualLastPoint = fins.getFinPoints()[lastIndex];
assertEquals( expectedLastPoint.x, actualLastPoint.x, EPSILON);
assertEquals( expectedLastPoint.y, actualLastPoint.y, EPSILON);
}
}
}
@Test @Test
public void testSetPoint_otherPoint() throws IllegalFinPointException { public void testSetPoint_otherPoint() throws IllegalFinPointException {
// combine the simple case with the complicated to ensure that the simple case is flagged, tested, and debugged before running the more complicated case... // combine the simple case with the complicated to ensure that the simple case is flagged, tested, and debugged before running the more complicated case...
@ -785,7 +1017,7 @@ public class FreeformFinSetTest extends BaseTestCase {
} }
@Test @Test
public void testForNonIntersection() { public void testForIntersection_false() {
final Rocket rkt = new Rocket(); final Rocket rkt = new Rocket();
final AxialStage stg = new AxialStage(); final AxialStage stg = new AxialStage();
rkt.addChild(stg); rkt.addChild(stg);
@ -811,10 +1043,10 @@ public class FreeformFinSetTest extends BaseTestCase {
body.addChild(fins); body.addChild(fins);
assertFalse( " Fin detects false positive intersection in fin points: ", fins.intersects()); assertFalse( " Fin detects false positive intersection in fin points: ", fins.intersects());
} }
@Test @Test
public void testForIntersection() { public void testForIntersection_true() {
final Rocket rkt = new Rocket(); final Rocket rkt = new Rocket();
final AxialStage stg = new AxialStage(); final AxialStage stg = new AxialStage();
rkt.addChild(stg); rkt.addChild(stg);
@ -849,6 +1081,53 @@ public class FreeformFinSetTest extends BaseTestCase {
assertThat( "Fin Set failed to detect an intersection! ", p1.y, not(equalTo(initPoints[1].y))); assertThat( "Fin Set failed to detect an intersection! ", p1.y, not(equalTo(initPoints[1].y)));
} }
@Test
public void testForIntersectionAtFirstLast() {
final Rocket rkt = new Rocket();
final AxialStage stg = new AxialStage();
rkt.addChild(stg);
BodyTube body = new BodyTube(2.0, 0.01);
stg.addChild(body);
//
// An obviously intersecting fin:
// [2] +---+ [1]
// | /
// | /
// [0]|/ [3]
// +---+-----+---+
// = +x =>
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] initPoints = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0, 1),
new Coordinate(1, 1),
new Coordinate(0, 0)
};
// this line throws an exception?
fins.setPoints(initPoints);
body.addChild(fins);
final Coordinate[] finPoints = fins.getFinPoints();
// p0
assertEquals("incorrect body points! ", 0., finPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", 0., finPoints[0].y, EPSILON);
// p1
assertEquals("incorrect body points! ", 0., finPoints[1].x, EPSILON);
assertEquals("incorrect body points! ", 1., finPoints[1].y, EPSILON);
// p2
assertEquals("incorrect body points! ", 1., finPoints[2].x, EPSILON);
assertEquals("incorrect body points! ", 1., finPoints[2].y, EPSILON);
// pf
assertEquals("incorrect body points! ", 0., finPoints[3].x, EPSILON);
assertEquals("incorrect body points! ", 0., finPoints[3].y, EPSILON);
}
@Test @Test
public void testWildmanVindicatorShape() throws Exception { public void testWildmanVindicatorShape() throws Exception {
@ -956,17 +1235,63 @@ public class FreeformFinSetTest extends BaseTestCase {
final Coordinate finFront = fins.getFinFront(); final Coordinate finFront = fins.getFinFront();
final Coordinate[] finPoints = fins.getFinPoints(); final Coordinate[] finPoints = fins.getFinPoints();
// translate from fin-frame to body-frame
final Coordinate[] finPointsFromBody = FinSet.translatePoints( fins.getFinPoints(), finFront.x, finFront.y );
final Coordinate expectedStartPoint = finPointsFromBody[0];
final Coordinate expectedEndPoint = finPointsFromBody[ finPoints.length-1];
{ // body points (relative to body)
final Coordinate[] bodyPoints = fins.getBodyPoints(); { // fin points (relative to fin) // preconditions
assertEquals(4, finPoints.length);
assertEquals("incorrect body points! ", 0f, finPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", 0f, finPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", 0.8, finPoints[3].x, EPSILON);
// ?? SMOKING GUN:
// ?? is this y-value of the fin not getting snapped to the body?
assertEquals(body.getRadius(0.8+finFront.x) - finFront.y, finPoints[3].y, EPSILON);
assertEquals("incorrect body points! ", 0.78466912, finPoints[3].y, EPSILON);
}{ // body points (relative to fin)
final Coordinate[] rootPoints = fins.getRootPoints();
assertEquals(101, rootPoints.length);
final int lastIndex = 100;
// trivial, and uninteresting: // trivial, and uninteresting:
assertEquals("incorrect body points! ", expectedStartPoint.x, bodyPoints[0].x, EPSILON); assertEquals("incorrect body points! ", finPoints[0].x, rootPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", expectedStartPoint.y, bodyPoints[0].y, EPSILON); assertEquals("incorrect body points! ", finPoints[0].y, rootPoints[0].y, EPSILON);
// n.b.: This should match EXACTLY the end point of the fin. (in fin coordinates)
assertEquals("incorrect body points! ", finPoints[finPoints.length -1].x, rootPoints[lastIndex].x, EPSILON);
assertEquals("incorrect body points! ", finPoints[finPoints.length -1].y, rootPoints[lastIndex].y, EPSILON);
{// the tests within this scope is are rather fragile, and may break for reasons other than bugs :(
// the number of points is somewhat arbitrary, but if this test fails, the rest *definitely* will.
assertEquals("Method is generating how many points? ", 101, rootPoints.length );
final int[] testIndices = { 2, 5, 61, 88};
final double[] expectedX = { 0.016, 0.04, 0.488, 0.704};
for( int testCase = 0; testCase < testIndices.length; testCase++){
final int testIndex = testIndices[testCase];
assertEquals(String.format("Root points @ %d :: x coordinate mismatch!", testIndex),
expectedX[testCase], rootPoints[testIndex].x, EPSILON);
assertEquals(String.format("Root points @ %d :: y coordinate mismatch!", testIndex),
body.getRadius(rootPoints[testIndex].x + finFront.x) - finFront.y, rootPoints[testIndex].y, EPSILON);
}
}
}{ // body points (relative to body)
// translate from fin-frame to body-frame
final Coordinate[] finPointsFromBody = FinSet.translatePoints( fins.getFinPoints(), finFront.x, finFront.y );
final Coordinate[] bodyPoints = fins.getBodyPoints();
assertEquals(101, bodyPoints.length);
final Coordinate expectedEndPoint = finPointsFromBody[ finPoints.length-1];
// trivial, and uninteresting:
assertEquals("incorrect body points! ", finPointsFromBody[0].x, bodyPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[0].y, bodyPoints[0].y, EPSILON);
// n.b.: This should match EXACTLY the end point of the fin. (in fin coordinates) // n.b.: This should match EXACTLY the end point of the fin. (in fin coordinates)
assertEquals("incorrect body points! ", expectedEndPoint.x, bodyPoints[bodyPoints.length-1].x, EPSILON); assertEquals("incorrect body points! ", expectedEndPoint.x, bodyPoints[bodyPoints.length-1].x, EPSILON);
@ -988,33 +1313,6 @@ public class FreeformFinSetTest extends BaseTestCase {
} }
} }
} }
{ // body points (relative to fin)
final Coordinate[] rootPoints = fins.getRootPoints();
// trivial, and uninteresting:
assertEquals("incorrect body points! ", finPoints[0].x, rootPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPoints[0].y, rootPoints[0].y, EPSILON);
// n.b.: This should match EXACTLY the end point of the fin. (in fin coordinates)
assertEquals("incorrect body points! ", finPoints[finPoints.length-1].x, rootPoints[rootPoints.length-1].x, EPSILON);
assertEquals("incorrect body points! ", finPoints[finPoints.length-1].y, rootPoints[rootPoints.length-1].y, EPSILON);
{// the tests within this scope is are rather fragile, and may break for reasons other than bugs :(
// the number of points is somewhat arbitrary, but if this test fails, the rest *definitely* will.
assertEquals("Method is generating how many points? ", 101, rootPoints.length );
final int[] testIndices = { 2, 5, 61, 88};
final double[] expectedX = { 0.016, 0.04, 0.488, 0.704};
for( int testCase = 0; testCase < testIndices.length; testCase++){
final int testIndex = testIndices[testCase];
assertEquals(String.format("Root points @ %d :: x coordinate mismatch!", testIndex),
expectedX[testCase], rootPoints[testIndex].x, EPSILON);
assertEquals(String.format("Root points @ %d :: y coordinate mismatch!", testIndex),
body.getRadius(rootPoints[testIndex].x + finFront.x) - finFront.y, rootPoints[testIndex].y, EPSILON);
}
}
}
} }
@Test @Test