import java.util.Collection;
import java.util.List;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation;
public abstract class FinSet extends ExternalComponent {
+ private static final Translator trans = Application.getTranslator();
+
+ // FIXME: converting triangular fins to freeform fails
/**
* Maximum allowed cant of fins.
public enum CrossSection {
- SQUARE("Square", 1.00),
- ROUNDED("Rounded", 0.99),
- AIRFOIL("Airfoil", 0.85);
+ //// Square
+ SQUARE(trans.get("FinSet.CrossSection.SQUARE"), 1.00),
+ //// Rounded
+ ROUNDED(trans.get("FinSet.CrossSection.ROUNDED"), 0.99),
+ //// Airfoil
+ AIRFOIL(trans.get("FinSet.CrossSection.AIRFOIL"), 0.85);
private final String name;
private final double volume;
+
CrossSection(String name, double volume) {
this.name = name;
this.volume = volume;
public double getRelativeVolume() {
return volume;
}
+
@Override
public String toString() {
return name;
}
public enum TabRelativePosition {
- FRONT("Root chord leading edge"),
- CENTER("Root chord midpoint"),
- END("Root chord trailing edge");
+ //// Root chord leading edge
+ FRONT(trans.get("FinSet.TabRelativePosition.FRONT")),
+ //// Root chord midpoint
+ CENTER(trans.get("FinSet.TabRelativePosition.CENTER")),
+ //// Root chord trailing edge
+ END(trans.get("FinSet.TabRelativePosition.END"));
private final String name;
+
TabRelativePosition(String name) {
this.name = name;
}
/**
* Rotation about the x-axis by 2*PI/fins.
*/
- protected Transformation finRotation = Transformation.rotate_x(2*Math.PI/fins);
+ protected Transformation finRotation = Transformation.rotate_x(2 * Math.PI / fins);
/**
* Rotation angle of the first fin. Zero corresponds to the positive y-axis.
*/
protected Transformation baseRotation = Transformation.rotate_x(rotation);
-
+
/**
* Cant angle of fins.
*/
*/
protected double thickness = 0.003;
-
+
/**
* The cross-section shape of the fins.
*/
protected CrossSection crossSection = CrossSection.SQUARE;
-
+
/*
* Fin tab properties.
*/
private double tabShift = 0;
private TabRelativePosition tabRelativePosition = TabRelativePosition.CENTER;
-
+
// Cached fin area & CG. Validity of both must be checked using finArea!
// Fin area does not include fin tabs, CG does.
private double finArea = -1;
public FinSet() {
super(RocketComponent.Position.BOTTOM);
}
-
+
/**
* Return the number of fins in the set.
* @return The number of fins.
public int getFinCount() {
return fins;
}
-
+
/**
* Sets the number of fins in the set.
* @param n The number of fins, greater of equal to one.
if (n > 8)
n = 8;
fins = n;
- finRotation = Transformation.rotate_x(2*Math.PI/fins);
+ finRotation = Transformation.rotate_x(2 * Math.PI / fins);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
public Transformation getFinRotationTransformation() {
return finRotation;
}
-
+
/**
* Gets the base rotation amount of the first fin.
* @return The base rotation amount.
baseRotation = Transformation.rotate_x(rotation);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
public Transformation getBaseRotationTransformation() {
return baseRotation;
}
-
+
public double getCantAngle() {
return cantAngle;
}
public Transformation getCantRotation() {
if (cantRotation == null) {
- if (MathUtil.equals(cantAngle,0)) {
+ if (MathUtil.equals(cantAngle, 0)) {
cantRotation = Transformation.IDENTITY;
} else {
- Transformation t = new Transformation(-length/2,0,0);
+ Transformation t = new Transformation(-length / 2, 0, 0);
t = Transformation.rotate_y(cantAngle).applyTransformation(t);
- t = new Transformation(length/2,0,0).applyTransformation(t);
+ t = new Transformation(length / 2, 0, 0).applyTransformation(t);
cantRotation = t;
}
}
public void setThickness(double r) {
if (thickness == r)
return;
- thickness = Math.max(r,0);
+ thickness = Math.max(r, 0);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
}
-
-
+
+
@Override
public void setRelativePosition(RocketComponent.Position position) {
super.setRelativePosition(position);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
@Override
public void setPositionValue(double value) {
-
+
public double getTabHeight() {
return tabHeight;
}
-
+
public void setTabHeight(double height) {
height = MathUtil.max(height, 0);
if (MathUtil.equals(this.tabHeight, height))
this.tabHeight = height;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
public double getTabLength() {
return tabLength;
}
-
+
public void setTabLength(double length) {
length = MathUtil.max(length, 0);
if (MathUtil.equals(this.tabLength, length))
this.tabLength = length;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
public double getTabShift() {
return tabShift;
}
-
+
public void setTabShift(double shift) {
this.tabShift = shift;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
case FRONT:
this.tabShift = front;
break;
-
+
case CENTER:
- this.tabShift = front + tabLength/2 - getLength()/2;
+ this.tabShift = front + tabLength / 2 - getLength() / 2;
break;
-
+
case END:
this.tabShift = front + tabLength - getLength();
break;
-
+
default:
- throw new IllegalArgumentException("position="+position);
+ throw new IllegalArgumentException("position=" + position);
}
this.tabRelativePosition = position;
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
-
+
/**
* Return the tab front edge position from the front of the fin.
return tabShift;
case CENTER:
- return getLength()/2 - tabLength/2 + tabShift;
+ return getLength() / 2 - tabLength / 2 + tabShift;
case END:
return getLength() - tabLength + tabShift;
default:
- throw new IllegalStateException("tabRelativePosition="+tabRelativePosition);
+ throw new IllegalStateException("tabRelativePosition=" + tabRelativePosition);
}
}
-
+
/**
* Return the tab trailing edge position *from the front of the fin*.
*/
public double getTabTrailingEdge() {
switch (this.tabRelativePosition) {
case FRONT:
- return tabLength + tabShift;
+ return tabLength + tabShift;
case CENTER:
- return getLength()/2 + tabLength/2 + tabShift;
+ return getLength() / 2 + tabLength / 2 + tabShift;
case END:
return getLength() + tabShift;
default:
- throw new IllegalStateException("tabRelativePosition="+tabRelativePosition);
+ throw new IllegalStateException("tabRelativePosition=" + tabRelativePosition);
}
}
-
-
- /////////// Calculation methods ///////////
+
+ /////////// Calculation methods ///////////
+
/**
* Return the area of one side of one fin. This does NOT include the area of
* the fin tab.
if (finArea < 0)
calculateAreaCG();
- return new Coordinate(finCGx,finCGy,0);
+ return new Coordinate(finCGx, finCGy, 0);
}
@Override
public double getComponentVolume() {
- return fins * (getFinArea() + tabHeight*tabLength) * thickness *
- crossSection.getRelativeVolume();
+ return fins * (getFinArea() + tabHeight * tabLength) * thickness *
+ crossSection.getRelativeVolume();
}
-
+
@Override
public Coordinate getComponentCG() {
if (finArea < 0)
calculateAreaCG();
- double mass = getComponentMass(); // safe
+ double mass = getComponentMass(); // safe
if (fins == 1) {
return baseRotation.transform(
- new Coordinate(finCGx,finCGy + getBodyRadius(), 0, mass));
+ new Coordinate(finCGx, finCGy + getBodyRadius(), 0, mass));
} else {
return new Coordinate(finCGx, 0, 0, mass);
}
}
-
+
private void calculateAreaCG() {
Coordinate[] points = this.getFinPoints();
finCGx = 0;
finCGy = 0;
- for (int i=0; i < points.length-1; i++) {
+ for (int i = 0; i < points.length - 1; i++) {
final double x0 = points[i].x;
- final double x1 = points[i+1].x;
+ final double x1 = points[i + 1].x;
final double y0 = points[i].y;
- final double y1 = points[i+1].y;
+ final double y1 = points[i + 1].y;
- double da = (y0+y1)*(x1-x0) / 2;
+ double da = (y0 + y1) * (x1 - x0) / 2;
finArea += da;
- if (Math.abs(y0-y1) < 0.00001) {
- finCGx += (x0+x1)/2 * da;
- finCGy += y0/2 * da;
+ if (Math.abs(y0 - y1) < 0.00001) {
+ finCGx += (x0 + x1) / 2 * da;
+ finCGy += y0 / 2 * da;
} else {
- finCGx += (x0*(2*y0 + y1) + x1*(y0 + 2*y1)) / (3*(y0 + y1)) * da;
- finCGy += (y1 + y0*y0/(y0 + y1))/3 * da;
+ finCGx += (x0 * (2 * y0 + y1) + x1 * (y0 + 2 * y1)) / (3 * (y0 + y1)) * da;
+ finCGy += (y1 + y0 * y0 / (y0 + y1)) / 3 * da;
}
}
double tabArea = tabLength * tabHeight;
if (!MathUtil.equals(tabArea, 0)) {
- double x = (getTabFrontEdge() + getTabTrailingEdge())/2;
- double y = -this.tabHeight/2;
+ double x = (getTabFrontEdge() + getTabTrailingEdge()) / 2;
+ double y = -this.tabHeight / 2;
- finCGx += x*tabArea;
- finCGy += y*tabArea;
+ finCGx += x * tabArea;
+ finCGy += y * tabArea;
}
finCGx /= (finArea + tabArea);
finCGy /= (finArea + tabArea);
} else {
- finCGx = (points[0].x + points[points.length-1].x)/2;
+ finCGx = (points[0].x + points[points.length - 1].x) / 2;
finCGy = 0;
}
}
/*
- * Return an approximation of the longitudal unitary inertia of the fin set.
+ * Return an approximation of the longitudinal unitary inertia of the fin set.
* The process is the following:
*
* 1. Approximate the fin with a rectangular fin
* set and multiplied by the number of fins.
*/
@Override
- public double getLongitudalUnitInertia() {
+ public double getLongitudinalUnitInertia() {
double area = getFinArea();
if (MathUtil.equals(area, 0))
return 0;
// w2 and h2 are squares of the fin width and height
double w = getLength();
double h = getSpan();
- double w2,h2;
+ double w2, h2;
- if (MathUtil.equals(w*h,0)) {
+ if (MathUtil.equals(w * h, 0)) {
w2 = area;
h2 = area;
} else {
- w2 = w*area/h;
- h2 = h*area/w;
+ w2 = w * area / h;
+ h2 = h * area / w;
}
- double inertia = (h2 + 2*w2)/24;
+ double inertia = (h2 + 2 * w2) / 24;
if (fins == 1)
return inertia;
double radius = getBodyRadius();
-
+
return fins * (inertia + MathUtil.pow2(Math.sqrt(h2) + radius));
}
double w = getLength();
double h = getSpan();
- if (MathUtil.equals(w*h,0)) {
+ if (MathUtil.equals(w * h, 0)) {
h = Math.sqrt(area);
} else {
- h = Math.sqrt(h*area/w);
+ h = Math.sqrt(h * area / w);
}
if (fins == 1)
- return h*h / 12;
+ return h * h / 12;
double radius = getBodyRadius();
- return fins * (h*h/12 + MathUtil.pow2(h/2 + radius));
+ return fins * (h * h / 12 + MathUtil.pow2(h / 2 + radius));
}
List<Coordinate> bounds = new ArrayList<Coordinate>();
double r = getBodyRadius();
- for (Coordinate point: getFinPoints()) {
+ for (Coordinate point : getFinPoints()) {
addFinBound(bounds, point.x, point.y + r);
}
return bounds;
}
-
+
/**
* Adds the 2d-coordinate bound (x,y) to the collection for both z-components and for
Coordinate c;
int i;
- c = new Coordinate(x,y,thickness/2);
+ c = new Coordinate(x, y, thickness / 2);
c = baseRotation.transform(c);
set.add(c);
- for (i=1; i<fins; i++) {
+ for (i = 1; i < fins; i++) {
c = finRotation.transform(c);
set.add(c);
}
- c = new Coordinate(x,y,-thickness/2);
+ c = new Coordinate(x, y, -thickness / 2);
c = baseRotation.transform(c);
set.add(c);
- for (i=1; i<fins; i++) {
+ for (i = 1; i < fins; i++) {
c = finRotation.transform(c);
set.add(c);
}
}
-
+
@Override
public void componentChanged(ComponentChangeEvent e) {
if (e.isAerodynamicChange()) {
RocketComponent s;
s = this.getParent();
- while (s!=null) {
+ while (s != null) {
if (s instanceof SymmetricComponent) {
- double x = this.toRelative(new Coordinate(0,0,0), s)[0].x;
- return ((SymmetricComponent)s).getRadius(x);
+ double x = this.toRelative(new Coordinate(0, 0, 0), s)[0].x;
+ return ((SymmetricComponent) s).getRadius(x);
}
s = s.getParent();
}
return 0;
}
+ @Override
+ public boolean allowsChildren() {
+ return false;
+ }
+
/**
* Allows nothing to be attached to a FinSet.
*
}
-
-
+
+
/**
* Return a list of coordinates defining the geometry of a single fin.
* The coordinates are the XY-coordinates of points defining the shape of a single fin,
public Coordinate[] getFinPointsWithTab() {
Coordinate[] points = getFinPoints();
- if (MathUtil.equals(getTabHeight(), 0) ||
+ if (MathUtil.equals(getTabHeight(), 0) ||
MathUtil.equals(getTabLength(), 0))
return points;
double x1 = getTabFrontEdge();
double x2 = getTabTrailingEdge();
double y = -getTabHeight();
-
+
int n = points.length;
- points = Arrays.copyOf(points, points.length+4);
+ points = Arrays.copyOf(points, points.length + 4);
points[n] = new Coordinate(x2, 0);
- points[n+1] = new Coordinate(x2, y);
- points[n+2] = new Coordinate(x1, y);
- points[n+3] = new Coordinate(x1, 0);
+ points[n + 1] = new Coordinate(x2, y);
+ points[n + 2] = new Coordinate(x1, y);
+ points[n + 3] = new Coordinate(x1, 0);
return points;
}
-
+
/**
* Get the span of a single fin. That is, the length from the root to the tip of the fin.
* @return Span of a single fin.
@Override
- protected void copyFrom(RocketComponent c) {
- super.copyFrom(c);
-
- FinSet src = (FinSet)c;
+ protected List<RocketComponent> copyFrom(RocketComponent c) {
+ FinSet src = (FinSet) c;
this.fins = src.fins;
this.finRotation = src.finRotation;
this.rotation = src.rotation;
this.tabLength = src.tabLength;
this.tabRelativePosition = src.tabRelativePosition;
this.tabShift = src.tabShift;
+
+ return super.copyFrom(c);
}
}