Merge pull request #2243 from SiboVG/issue-2231

[#2231 & #2242] Account for fin cant in root points
This commit is contained in:
Joe Pfeiffer 2023-07-05 13:54:34 -06:00 committed by GitHub
commit eeae3d2c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 300 additions and 27 deletions

View File

@ -36,11 +36,18 @@ public class EllipticalFinSet extends FinSet {
@Override @Override
public Coordinate[] getFinPoints() { public Coordinate[] getFinPoints() {
double len = MathUtil.max(length, 0.0001); double len = MathUtil.max(length, 0.0001);
Coordinate[] points = new Coordinate[POINTS]; Coordinate[] finPoints = new Coordinate[POINTS];
for (int i = 0; i < POINTS; i++) { for (int i = 0; i < POINTS; i++) {
points[i] = new Coordinate(POINT_X[i] * len, POINT_Y[i] * height); finPoints[i] = new Coordinate(POINT_X[i] * len, POINT_Y[i] * height);
} }
return points;
// Set the start and end fin points the same as the root points (necessary for canted fins)
final Coordinate[] rootPoints = getRootPoints();
if (rootPoints.length > 1) {
finPoints[0] = finPoints[0].setX(rootPoints[0].x).setY(rootPoints[0].y);
finPoints[finPoints.length - 1] = finPoints[finPoints.length - 1].setX(rootPoints[rootPoints.length - 1].x).setY(rootPoints[rootPoints.length - 1].y);
}
return finPoints;
} }
@Override @Override

View File

@ -1025,7 +1025,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
* @return points representing the fin-root points, relative to ( x: fin-front, y: centerline ) i.e. relto: fin Component reference point * @return points representing the fin-root points, relative to ( x: fin-front, y: centerline ) i.e. relto: fin Component reference point
*/ */
public Coordinate[] getRootPoints(final int maximumBodyDivisionCount) { public Coordinate[] getRootPoints(final int maximumBodyDivisionCount) {
if( null == parent){ if (parent == null) {
return new Coordinate[]{Coordinate.ZERO}; return new Coordinate[]{Coordinate.ZERO};
} }
@ -1075,11 +1075,11 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
// for a simple body, one increment is perfectly accurate. // for a simple body, one increment is perfectly accurate.
int divisionCount = 1; int divisionCount = 1;
final SymmetricComponent body = (SymmetricComponent) getParent(); final SymmetricComponent parent = (SymmetricComponent) getParent();
final double intervalLength = xEnd - xStart; final double intervalLength = xEnd - xStart;
// for anything more complicated, increase the count: // for anything more complicated, increase the count:
if ((body instanceof Transition) && (((Transition)body).getShapeType() != Shape.CONICAL)) { if ((!MathUtil.equals(getCantAngle(), 0)) || (parent instanceof Transition) && (((Transition)parent).getShapeType() != Shape.CONICAL)) {
// the maximum precision to enforce when calculating the areas of fins (especially on curved parent bodies) // the maximum precision to enforce when calculating the areas of fins (especially on curved parent bodies)
final double xWidth = 0.0025; // width (in meters) of each individual iteration final double xWidth = 0.0025; // width (in meters) of each individual iteration
divisionCount = (int) Math.ceil(intervalLength / xWidth); divisionCount = (int) Math.ceil(intervalLength / xWidth);
@ -1093,13 +1093,18 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
double xIncrement = intervalLength / divisionCount; double xIncrement = intervalLength / divisionCount;
// Create the points: step through the radius of the parent // Create the points: step through the radius of the parent
double xCur = xStart; double xCurr = xStart;
List<Coordinate> points = new ArrayList<>(); List<Coordinate> points = new ArrayList<>();
for (int index = 0; index < divisionCount+1; index++) { for (int index = 0; index < divisionCount+1; index++) {
double yCur = body.getRadius(xCur); double yCurr = parent.getRadius(xCurr);
points.add(new Coordinate(xCur, yCur));
xCur += xIncrement; // Account for the fin cant angle
final double dy = getFinCantYOffset(xStart, xEnd, xCurr);
yCurr += dy;
points.add(new Coordinate(xCurr, yCurr));
xCurr += xIncrement;
} }
/* /*
@ -1107,11 +1112,11 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
then we need to add an extra root point at the front of the parent. Same goes for the last point, but vice versa. then we need to add an extra root point at the front of the parent. Same goes for the last point, but vice versa.
This ensures that fins are drawn correctly on transitions and nose cones (see GitHub issue #1021 for more info). This ensures that fins are drawn correctly on transitions and nose cones (see GitHub issue #1021 for more info).
*/ */
// Front fin point is outside the parent's bounds and last point is still within the parent's bounds // Front fin point is outside the parent's bounds and last point is beyond the parent's fore end
if (xStart < 0 && xEnd > 0) { if (xStart < 0 && xEnd > 0) {
points.add(1, new Coordinate(0, points.get(0).y)); points.add(1, new Coordinate(0, points.get(0).y));
} }
// End fin point is outside the parent's bounds and first point is still within the parent's bounds // End fin point is beyond the parent's aft and first point is still before the parent's aft end
if (xEnd > parent.length && xStart < parent.length) { if (xEnd > parent.length && xStart < parent.length) {
final double x = parent.length; final double x = parent.length;
final double y = points.get(points.size() - 1).y; final double y = points.get(points.size() - 1).y;
@ -1122,8 +1127,8 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
// correct last point, if beyond a rounding error from body's end. // correct last point, if beyond a rounding error from body's end.
final int lastIndex = rootPoints.length - 1; final int lastIndex = rootPoints.length - 1;
if (Math.abs(rootPoints[lastIndex].x - body.getLength()) < MathUtil.EPSILON) { if (Math.abs(rootPoints[lastIndex].x - parent.getLength()) < MathUtil.EPSILON) {
rootPoints[lastIndex] = rootPoints[lastIndex].setX(body.getLength()).setY(body.getAftRadius()); rootPoints[lastIndex] = rootPoints[lastIndex].setX(parent.getLength());
} }
// translate the points if needed // translate the points if needed
@ -1138,6 +1143,51 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
return getMountPoints(xStart, xEnd, xOffset, yOffset, MAX_ROOT_DIVISIONS); return getMountPoints(xStart, xEnd, xOffset, yOffset, MAX_ROOT_DIVISIONS);
} }
/**
* Returns a y offset that should be applied to the fin root point at the given x position to account for the fin cant.
* @param xStart The x position of the fin root point at the front of the fin.
* @param xEnd The x position of the fin root point at the back of the fin.
* @param xCurr The x position of the fin root point to calculate the y offset for.
* @return The y offset to apply to the fin root point at the given x position.
*/
private double getFinCantYOffset(final double xStart, final double xEnd, double xCurr) {
final SymmetricComponent parent = (SymmetricComponent) getParent();
final double cantAngle = getCantAngle();
if (MathUtil.equals(cantAngle, 0) || (xStart > parent.length && xEnd > parent.length) || (xStart < 0 && xEnd < 0)) {
return 0;
}
// Limit the x position to the parent's bounds
double x = Math.max(xCurr, 0);
x = Math.min(x, parent.getLength());
// Determine the center of rotation
final double xCenter = (xStart + xEnd) / 2;
// Calculate the new x position after rotation
final double dx = x - xCenter; // Distance to the center of rotation
final double xCurr_rot = xCenter + dx * Math.abs(Math.cos(cantAngle)); // X coordinate after rotation
// Extend the root of the fin to touch the surface of the parent
final double radius_rot = parent.getRadius(xCurr_rot);
final double dz = Math.abs(Math.sin(cantAngle)) * dx;
final double dy;
if (dz >= radius_rot) {
dy = 0;
} else {
/*
Simplification of r^2 = (r-dy)^2 + dz^2, given that dz < r. (to derive this, draw
the cross-section of the body, which is just a circle, with a tangent line starting on the top of the circle
and reaching a distance dz. Use some Pythagorean theorem and bam, you got this equation.)
*/
dy = radius_rot - Math.sqrt(Math.pow(radius_rot, 2) - Math.pow(dz, 2));
}
return -dy;
}
/** /**
* Return a list of coordinates defining the geometry of a single fin, including the parent's body points . * Return a list of coordinates defining the geometry of a single fin, including the parent's body points .
*/ */

View File

@ -392,7 +392,16 @@ public class FreeformFinSet extends FinSet {
@Override @Override
public Coordinate[] getFinPoints() { public Coordinate[] getFinPoints() {
return points.toArray(new Coordinate[0]); Coordinate[] finPoints = points.toArray(new Coordinate[0]);
// Set the start and end fin points the same as the root points (necessary for canted fins)
final Coordinate[] rootPoints = getRootPoints();
if (rootPoints.length > 1) {
finPoints[0] = finPoints[0].setX(rootPoints[0].x).setY(rootPoints[0].y);
finPoints[finPoints.length - 1] = finPoints[finPoints.length - 1].setX(rootPoints[rootPoints.length - 1].x).setY(rootPoints[rootPoints.length - 1].y);
}
return finPoints;
} }
@Override @Override

View File

@ -193,16 +193,25 @@ public class TrapezoidFinSet extends FinSet {
*/ */
@Override @Override
public Coordinate[] getFinPoints() { public Coordinate[] getFinPoints() {
List<Coordinate> list = new ArrayList<Coordinate>(4); List<Coordinate> points = new ArrayList<>(4);
list.add(Coordinate.NUL); points.add(Coordinate.NUL);
list.add(new Coordinate(sweep, height)); points.add(new Coordinate(sweep, height));
if (tipChord > 0.0001) { if (tipChord > 0.0001) {
list.add(new Coordinate(sweep + tipChord, height)); points.add(new Coordinate(sweep + tipChord, height));
} }
list.add(new Coordinate(MathUtil.max(length, 0.0001), 0)); points.add(new Coordinate(MathUtil.max(length, 0.0001), 0));
return list.toArray(new Coordinate[list.size()]); Coordinate[] finPoints = points.toArray(new Coordinate[0]);
// Set the start and end fin points the same as the root points (necessary for canted fins)
final Coordinate[] rootPoints = getRootPoints();
if (rootPoints.length > 1) {
finPoints[0] = finPoints[0].setX(rootPoints[0].x).setY(rootPoints[0].y);
finPoints[finPoints.length - 1] = finPoints[finPoints.length - 1].setX(rootPoints[rootPoints.length - 1].x).setY(rootPoints[rootPoints.length - 1].y);
}
return finPoints;
} }
/** /**

View File

@ -248,6 +248,141 @@ public class FreeformFinSetTest extends BaseTestCase {
} }
@Test
public void testGenerateTrapezoidalPointsWithCant() {
final Rocket rkt = createTemplateRocket();
final Transition tailCone = (Transition) rkt.getChild(0).getChild(2);
final FreeformFinSet fins = createFinOnConicalTransition(tailCone);
fins.setCantAngle(Math.toRadians(15));
Coordinate[] actPoints = fins.getFinPoints();
Coordinate[] rootPoints = fins.getRootPoints();
final Coordinate[] expPoints = new Coordinate[] {
new Coordinate(0, -0.001683625, 0),
new Coordinate(0.4, 0.2, 0),
new Coordinate(0.4, -0.202224401, 0)
};
final Coordinate[] expRootPoints = new Coordinate[]{
new Coordinate(0, -0.001683625, 0),
new Coordinate(0.004, -0.003620824, 0),
new Coordinate(0.008, -0.005559077, 0),
new Coordinate(0.012, -0.00749839, 0),
new Coordinate(0.016, -0.009438771, 0),
new Coordinate(0.02, -0.011380227, 0),
new Coordinate(0.024, -0.013322767, 0),
new Coordinate(0.028, -0.015266398, 0),
new Coordinate(0.032, -0.017211128, 0),
new Coordinate(0.036, -0.019156966, 0),
new Coordinate(0.04, -0.021103918, 0),
new Coordinate(0.044, -0.023051993, 0),
new Coordinate(0.048, -0.0250012, 0),
new Coordinate(0.052, -0.026951546, 0),
new Coordinate(0.056, -0.028903041, 0),
new Coordinate(0.06, -0.030855692, 0),
new Coordinate(0.064, -0.032809508, 0),
new Coordinate(0.068, -0.034764497, 0),
new Coordinate(0.072, -0.036720669, 0),
new Coordinate(0.076, -0.038678032, 0),
new Coordinate(0.08, -0.040636596, 0),
new Coordinate(0.084, -0.042596368, 0),
new Coordinate(0.088, -0.044557359, 0),
new Coordinate(0.092, -0.046519577, 0),
new Coordinate(0.096, -0.048483032, 0),
new Coordinate(0.1, -0.050447733, 0),
new Coordinate(0.104, -0.052413689, 0),
new Coordinate(0.108, -0.054380911, 0),
new Coordinate(0.112, -0.056349408, 0),
new Coordinate(0.116, -0.05831919, 0),
new Coordinate(0.12, -0.060290266, 0),
new Coordinate(0.124, -0.062262648, 0),
new Coordinate(0.128, -0.064236344, 0),
new Coordinate(0.132, -0.066211365, 0),
new Coordinate(0.136, -0.068187722, 0),
new Coordinate(0.14, -0.070165425, 0),
new Coordinate(0.144, -0.072144484, 0),
new Coordinate(0.148, -0.074124911, 0),
new Coordinate(0.152, -0.076106716, 0),
new Coordinate(0.156, -0.07808991, 0),
new Coordinate(0.16, -0.080074505, 0),
new Coordinate(0.164, -0.082060511, 0),
new Coordinate(0.168, -0.08404794, 0),
new Coordinate(0.172, -0.086036803, 0),
new Coordinate(0.176, -0.088027112, 0),
new Coordinate(0.18, -0.090018879, 0),
new Coordinate(0.184, -0.092012115, 0),
new Coordinate(0.188, -0.094006834, 0),
new Coordinate(0.192, -0.096003045, 0),
new Coordinate(0.196, -0.098000763, 0),
new Coordinate(0.2, -0.1, 0),
new Coordinate(0.204, -0.102000768, 0),
new Coordinate(0.208, -0.104003079, 0),
new Coordinate(0.212, -0.106006948, 0),
new Coordinate(0.216, -0.108012386, 0),
new Coordinate(0.22, -0.110019407, 0),
new Coordinate(0.224, -0.112028025, 0),
new Coordinate(0.228, -0.114038253, 0),
new Coordinate(0.232, -0.116050104, 0),
new Coordinate(0.236, -0.118063594, 0),
new Coordinate(0.24, -0.120078734, 0),
new Coordinate(0.244, -0.122095541, 0),
new Coordinate(0.248, -0.124114028, 0),
new Coordinate(0.252, -0.126134209, 0),
new Coordinate(0.256, -0.1281561, 0),
new Coordinate(0.26, -0.130179716, 0),
new Coordinate(0.264, -0.132205071, 0),
new Coordinate(0.268, -0.134232181, 0),
new Coordinate(0.272, -0.136261062, 0),
new Coordinate(0.276, -0.138291728, 0),
new Coordinate(0.28, -0.140324197, 0),
new Coordinate(0.284, -0.142358484, 0),
new Coordinate(0.288, -0.144394605, 0),
new Coordinate(0.292, -0.146432578, 0),
new Coordinate(0.296, -0.148472418, 0),
new Coordinate(0.3, -0.150514143, 0),
new Coordinate(0.304, -0.152557769, 0),
new Coordinate(0.308, -0.154603316, 0),
new Coordinate(0.312, -0.156650799, 0),
new Coordinate(0.316, -0.158700236, 0),
new Coordinate(0.32, -0.160751647, 0),
new Coordinate(0.324, -0.16280505, 0),
new Coordinate(0.328, -0.164860462, 0),
new Coordinate(0.332, -0.166917903, 0),
new Coordinate(0.336, -0.168977392, 0),
new Coordinate(0.34, -0.171038948, 0),
new Coordinate(0.344, -0.173102591, 0),
new Coordinate(0.348, -0.175168341, 0),
new Coordinate(0.352, -0.177236218, 0),
new Coordinate(0.356, -0.179306243, 0),
new Coordinate(0.36, -0.181378435, 0),
new Coordinate(0.364, -0.183452818, 0),
new Coordinate(0.368, -0.18552941, 0),
new Coordinate(0.372, -0.187608235, 0),
new Coordinate(0.376, -0.189689315, 0),
new Coordinate(0.38, -0.191772671, 0),
new Coordinate(0.384, -0.193858326, 0),
new Coordinate(0.388, -0.195946303, 0),
new Coordinate(0.392, -0.198036625, 0),
new Coordinate(0.396, -0.200129317, 0),
new Coordinate(0.4, -0.202224401, 0)
};
assertEquals("Canted fin number of points doesn't match! ", expPoints.length, actPoints.length);
assertEquals("Canted root number of points doesn't match! ", expRootPoints.length, rootPoints.length);
for (int i = 0; i < expPoints.length; i++) {
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].x, actPoints[i].x, EPSILON);
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].y, actPoints[i].y, EPSILON);
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].z, actPoints[i].z, EPSILON);
}
for (int i = 0; i < expRootPoints.length; i++) {
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].x, rootPoints[i].x, EPSILON);
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].y, rootPoints[i].y, EPSILON);
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].z, rootPoints[i].z, EPSILON);
}
}
@Test @Test
public void testFreeformCMComputation_trapezoidOnTube() { public void testFreeformCMComputation_trapezoidOnTube() {
final Rocket rkt = createTemplateRocket(); final Rocket rkt = createTemplateRocket();

View File

@ -1,6 +1,7 @@
package net.sf.openrocket.rocketcomponent; package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import org.junit.Test; import org.junit.Test;
@ -111,6 +112,71 @@ public class TrapezoidFinSetTest extends BaseTestCase {
} }
} }
@Test
public void testGenerateTrapezoidalPointsWithCant() {
final Rocket rkt = createSimpleTrapezoidalFin();
FinSet fins = (FinSet) rkt.getChild(0).getChild(0).getChild(0);
fins.setCantAngle(Math.toRadians(15));
// Fin length = 0.05
// Tab Length = 0.01
// +--+
// / \
// / \
// +---+--------+---+
//
Coordinate[] actPoints = fins.getFinPoints();
Coordinate[] rootPoints = fins.getRootPoints();
final Coordinate[] expPoints = new Coordinate[] {
new Coordinate(0.00, -0.00030189855, 0.00),
new Coordinate(0.02, 0.05, 0.00),
new Coordinate(0.04, 0.05, 0.00),
new Coordinate(0.06, -0.00030189855, 0.00)
};
final Coordinate[] expRootPoints = new Coordinate[] {
new Coordinate(0.0000, -0.000301899, 0.0000),
new Coordinate(0.0025, -0.000253617, 0.0000),
new Coordinate(0.0050, -0.000209555, 0.0000),
new Coordinate(0.0075, -0.000169706, 0.0000),
new Coordinate(0.0100, -0.000134064, 0.0000),
new Coordinate(0.0125, -0.000102627, 0.0000),
new Coordinate(0.0150, -0.000075389, 0.0000),
new Coordinate(0.0175, -0.000052348, 0.0000),
new Coordinate(0.0200, -0.000033499, 0.0000),
new Coordinate(0.0225, -0.000018842, 0.0000),
new Coordinate(0.0250, -0.000008374, 0.0000),
new Coordinate(0.0275, -0.000002093, 0.0000),
new Coordinate(0.0300, 0.0000, 0.0000),
new Coordinate(0.0325, -0.000002093, 0.0000),
new Coordinate(0.0350, -0.000008374, 0.0000),
new Coordinate(0.0375, -0.000018842, 0.0000),
new Coordinate(0.0400, -0.000033499, 0.0000),
new Coordinate(0.0425, -0.000052348, 0.0000),
new Coordinate(0.0450, -0.000075389, 0.0000),
new Coordinate(0.0475, -0.000102627, 0.0000),
new Coordinate(0.0500, -0.000134064, 0.0000),
new Coordinate(0.0525, -0.000169706, 0.0000),
new Coordinate(0.0550, -0.000209555, 0.0000),
new Coordinate(0.0575, -0.000253617, 0.0000),
new Coordinate(0.0600, -0.000301899, 0.0000)
};
assertEquals("Canted fin number of points doesn't match! ", expPoints.length, actPoints.length);
assertEquals("Canted root number of points doesn't match! ", expRootPoints.length, rootPoints.length);
for (int i = 0; i < expPoints.length; i++) {
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].x, actPoints[i].x, EPSILON);
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].y, actPoints[i].y, EPSILON);
assertEquals("Canted fin point [" + i + "] doesn't match! ", expPoints[i].z, actPoints[i].z, EPSILON);
}
for (int i = 0; i < expRootPoints.length; i++) {
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].x, rootPoints[i].x, EPSILON);
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].y, rootPoints[i].y, EPSILON);
assertEquals("Canted root point [" + i + "] doesn't match! ", expRootPoints[i].z, rootPoints[i].z, EPSILON);
}
}
@Test @Test
public void testCGCalculation_simpleSquareFin() { public void testCGCalculation_simpleSquareFin() {
final Rocket rkt = createSimpleTrapezoidalFin(); final Rocket rkt = createSimpleTrapezoidalFin();

View File

@ -40,12 +40,9 @@ public class FinRenderer {
{ {
gl.glPushMatrix(); gl.glPushMatrix();
gl.glTranslated(finSet.getLength() / 2, 0, 0); gl.glTranslated(0, - finSet.getBodyRadius(), 0); // Move to the rocket centerline
gl.glTranslated(0, - finSet.getBodyRadius(), 0);
gl.glRotated( Math.toDegrees(finSet.getCantAngle()), 0, 1, 0); gl.glRotated( Math.toDegrees(finSet.getCantAngle()), 0, 1, 0);
gl.glTranslated(-finSet.getLength() / 2, 0, 0);
GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() { GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() {
@Override @Override