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
public Coordinate[] getFinPoints() {
double len = MathUtil.max(length, 0.0001);
Coordinate[] points = new Coordinate[POINTS];
Coordinate[] finPoints = new Coordinate[POINTS];
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

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
*/
public Coordinate[] getRootPoints(final int maximumBodyDivisionCount) {
if( null == parent){
if (parent == null) {
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.
int divisionCount = 1;
final SymmetricComponent body = (SymmetricComponent) getParent();
final SymmetricComponent parent = (SymmetricComponent) getParent();
final double intervalLength = xEnd - xStart;
// 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)
final double xWidth = 0.0025; // width (in meters) of each individual iteration
divisionCount = (int) Math.ceil(intervalLength / xWidth);
@ -1093,13 +1093,18 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
double xIncrement = intervalLength / divisionCount;
// Create the points: step through the radius of the parent
double xCur = xStart;
double xCurr = xStart;
List<Coordinate> points = new ArrayList<>();
for (int index = 0; index < divisionCount+1; index++) {
double yCur = body.getRadius(xCur);
points.add(new Coordinate(xCur, yCur));
double yCurr = parent.getRadius(xCurr);
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.
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) {
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) {
final double x = parent.length;
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.
final int lastIndex = rootPoints.length - 1;
if (Math.abs(rootPoints[lastIndex].x - body.getLength()) < MathUtil.EPSILON) {
rootPoints[lastIndex] = rootPoints[lastIndex].setX(body.getLength()).setY(body.getAftRadius());
if (Math.abs(rootPoints[lastIndex].x - parent.getLength()) < MathUtil.EPSILON) {
rootPoints[lastIndex] = rootPoints[lastIndex].setX(parent.getLength());
}
// 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);
}
/**
* 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 .
*/

View File

@ -392,7 +392,16 @@ public class FreeformFinSet extends FinSet {
@Override
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

View File

@ -193,16 +193,25 @@ public class TrapezoidFinSet extends FinSet {
*/
@Override
public Coordinate[] getFinPoints() {
List<Coordinate> list = new ArrayList<Coordinate>(4);
List<Coordinate> points = new ArrayList<>(4);
list.add(Coordinate.NUL);
list.add(new Coordinate(sweep, height));
points.add(Coordinate.NUL);
points.add(new Coordinate(sweep, height));
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
public void testFreeformCMComputation_trapezoidOnTube() {
final Rocket rkt = createTemplateRocket();

View File

@ -1,6 +1,7 @@
package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
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
public void testCGCalculation_simpleSquareFin() {
final Rocket rkt = createSimpleTrapezoidalFin();

View File

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