From f5e0b36dee6b55a15bd8fedd7fe30e619de0abf8 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Tue, 18 Nov 2014 14:29:20 -0600 Subject: [PATCH] Added preliminary aerodynamics based on approximating each tube fin with 3 regular fins. Added example Tube Fin rocket. --- .../barrowman/TubeFinSetCalc.java | 660 +++++++++++++++++- .../resources/datafiles/examples/Tube Fin.ork | Bin 0 -> 34520 bytes 2 files changed, 654 insertions(+), 6 deletions(-) create mode 100644 swing/resources/datafiles/examples/Tube Fin.ork diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java index 83ec669d6..d6e1d56d7 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java @@ -1,27 +1,675 @@ package net.sf.openrocket.aerodynamics.barrowman; +import static java.lang.Math.pow; +import static net.sf.openrocket.util.MathUtil.pow2; + +import java.util.Arrays; + import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.Warning; +import net.sf.openrocket.aerodynamics.Warning.Other; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.TubeFinSet; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.LinearInterpolator; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.PolyInterpolator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Preliminary computation of tube fin aerodynamics. + * + * Uses a complete clone of FinSetCalc modelling each tube fin as 3 individual fins. It does not correctly account for + * fin & tube fin interference. + * + * Changes to BarrowmanCalculator's calculateFrictionDrag are also probably required. + * + * @author kruland + * + */ public class TubeFinSetCalc extends RocketComponentCalc { + private final static Logger logger = LoggerFactory.getLogger(FinSetCalc.class); + + private static final double STALL_ANGLE = (20 * Math.PI / 180); + + /** Number of divisions in the fin chords. */ + protected static final int DIVISIONS = 48; + + protected double macLength = Double.NaN; // MAC length + protected double macLead = Double.NaN; // MAC leading edge position + protected double macSpan = Double.NaN; // MAC spanwise position + protected double finArea = Double.NaN; // Fin area + protected double ar = Double.NaN; // Fin aspect ratio + protected double span = Double.NaN; // Fin span + protected double cosGamma = Double.NaN; // Cosine of midchord sweep angle + protected double cosGammaLead = Double.NaN; // Cosine of leading edge sweep angle + protected double rollSum = Double.NaN; // Roll damping sum term + + + protected double[] chordLead = new double[DIVISIONS]; + protected double[] chordTrail = new double[DIVISIONS]; + protected double[] chordLength = new double[DIVISIONS]; + + private double[] poly = new double[6]; + + private final double thickness; + private final double bodyRadius; + private final int finCount; + private final double baseRotation; + private final double cantAngle; + protected final int interferenceFinCount; + private final FinSet.CrossSection crossSection; + public TubeFinSetCalc(RocketComponent component) { super(component); - // TODO Auto-generated constructor stub + if (!(component instanceof TubeFinSet)) { + throw new IllegalArgumentException("Illegal component type " + component); + } + + TubeFinSet fin = (TubeFinSet) component; + thickness = fin.getThickness(); + bodyRadius = fin.getBodyRadius(); + finCount = 3 * fin.getFinCount(); + baseRotation = fin.getBaseRotation(); + cantAngle = 0; + span = 2 * fin.getOuterRadius(); + finArea = span * fin.getLength(); + crossSection = FinSet.CrossSection.SQUARE; + + calculateFinGeometry(fin); + calculatePoly(); + interferenceFinCount = calculateInterferenceFinCount(fin); } + /* + * Calculates the non-axial forces produced by the fins (normal and side forces, + * pitch, yaw and roll moments, CP position, CNa). + */ @Override - public void calculateNonaxialForces(FlightConditions conditions, AerodynamicForces forces, WarningSet warnings) { - // TODO Auto-generated method stub + public void calculateNonaxialForces(FlightConditions conditions, + AerodynamicForces forces, WarningSet warnings) { + + if (span < 0.001) { + forces.setCm(0); + forces.setCN(0); + forces.setCNa(0); + forces.setCP(Coordinate.NUL); + forces.setCroll(0); + forces.setCrollDamp(0); + forces.setCrollForce(0); + forces.setCside(0); + forces.setCyaw(0); + return; + } + + // Add warnings (radius/2 == diameter/4) + if (thickness > bodyRadius / 2) { + warnings.add(Warning.THICK_FIN); + } + warnings.add(new Other("Tube fin support is experimental")); + + //////// Calculate CNa. ///////// + + // One fin without interference (both sub- and supersonic): + double cna1 = calculateFinCNa1(conditions); + + // logger.debug("Component cna1 = {}", cna1); + + // Multiple fins with fin-fin interference + double cna; + double theta = conditions.getTheta(); + double angle = baseRotation; + + // Compute basic CNa without interference effects + if (finCount == 1 || finCount == 2) { + // Basic CNa from geometry + double mul = 0; + for (int i = 0; i < finCount; i++) { + mul += MathUtil.pow2(Math.sin(theta - angle)); + angle += 2 * Math.PI / finCount; + } + cna = cna1 * mul; + } else { + // Basic CNa assuming full efficiency + cna = cna1 * finCount / 2.0; + } + + // logger.debug("Component cna = {}", cna); + + // Take into account fin-fin interference effects + switch (interferenceFinCount) { + case 1: + case 2: + case 3: + case 4: + // No interference effect + break; + + case 5: + cna *= 0.948; + break; + + case 6: + cna *= 0.913; + break; + + case 7: + cna *= 0.854; + break; + + case 8: + cna *= 0.81; + break; + + default: + // Assume 75% efficiency + cna *= 0.75; + break; + } + + + // Body-fin interference effect + double r = bodyRadius; + double tau = r / (span + r); + if (Double.isNaN(tau) || Double.isInfinite(tau)) + tau = 0; + cna *= 1 + tau; // Classical Barrowman + // cna *= pow2(1 + tau); // Barrowman thesis (too optimistic??) + // logger.debug("Component cna = {}", cna); + + // TODO: LOW: check for fin tip mach cone interference + // (Barrowman thesis pdf-page 40) + + // TODO: LOW: fin-fin mach cone effect, MIL-HDBK page 5-25 + + // Calculate CP position + double x = macLead + calculateCPPos(conditions) * macLength; + // logger.debug("Component macLead = {}", macLead); + // logger.debug("Component macLength = {}", macLength); + // logger.debug("Component x = {}", x); + + + // Calculate roll forces, reduce forcing above stall angle + + // Without body-fin interference effect: + // forces.CrollForce = fins * (macSpan+r) * cna1 * component.getCantAngle() / + // conditions.getRefLength(); + // With body-fin interference effect: + forces.setCrollForce(finCount * (macSpan + r) * cna1 * (1 + tau) * cantAngle / conditions.getRefLength()); + + if (conditions.getAOA() > STALL_ANGLE) { + // System.out.println("Fin stalling in roll"); + forces.setCrollForce(forces.getCrollForce() * MathUtil.clamp( + 1 - (conditions.getAOA() - STALL_ANGLE) / (STALL_ANGLE / 2), 0, 1)); + } + forces.setCrollDamp(calculateDampingMoment(conditions)); + forces.setCroll(forces.getCrollForce() - forces.getCrollDamp()); + + // System.out.printf(component.getName() + ": roll rate:%.3f force:%.3f damp:%.3f " + + // "total:%.3f\n", + // conditions.getRollRate(), forces.CrollForce, forces.CrollDamp, forces.Croll); + + forces.setCNa(cna); + forces.setCN(cna * MathUtil.min(conditions.getAOA(), STALL_ANGLE)); + forces.setCP(new Coordinate(x, 0, 0, cna)); + forces.setCm(forces.getCN() * x / conditions.getRefLength()); + + /* + * TODO: HIGH: Compute actual side force and yaw moment. + * This is not currently performed because it produces strange results for + * stable rockets that have two fins in the front part of the fuselage, + * where the rocket flies at an ever-increasing angle of attack. This may + * be due to incorrect computation of pitch/yaw damping moments. + */ + // if (fins == 1 || fins == 2) { + // forces.Cside = fins * cna1 * Math.cos(theta-angle) * Math.sin(theta-angle); + // forces.Cyaw = fins * forces.Cside * x / conditions.getRefLength(); + // } else { + // forces.Cside = 0; + // forces.Cyaw = 0; + // } + forces.setCside(0); + forces.setCyaw(0); } + /** + * Returns the MAC length of the fin. This is required in the friction drag + * computation. + * + * @return the MAC length of the fin. + */ + public double getMACLength() { + return macLength; + } + + public double getMidchordPos() { + return macLead + 0.5 * macLength; + } + + /** + * Pre-calculates the fin geometry values. + */ + protected void calculateFinGeometry(TubeFinSet component) { + + ar = 2 * pow2(span) / finArea; + + Coordinate[] points = { + Coordinate.NUL, + new Coordinate(0, span), + new Coordinate(component.getLength(), span), + new Coordinate(component.getLength(), 0) + }; + + + // Calculate the chord lead and trail positions and length + + Arrays.fill(chordLead, Double.POSITIVE_INFINITY); + Arrays.fill(chordTrail, Double.NEGATIVE_INFINITY); + Arrays.fill(chordLength, 0); + + for (int point = 1; point < points.length; point++) { + double x1 = points[point - 1].x; + double y1 = points[point - 1].y; + double x2 = points[point].x; + double y2 = points[point].y; + + // Don't use the default EPSILON since it is too small + // and causes too much numerical instability in the computation of x below + if (MathUtil.equals(y1, y2, 0.001)) + continue; + + int i1 = (int) (y1 * 1.0001 / span * (DIVISIONS - 1)); + int i2 = (int) (y2 * 1.0001 / span * (DIVISIONS - 1)); + i1 = MathUtil.clamp(i1, 0, DIVISIONS - 1); + i2 = MathUtil.clamp(i2, 0, DIVISIONS - 1); + if (i1 > i2) { + int tmp = i2; + i2 = i1; + i1 = tmp; + } + + for (int i = i1; i <= i2; i++) { + // Intersection point (x,y) + double y = i * span / (DIVISIONS - 1); + double x = (y - y2) / (y1 - y2) * x1 + (y1 - y) / (y1 - y2) * x2; + if (x < chordLead[i]) + chordLead[i] = x; + if (x > chordTrail[i]) + chordTrail[i] = x; + + // TODO: LOW: If fin point exactly on chord line, might be counted twice: + if (y1 < y2) { + chordLength[i] -= x; + } else { + chordLength[i] += x; + } + } + } + + // Check and correct any inconsistencies + for (int i = 0; i < DIVISIONS; i++) { + if (Double.isInfinite(chordLead[i]) || Double.isInfinite(chordTrail[i]) || + Double.isNaN(chordLead[i]) || Double.isNaN(chordTrail[i])) { + chordLead[i] = 0; + chordTrail[i] = 0; + } + if (chordLength[i] < 0 || Double.isNaN(chordLength[i])) { + chordLength[i] = 0; + } + if (chordLength[i] > chordTrail[i] - chordLead[i]) { + chordLength[i] = chordTrail[i] - chordLead[i]; + } + } + + /* Calculate fin properties: + * + * macLength // MAC length + * macLead // MAC leading edge position + * macSpan // MAC spanwise position + * ar // Fin aspect ratio (already set) + * span // Fin span (already set) + */ + macLength = 0; + macLead = 0; + macSpan = 0; + cosGamma = 0; + cosGammaLead = 0; + rollSum = 0; + double area = 0; + double radius = component.getBodyRadius(); + + final double dy = span / (DIVISIONS - 1); + for (int i = 0; i < DIVISIONS; i++) { + double length = chordTrail[i] - chordLead[i]; + double y = i * dy; + + macLength += length * length; + logger.debug("macLength = {}, length = {}, i = {}", macLength, length, i); + macSpan += y * length; + macLead += chordLead[i] * length; + area += length; + rollSum += chordLength[i] * pow2(radius + y); + + if (i > 0) { + double dx = (chordTrail[i] + chordLead[i]) / 2 - (chordTrail[i - 1] + chordLead[i - 1]) / 2; + cosGamma += dy / MathUtil.hypot(dx, dy); + + dx = chordLead[i] - chordLead[i - 1]; + cosGammaLead += dy / MathUtil.hypot(dx, dy); + } + } + + macLength *= dy; + logger.debug("macLength = {}", macLength); + macSpan *= dy; + macLead *= dy; + area *= dy; + rollSum *= dy; + + macLength /= area; + macSpan /= area; + macLead /= area; + cosGamma /= (DIVISIONS - 1); + cosGammaLead /= (DIVISIONS - 1); + } + + /////////////// CNa1 calculation //////////////// + + private static final double CNA_SUBSONIC = 0.9; + private static final double CNA_SUPERSONIC = 1.5; + private static final double CNA_SUPERSONIC_B = pow(pow2(CNA_SUPERSONIC) - 1, 1.5); + private static final double GAMMA = 1.4; + private static final LinearInterpolator K1, K2, K3; + private static final PolyInterpolator cnaInterpolator = new PolyInterpolator( + new double[] { CNA_SUBSONIC, CNA_SUPERSONIC }, + new double[] { CNA_SUBSONIC, CNA_SUPERSONIC }, + new double[] { CNA_SUBSONIC } + ); + /* Pre-calculate the values for K1, K2 and K3 */ + static { + // Up to Mach 5 + int n = (int) ((5.0 - CNA_SUPERSONIC) * 10); + double[] x = new double[n]; + double[] k1 = new double[n]; + double[] k2 = new double[n]; + double[] k3 = new double[n]; + for (int i = 0; i < n; i++) { + double M = CNA_SUPERSONIC + i * 0.1; + double beta = MathUtil.safeSqrt(M * M - 1); + x[i] = M; + k1[i] = 2.0 / beta; + k2[i] = ((GAMMA + 1) * pow(M, 4) - 4 * pow2(beta)) / (4 * pow(beta, 4)); + k3[i] = ((GAMMA + 1) * pow(M, 8) + (2 * pow2(GAMMA) - 7 * GAMMA - 5) * pow(M, 6) + + 10 * (GAMMA + 1) * pow(M, 4) + 8) / (6 * pow(beta, 7)); + } + K1 = new LinearInterpolator(x, k1); + K2 = new LinearInterpolator(x, k2); + K3 = new LinearInterpolator(x, k3); + + // System.out.println("K1[m="+CNA_SUPERSONIC+"] = "+k1[0]); + // System.out.println("K2[m="+CNA_SUPERSONIC+"] = "+k2[0]); + // System.out.println("K3[m="+CNA_SUPERSONIC+"] = "+k3[0]); + } + + protected double calculateFinCNa1(FlightConditions conditions) { + double mach = conditions.getMach(); + double ref = conditions.getRefArea(); + double alpha = MathUtil.min(conditions.getAOA(), + Math.PI - conditions.getAOA(), STALL_ANGLE); + + // Subsonic case + if (mach <= CNA_SUBSONIC) { + return 2 * Math.PI * pow2(span) / (1 + MathUtil.safeSqrt(1 + (1 - pow2(mach)) * + pow2(pow2(span) / (finArea * cosGamma)))) / ref; + } + + // Supersonic case + if (mach >= CNA_SUPERSONIC) { + return finArea * (K1.getValue(mach) + K2.getValue(mach) * alpha + + K3.getValue(mach) * pow2(alpha)) / ref; + } + + // Transonic case, interpolate + double subV, superV; + double subD, superD; + + double sq = MathUtil.safeSqrt(1 + (1 - pow2(CNA_SUBSONIC)) * pow2(span * span / (finArea * cosGamma))); + subV = 2 * Math.PI * pow2(span) / ref / (1 + sq); + subD = 2 * mach * Math.PI * pow(span, 6) / (pow2(finArea * cosGamma) * ref * + sq * pow2(1 + sq)); + + superV = finArea * (K1.getValue(CNA_SUPERSONIC) + K2.getValue(CNA_SUPERSONIC) * alpha + + K3.getValue(CNA_SUPERSONIC) * pow2(alpha)) / ref; + superD = -finArea / ref * 2 * CNA_SUPERSONIC / CNA_SUPERSONIC_B; + + // System.out.println("subV="+subV+" superV="+superV+" subD="+subD+" superD="+superD); + + return cnaInterpolator.interpolate(mach, subV, superV, subD, superD, 0); + } + + private double calculateDampingMoment(FlightConditions conditions) { + double rollRate = conditions.getRollRate(); + + if (Math.abs(rollRate) < 0.1) + return 0; + + double mach = conditions.getMach(); + double absRate = Math.abs(rollRate); + + /* + * At low speeds and relatively large roll rates (i.e. near apogee) the + * fin tips rotate well above stall angle. In this case sum the chords + * separately. + */ + if (absRate * (bodyRadius + span) / conditions.getVelocity() > 15 * Math.PI / 180) { + double sum = 0; + for (int i = 0; i < DIVISIONS; i++) { + double dist = bodyRadius + span * i / DIVISIONS; + double aoa = Math.min(absRate * dist / conditions.getVelocity(), 15 * Math.PI / 180); + sum += chordLength[i] * dist * aoa; + } + sum = sum * (span / DIVISIONS) * 2 * Math.PI / conditions.getBeta() / + (conditions.getRefArea() * conditions.getRefLength()); + + // System.out.println("SPECIAL: " + + // (MathUtil.sign(rollRate) *component.getFinCount() * sum)); + return MathUtil.sign(rollRate) * finCount * sum; + } + + if (mach <= CNA_SUBSONIC) { + // System.out.println("BASIC: "+ + // (component.getFinCount() * 2*Math.PI * rollRate * rollSum / + // (conditions.getRefArea() * conditions.getRefLength() * + // conditions.getVelocity() * conditions.getBeta()))); + + return finCount * 2 * Math.PI * rollRate * rollSum / + (conditions.getRefArea() * conditions.getRefLength() * + conditions.getVelocity() * conditions.getBeta()); + } + if (mach >= CNA_SUPERSONIC) { + + double vel = conditions.getVelocity(); + double k1 = K1.getValue(mach); + double k2 = K2.getValue(mach); + double k3 = K3.getValue(mach); + + double sum = 0; + + for (int i = 0; i < DIVISIONS; i++) { + double y = i * span / (DIVISIONS - 1); + double angle = rollRate * (bodyRadius + y) / vel; + + sum += (k1 * angle + k2 * angle * angle + k3 * angle * angle * angle) + * chordLength[i] * (bodyRadius + y); + } + + return finCount * sum * span / (DIVISIONS - 1) / + (conditions.getRefArea() * conditions.getRefLength()); + } + + // Transonic, do linear interpolation + + FlightConditions cond = conditions.clone(); + cond.setMach(CNA_SUBSONIC - 0.01); + double subsonic = calculateDampingMoment(cond); + cond.setMach(CNA_SUPERSONIC + 0.01); + double supersonic = calculateDampingMoment(cond); + + return subsonic * (CNA_SUPERSONIC - mach) / (CNA_SUPERSONIC - CNA_SUBSONIC) + + supersonic * (mach - CNA_SUBSONIC) / (CNA_SUPERSONIC - CNA_SUBSONIC); + } + + /** + * Return the relative position of the CP along the mean aerodynamic chord. + * Below mach 0.5 it is at the quarter chord, above mach 2 calculated using an + * empirical formula, between these two using an interpolation polynomial. + * + * @param cond Mach speed used + * @return CP position along the MAC + */ + private double calculateCPPos(FlightConditions cond) { + double m = cond.getMach(); + // logger.debug("m = {} ", m); + if (m <= 0.5) { + // At subsonic speeds CP at quarter chord + return 0.25; + } + if (m >= 2) { + // At supersonic speeds use empirical formula + double beta = cond.getBeta(); + return (ar * beta - 0.67) / (2 * ar * beta - 1); + } + + // In between use interpolation polynomial + double x = 1.0; + double val = 0; + + for (int i = 0; i < poly.length; i++) { + val += poly[i] * x; + x *= m; + } + // logger.debug("val = {}", val); + return val; + } + + /** + * Calculate CP position interpolation polynomial coefficients from the + * fin geometry. This is a fifth order polynomial that satisfies + * + * p(0.5)=0.25 + * p'(0.5)=0 + * p(2) = f(2) + * p'(2) = f'(2) + * p''(2) = 0 + * p'''(2) = 0 + * + * where f(M) = (ar*sqrt(M^2-1) - 0.67) / (2*ar*sqrt(M^2-1) - 1). + * + * The values were calculated analytically in Mathematica. The coefficients + * are used as poly[0] + poly[1]*x + poly[2]*x^2 + ... + */ + private void calculatePoly() { + double denom = pow2(1 - 3.4641 * ar); // common denominator + + poly[5] = (-1.58025 * (-0.728769 + ar) * (-0.192105 + ar)) / denom; + poly[4] = (12.8395 * (-0.725688 + ar) * (-0.19292 + ar)) / denom; + poly[3] = (-39.5062 * (-0.72074 + ar) * (-0.194245 + ar)) / denom; + poly[2] = (55.3086 * (-0.711482 + ar) * (-0.196772 + ar)) / denom; + poly[1] = (-31.6049 * (-0.705375 + ar) * (-0.198476 + ar)) / denom; + poly[0] = (9.16049 * (-0.588838 + ar) * (-0.20624 + ar)) / denom; + } + + + // @SuppressWarnings("null") + // public static void main(String arg[]) { + // Rocket rocket = TestRocket.makeRocket(); + // FinSet finset = null; + // + // Iterator iter = rocket.deepIterator(); + // while (iter.hasNext()) { + // RocketComponent c = iter.next(); + // if (c instanceof FinSet) { + // finset = (FinSet)c; + // break; + // } + // } + // + // ((TrapezoidFinSet)finset).setHeight(0.10); + // ((TrapezoidFinSet)finset).setRootChord(0.10); + // ((TrapezoidFinSet)finset).setTipChord(0.10); + // ((TrapezoidFinSet)finset).setSweep(0.0); + // + // + // FinSetCalc calc = new FinSetCalc(finset); + // + // calc.calculateFinGeometry(); + // FlightConditions cond = new FlightConditions(new Configuration(rocket)); + // for (double m=0; m < 3; m+=0.05) { + // cond.setMach(m); + // cond.setAOA(0.0*Math.PI/180); + // double cna = calc.calculateFinCNa1(cond); + // System.out.printf("%5.2f "+cna+"\n", m); + // } + // + // } + @Override - public double calculatePressureDragForce(FlightConditions conditions, double stagnationCD, double baseCD, WarningSet warnings) { - // TODO Auto-generated method stub - return 0; + public double calculatePressureDragForce(FlightConditions conditions, + double stagnationCD, double baseCD, WarningSet warnings) { + + double mach = conditions.getMach(); + double drag = 0; + + // Pressure fore-drag + if (crossSection == FinSet.CrossSection.AIRFOIL || + crossSection == FinSet.CrossSection.ROUNDED) { + + // Round leading edge + if (mach < 0.9) { + drag = Math.pow(1 - pow2(mach), -0.417) - 1; + } else if (mach < 1) { + drag = 1 - 1.785 * (mach - 0.9); + } else { + drag = 1.214 - 0.502 / pow2(mach) + 0.1095 / pow2(pow2(mach)); + } + + } else if (crossSection == FinSet.CrossSection.SQUARE) { + drag = stagnationCD; + } else { + throw new UnsupportedOperationException("Unsupported fin profile: " + crossSection); + } + + // Slanted leading edge + drag *= pow2(cosGammaLead); + + // Trailing edge drag + if (crossSection == FinSet.CrossSection.SQUARE) { + drag += baseCD; + } else if (crossSection == FinSet.CrossSection.ROUNDED) { + drag += baseCD / 2; + } + // Airfoil assumed to have zero base drag + + // Scale to correct reference area + drag *= finCount * span * thickness / conditions.getRefArea(); + + return drag; + } + + private static int calculateInterferenceFinCount(TubeFinSet component) { + RocketComponent parent = component.getParent(); + if (parent == null) { + throw new IllegalStateException("fin set without parent component"); + } + + return 3 * component.getFinCount(); } } diff --git a/swing/resources/datafiles/examples/Tube Fin.ork b/swing/resources/datafiles/examples/Tube Fin.ork new file mode 100644 index 0000000000000000000000000000000000000000..211b210b384a243d7a786b1f3a81c2922ca5f029 GIT binary patch literal 34520 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