Merged l10n branch to trunk
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / Transition.java
index 0daa5f7f185bc481cc6da67179cfbfe2b1f997bc..1c2adb36cbdb1ccb8e9a7aa38d8ce123dd3bf294 100644 (file)
@@ -1,29 +1,28 @@
 package net.sf.openrocket.rocketcomponent;
 
-import net.sf.openrocket.util.Coordinate;
-import net.sf.openrocket.util.MathUtil;
+import static java.lang.Math.*;
+import static net.sf.openrocket.util.MathUtil.*;
 
 import java.util.Collection;
 
-import static java.lang.Math.sin;
-import static java.lang.Math.sqrt;
-import static net.sf.openrocket.util.Chars.FRAC12;
-import static net.sf.openrocket.util.Chars.FRAC34;
-import static net.sf.openrocket.util.MathUtil.pow2;
-import static net.sf.openrocket.util.MathUtil.pow3;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.MathUtil;
 
 
 public class Transition extends SymmetricComponent {
+       private static final Translator trans = Application.getTranslator();
        private static final double CLIP_PRECISION = 0.0001;
-
+       
 
        private Shape type;
        private double shapeParameter;
        private boolean clipped; // Not to be read - use isClipped(), which may be overriden
-
+       
        private double radius1, radius2;
        private boolean autoRadius1, autoRadius2; // Whether the start radius is automatic
-
+                       
 
        private double foreShoulderRadius;
        private double foreShoulderThickness;
@@ -33,30 +32,30 @@ public class Transition extends SymmetricComponent {
        private double aftShoulderThickness;
        private double aftShoulderLength;
        private boolean aftShoulderCapped;
-
+       
 
        // Used to cache the clip length
        private double clipLength = -1;
-
+       
        public Transition() {
                super();
-
+               
                this.radius1 = DEFAULT_RADIUS;
                this.radius2 = DEFAULT_RADIUS;
                this.length = DEFAULT_RADIUS * 3;
                this.autoRadius1 = true;
                this.autoRadius2 = true;
-
+               
                this.type = Shape.CONICAL;
                this.shapeParameter = 0;
                this.clipped = true;
        }
-
-
+       
+       
 
 
        ////////  Fore radius  ////////
-
+       
 
        @Override
        public double getForeRadius() {
@@ -73,35 +72,35 @@ public class Transition extends SymmetricComponent {
                }
                return radius1;
        }
-
+       
        public void setForeRadius(double radius) {
                if ((this.radius1 == radius) && (autoRadius1 == false))
                        return;
-
+               
                this.autoRadius1 = false;
                this.radius1 = Math.max(radius, 0);
-
+               
                if (this.thickness > this.radius1 && this.thickness > this.radius2)
                        this.thickness = Math.max(this.radius1, this.radius2);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        @Override
        public boolean isForeRadiusAutomatic() {
                return autoRadius1;
        }
-
+       
        public void setForeRadiusAutomatic(boolean auto) {
                if (autoRadius1 == auto)
                        return;
-
+               
                autoRadius1 = auto;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
        ////////  Aft radius  /////////
-
+       
        @Override
        public double getAftRadius() {
                if (isAftRadiusAutomatic()) {
@@ -117,62 +116,62 @@ public class Transition extends SymmetricComponent {
                }
                return radius2;
        }
-
-
+       
+       
 
        public void setAftRadius(double radius) {
                if ((this.radius2 == radius) && (autoRadius2 == false))
                        return;
-
+               
                this.autoRadius2 = false;
                this.radius2 = Math.max(radius, 0);
-
+               
                if (this.thickness > this.radius1 && this.thickness > this.radius2)
                        this.thickness = Math.max(this.radius1, this.radius2);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        @Override
        public boolean isAftRadiusAutomatic() {
                return autoRadius2;
        }
-
+       
        public void setAftRadiusAutomatic(boolean auto) {
                if (autoRadius2 == auto)
                        return;
-
+               
                autoRadius2 = auto;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
 
        //// Radius automatics
-
+       
        @Override
        protected double getFrontAutoRadius() {
                if (isAftRadiusAutomatic())
                        return -1;
                return getAftRadius();
        }
-
-
+       
+       
        @Override
        protected double getRearAutoRadius() {
                if (isForeRadiusAutomatic())
                        return -1;
                return getForeRadius();
        }
-
-
+       
+       
 
 
        ////////  Type & shape  /////////
-
+       
        public Shape getType() {
                return type;
        }
-
+       
        public void setType(Shape type) {
                if (type == null) {
                        throw new IllegalArgumentException("setType called with null argument");
@@ -184,142 +183,142 @@ public class Transition extends SymmetricComponent {
                this.shapeParameter = type.defaultParameter();
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        public double getShapeParameter() {
                return shapeParameter;
        }
-
+       
        public void setShapeParameter(double n) {
                if (shapeParameter == n)
                        return;
                this.shapeParameter = MathUtil.clamp(n, type.minParameter(), type.maxParameter());
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        public boolean isClipped() {
                if (!type.isClippable())
                        return false;
                return clipped;
        }
-
+       
        public void setClipped(boolean c) {
                if (clipped == c)
                        return;
                clipped = c;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
        public boolean isClippedEnabled() {
                return type.isClippable();
        }
-
+       
        public double getShapeParameterMin() {
                return type.minParameter();
        }
-
+       
        public double getShapeParameterMax() {
                return type.maxParameter();
        }
-
-
+       
+       
        ////////  Shoulders  ////////
-
+       
        public double getForeShoulderRadius() {
                return foreShoulderRadius;
        }
-
+       
        public void setForeShoulderRadius(double foreShoulderRadius) {
                if (MathUtil.equals(this.foreShoulderRadius, foreShoulderRadius))
                        return;
                this.foreShoulderRadius = foreShoulderRadius;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public double getForeShoulderThickness() {
                return foreShoulderThickness;
        }
-
+       
        public void setForeShoulderThickness(double foreShoulderThickness) {
                if (MathUtil.equals(this.foreShoulderThickness, foreShoulderThickness))
                        return;
                this.foreShoulderThickness = foreShoulderThickness;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public double getForeShoulderLength() {
                return foreShoulderLength;
        }
-
+       
        public void setForeShoulderLength(double foreShoulderLength) {
                if (MathUtil.equals(this.foreShoulderLength, foreShoulderLength))
                        return;
                this.foreShoulderLength = foreShoulderLength;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public boolean isForeShoulderCapped() {
                return foreShoulderCapped;
        }
-
+       
        public void setForeShoulderCapped(boolean capped) {
                if (this.foreShoulderCapped == capped)
                        return;
                this.foreShoulderCapped = capped;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
 
 
        public double getAftShoulderRadius() {
                return aftShoulderRadius;
        }
-
+       
        public void setAftShoulderRadius(double aftShoulderRadius) {
                if (MathUtil.equals(this.aftShoulderRadius, aftShoulderRadius))
                        return;
                this.aftShoulderRadius = aftShoulderRadius;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public double getAftShoulderThickness() {
                return aftShoulderThickness;
        }
-
+       
        public void setAftShoulderThickness(double aftShoulderThickness) {
                if (MathUtil.equals(this.aftShoulderThickness, aftShoulderThickness))
                        return;
                this.aftShoulderThickness = aftShoulderThickness;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public double getAftShoulderLength() {
                return aftShoulderLength;
        }
-
+       
        public void setAftShoulderLength(double aftShoulderLength) {
                if (MathUtil.equals(this.aftShoulderLength, aftShoulderLength))
                        return;
                this.aftShoulderLength = aftShoulderLength;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
+       
        public boolean isAftShoulderCapped() {
                return aftShoulderCapped;
        }
-
+       
        public void setAftShoulderCapped(boolean capped) {
                if (this.aftShoulderCapped == capped)
                        return;
                this.aftShoulderCapped = capped;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
 
 
        ///////////   Shape implementations   ////////////
-
+       
 
 
        /**
@@ -329,20 +328,20 @@ public class Transition extends SymmetricComponent {
        public double getRadius(double x) {
                if (x < 0 || x > length)
                        return 0;
-
+               
                double r1 = getForeRadius();
                double r2 = getAftRadius();
-
+               
                if (r1 == r2)
                        return r1;
-
+               
                if (r1 > r2) {
                        x = length - x;
                        double tmp = r1;
                        r1 = r2;
                        r2 = tmp;
                }
-
+               
                if (isClipped()) {
                        // Check clip calculation
                        if (clipLength < 0)
@@ -353,7 +352,7 @@ public class Transition extends SymmetricComponent {
                        return r1 + type.getRadius(x, r2 - r1, length, shapeParameter);
                }
        }
-
+       
        /**
         * Numerically solve clipLength from the equation
         *     r1 == type.getRadius(clipLength,r2,clipLength+length)
@@ -361,27 +360,27 @@ public class Transition extends SymmetricComponent {
         */
        private void calculateClip(double r1, double r2) {
                double min = 0, max = length;
-
+               
                if (r1 >= r2) {
                        double tmp = r1;
                        r1 = r2;
                        r2 = tmp;
                }
-
+               
                if (r1 == 0) {
                        clipLength = 0;
                        return;
                }
-
+               
                if (length <= 0) {
                        clipLength = 0;
                        return;
                }
-
+               
                // Required:
                //    getR(min,min+length,r2) - r1 < 0
                //    getR(max,max+length,r2) - r1 > 0
-
+               
                int n = 0;
                while (type.getRadius(max, r2, max + length, shapeParameter) - r1 < 0) {
                        min = max;
@@ -390,7 +389,7 @@ public class Transition extends SymmetricComponent {
                        if (n > 10)
                                break;
                }
-
+               
                while (true) {
                        clipLength = (min + max) / 2;
                        if ((max - min) < CLIP_PRECISION)
@@ -403,14 +402,14 @@ public class Transition extends SymmetricComponent {
                        }
                }
        }
-
-
+       
+       
        @Override
        public double getInnerRadius(double x) {
                return Math.max(getRadius(x) - thickness, 0);
        }
-
-
+       
+       
 
        @Override
        public Collection<Coordinate> getComponentBounds() {
@@ -421,7 +420,7 @@ public class Transition extends SymmetricComponent {
                        addBound(bounds, getLength() + aftShoulderLength, aftShoulderRadius);
                return bounds;
        }
-
+       
        @Override
        public double getComponentMass() {
                double mass = super.getComponentMass();
@@ -434,7 +433,7 @@ public class Transition extends SymmetricComponent {
                        final double ir = Math.max(getForeShoulderRadius() - getForeShoulderThickness(), 0);
                        mass += ringMass(ir, 0, getForeShoulderThickness(), getMaterial().getDensity());
                }
-
+               
                if (getAftShoulderLength() > 0.001) {
                        final double or = getAftShoulderRadius();
                        final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0);
@@ -444,10 +443,10 @@ public class Transition extends SymmetricComponent {
                        final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0);
                        mass += ringMass(ir, 0, getAftShoulderThickness(), getMaterial().getDensity());
                }
-
+               
                return mass;
        }
-
+       
        @Override
        public Coordinate getComponentCG() {
                Coordinate cg = super.getComponentCG();
@@ -462,7 +461,7 @@ public class Transition extends SymmetricComponent {
                                        getForeShoulderThickness() - getForeShoulderLength(),
                                        getMaterial().getDensity()));
                }
-
+               
                if (getAftShoulderLength() > 0.001) {
                        final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0);
                        cg = cg.average(ringCG(getAftShoulderRadius(), ir, getLength(),
@@ -476,8 +475,8 @@ public class Transition extends SymmetricComponent {
                }
                return cg;
        }
-
-
+       
+       
        /*
         * The moments of inertia are not explicitly corrected for the shoulders.
         * However, since the mass is corrected, the inertia is automatically corrected
@@ -491,30 +490,31 @@ public class Transition extends SymmetricComponent {
         */
        @Override
        public String getComponentName() {
-               return "Transition";
+               //// Transition
+               return trans.get("Transition.Transition");
        }
-
+       
        @Override
        protected void componentChanged(ComponentChangeEvent e) {
                super.componentChanged(e);
                clipLength = -1;
        }
-
+       
        /**
         * Check whether the given type can be added to this component.  Transitions allow any
         * InternalComponents to be added.
         *
-        * @param type  The RocketComponent class type to add.
+        * @param ctype  The RocketComponent class type to add.
         * @return      Whether such a component can be added.
         */
        @Override
-       public boolean isCompatible(Class<? extends RocketComponent> type) {
-               if (InternalComponent.class.isAssignableFrom(type))
+       public boolean isCompatible(Class<? extends RocketComponent> ctype) {
+               if (InternalComponent.class.isAssignableFrom(ctype))
                        return true;
                return false;
        }
-
-
+       
+       
 
        /**
         * An enumeration listing the possible shapes of transitions.
@@ -522,13 +522,16 @@ public class Transition extends SymmetricComponent {
         * @author Sampo Niskanen <sampo.niskanen@iki.fi>
         */
        public static enum Shape {
-
+               
                /**
                 * Conical shape.
                 */
-               CONICAL("Conical",
-                               "A conical nose cone has a profile of a triangle.",
-                               "A conical transition has straight sides.") {
+               //// Conical
+               CONICAL(trans.get("Shape.Conical"),
+                               //// A conical nose cone has a profile of a triangle.
+                               trans.get("Shape.Conical.desc1"),
+                               //// A conical transition has straight sides.
+                               trans.get("Shape.Conical.desc2")) {
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -537,31 +540,28 @@ public class Transition extends SymmetricComponent {
                                return radius * x / length;
                        }
                },
-
+               
                /**
                 * Ogive shape.  The shape parameter is the portion of an extended tangent ogive
                 * that will be used.  That is, for param==1 a tangent ogive will be produced, and
                 * for smaller values the shape straightens out into a cone at param==0.
                 */
-               OGIVE("Ogive",
-                               "An ogive nose cone has a profile that is a segment of a circle.  " +
-                                               "The shape parameter value 1 produces a <b>tangent ogive</b>, which has " +
-                                               "a smooth transition to the body tube, values less than 1 produce " +
-                                               "<b>secant ogives</b>.",
-                               "An ogive transition has a profile that is a segment of a circle.  " +
-                                               "The shape parameter value 1 produces a <b>tangent ogive</b>, which has " +
-                                               "a smooth transition to the body tube at the aft end, values less than 1 " +
-                                               "produce <b>secant ogives</b>.") {
+               //// Ogive
+               OGIVE(trans.get("Shape.Ogive"),
+                               //// An ogive nose cone has a profile that is a segment of a circle.  The shape parameter value 1 produces a <b>tangent ogive</b>, which has a smooth transition to the body tube, values less than 1 produce <b>secant ogives</b>.
+                               trans.get("Shape.Ogive.desc1"),
+                               //// An ogive transition has a profile that is a segment of a circle.   The shape parameter value 1 produces a <b>tangent ogive</b>, which has a smooth transition to the body tube at the aft end, values less than 1 produce <b>secant ogives</b>.
+                               trans.get("Shape.Ogive.desc2")) {
                        @Override
                        public boolean usesParameter() {
                                return true; // Range 0...1 is default
                        }
-
+                       
                        @Override
                        public double defaultParameter() {
                                return 1.0; // Tangent ogive by default
                        }
-
+                       
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -569,17 +569,17 @@ public class Transition extends SymmetricComponent {
                                assert radius >= 0;
                                assert param >= 0;
                                assert param <= 1;
-
+                               
                                // Impossible to calculate ogive for length < radius, scale instead
                                // TODO: LOW: secant ogive could be calculated lower
                                if (length < radius) {
                                        x = x * radius / length;
                                        length = radius;
                                }
-
+                               
                                if (param < 0.001)
                                        return CONICAL.getRadius(x, radius, length, param);
-
+                               
                                // Radius of circle is:
                                double R = sqrt((pow2(length) + pow2(radius)) *
                                                (pow2((2 - param) * length) + pow2(param * radius)) / (4 * pow2(param * radius)));
@@ -589,17 +589,16 @@ public class Transition extends SymmetricComponent {
                                return sqrt(R * R - (L - x) * (L - x)) - y0;
                        }
                },
-
+               
                /**
                 * Ellipsoidal shape.
                 */
-               ELLIPSOID("Ellipsoid",
-                               "An ellipsoidal nose cone has a profile of a half-ellipse " +
-                                               "with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.",
-                               "An ellipsoidal transition has a profile of a half-ellipse " +
-                                               "with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.  If the " +
-                                               "transition is not clipped, then the profile is extended at the center by the " +
-                                               "corresponding radius.", true) {
+               //// Ellipsoid
+               ELLIPSOID(trans.get("Shape.Ellipsoid"),
+                               //// An ellipsoidal nose cone has a profile of a half-ellipse with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.
+                               trans.get("Shape.Ellipsoid.desc1"),
+                               //// An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.  If the transition is not clipped, then the profile is extended at the center by the corresponding radius.         
+                               trans.get("Shape.Ellipsoid.desc2"), true) {
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -609,30 +608,21 @@ public class Transition extends SymmetricComponent {
                                return sqrt(2 * radius * x - x * x); // radius/length * sphere
                        }
                },
-
-               POWER("Power series",
-                               "A power series nose cone has a profile of " +
-                                               "<i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)" +
-                                               "<sup><i>k</i></sup> " +
-                                               "where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 this is a " +
-                                               "<b>" + FRAC12 + "-power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a " +
-                                               "<b>" + FRAC34 + "-power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.",
-                               "A power series transition has a profile of " +
-                                               "<i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)" +
-                                               "<sup><i>k</i></sup> " +
-                                               "where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 the transition is " +
-                                               "<b>" + FRAC12 + "-power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a " +
-                                               "<b>" + FRAC34 + "-power</b>, and for <i>k</i>=1 <b>conical</b>.", true) {
+               
+               //// Power series
+               POWER(trans.get("Shape.Powerseries"),
+                               trans.get("Shape.Powerseries.desc1"),
+                               trans.get("Shape.Powerseries.desc2"), true) {
                        @Override
                        public boolean usesParameter() { // Range 0...1
                                return true;
                        }
-
+                       
                        @Override
                        public double defaultParameter() {
                                return 0.5;
                        }
-
+                       
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -648,34 +638,29 @@ public class Transition extends SymmetricComponent {
                                }
                                return radius * Math.pow(x / length, param);
                        }
-
+                       
                },
-
-               PARABOLIC("Parabolic series",
-                               "A parabolic series nose cone has a profile of a parabola.  The shape " +
-                                               "parameter defines the segment of the parabola to utilize.  The shape " +
-                                               "parameter 1.0 produces a <b>full parabola</b> which is tangent to the body " +
-                                               "tube, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a " +
-                                               "<b>1/2 parabola</b> and 0 produces a <b>conical</b> nose cone.",
-                               "A parabolic series transition has a profile of a parabola.  The shape " +
-                                               "parameter defines the segment of the parabola to utilize.  The shape " +
-                                               "parameter 1.0 produces a <b>full parabola</b> which is tangent to the body " +
-                                               "tube at the aft end, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a " +
-                                               "<b>1/2 parabola</b> and 0 produces a <b>conical</b> transition.") {
-
+               
+               //// Parabolic series
+               PARABOLIC(trans.get("Shape.Parabolicseries"),
+                               ////A parabolic series nose cone has a profile of a parabola.  The shape parameter defines the segment of the parabola to utilize.  The shape parameter 1.0 produces a <b>full parabola</b> which is tangent to the body tube, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a <b>1/2 parabola</b> and 0 produces a <b>conical</b> nose cone.
+                               trans.get("Shape.Parabolicseries.desc1"),
+                               ////A parabolic series transition has a profile of a parabola.  The shape parameter defines the segment of the parabola to utilize.  The shape parameter 1.0 produces a <b>full parabola</b> which is tangent to the body tube at the aft end, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a <b>1/2 parabola</b> and 0 produces a <b>conical</b> transition.
+                               trans.get("Shape.Parabolicseries.desc2")) {
+                       
                        // In principle a parabolic transition is clippable, but the difference is
                        // negligible.
-
+                       
                        @Override
                        public boolean usesParameter() { // Range 0...1
                                return true;
                        }
-
+                       
                        @Override
                        public double defaultParameter() {
                                return 1.0;
                        }
-
+                       
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -683,33 +668,28 @@ public class Transition extends SymmetricComponent {
                                assert radius >= 0;
                                assert param >= 0;
                                assert param <= 1;
-
+                               
                                return radius * ((2 * x / length - param * pow2(x / length)) / (2 - param));
                        }
                },
-
-
-
-               HAACK("Haack series",
-                               "The Haack series nose cones are designed to minimize drag.  The shape parameter " +
-                                               "0 produces an <b>LD-Haack</b> or <b>Von Karman</b> nose cone, which minimizes " +
-                                               "drag for fixed length and diameter, while a value of 0.333 produces an " +
-                                               "<b>LV-Haack</b> nose cone, which minimizes drag for fixed length and volume.",
-                               "The Haack series <i>nose cones</i> are designed to minimize drag.  " +
-                                               "These transition shapes are their equivalents, but do not necessarily produce " +
-                                               "optimal drag for transitions.  " +
-                                               "The shape parameter 0 produces an <b>LD-Haack</b> or <b>Von Karman</b> shape, " +
-                                               "while a value of 0.333 produces an <b>LV-Haack</b> shape.", true) {
+               
+               //// Haack series
+               HAACK(trans.get("Shape.Haackseries"),
+                               //// The Haack series nose cones are designed to minimize drag.  The shape parameter 0 produces an <b>LD-Haack</b> or <b>Von Karman</b> nose cone, which minimizes drag for fixed length and diameter, while a value of 0.333 produces an <b>LV-Haack</b> nose cone, which minimizes drag for fixed length and volume.
+                               trans.get("Shape.Haackseries.desc1"),
+                               //// The Haack series <i>nose cones</i> are designed to minimize drag.  These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions.  The shape parameter 0 produces an <b>LD-Haack</b> or <b>Von Karman</b> shape, while a value of 0.333 produces an <b>LV-Haack</b> shape.            
+                               trans.get("Shape.Haackseries.desc2"), true) {
+                       
                        @Override
                        public boolean usesParameter() {
                                return true;
                        }
-
+                       
                        @Override
                        public double maxParameter() {
                                return 1.0 / 3.0; // Range 0...1/3
                        }
-
+                       
                        @Override
                        public double getRadius(double x, double radius, double length, double param) {
                                assert x >= 0;
@@ -717,7 +697,7 @@ public class Transition extends SymmetricComponent {
                                assert radius >= 0;
                                assert param >= 0;
                                assert param <= 2;
-
+                               
                                double theta = Math.acos(1 - 2 * x / length);
                                if (param == 0) {
                                        return radius * sqrt((theta - sin(2 * theta) / 2) / Math.PI);
@@ -725,7 +705,7 @@ public class Transition extends SymmetricComponent {
                                return radius * sqrt((theta - sin(2 * theta) / 2 + param * pow3(sin(theta))) / Math.PI);
                        }
                },
-
+               
                //              POLYNOMIAL("Smooth polynomial",
                //                              "A polynomial is fitted such that the nose cone profile is horizontal "+
                //                              "at the aft end of the transition.  The angle at the tip is defined by "+
@@ -757,18 +737,18 @@ public class Transition extends SymmetricComponent {
                //                      }
                //              }
                ;
-
+               
                // Privete fields of the shapes
                private final String name;
                private final String transitionDesc;
                private final String noseconeDesc;
                private final boolean canClip;
-
+               
                // Non-clippable constructor
                Shape(String name, String noseconeDesc, String transitionDesc) {
                        this(name, noseconeDesc, transitionDesc, false);
                }
-
+               
                // Clippable constructor
                Shape(String name, String noseconeDesc, String transitionDesc, boolean canClip) {
                        this.name = name;
@@ -776,29 +756,29 @@ public class Transition extends SymmetricComponent {
                        this.noseconeDesc = noseconeDesc;
                        this.transitionDesc = transitionDesc;
                }
-
-
+               
+               
                /**
                 * Return the name of the transition shape name.
                 */
                public String getName() {
                        return name;
                }
-
+               
                /**
                 * Get a description of the Transition shape.
                 */
                public String getTransitionDescription() {
                        return transitionDesc;
                }
-
+               
                /**
                 * Get a description of the NoseCone shape.
                 */
                public String getNoseConeDescription() {
                        return noseconeDesc;
                }
-
+               
                /**
                 * Check whether the shape differs in clipped mode.  The clipping should be
                 * enabled by default if possible.
@@ -806,35 +786,35 @@ public class Transition extends SymmetricComponent {
                public boolean isClippable() {
                        return canClip;
                }
-
+               
                /**
                 * Return whether the shape uses the shape parameter.  (Default false.)
                 */
                public boolean usesParameter() {
                        return false;
                }
-
+               
                /**
                 * Return the minimum value of the shape parameter.  (Default 0.)
                 */
                public double minParameter() {
                        return 0.0;
                }
-
+               
                /**
                 * Return the maximum value of the shape parameter.  (Default 1.)
                 */
                public double maxParameter() {
                        return 1.0;
                }
-
+               
                /**
                 * Return the default value of the shape parameter.  (Default 0.)
                 */
                public double defaultParameter() {
                        return 0.0;
                }
-
+               
                /**
                 * Calculate the basic radius of a transition with the given radius, length and
                 * shape parameter at the point x from the tip of the component.  It is assumed
@@ -848,8 +828,8 @@ public class Transition extends SymmetricComponent {
                 * @return       The basic radius at the given position.
                 */
                public abstract double getRadius(double x, double radius, double length, double param);
-
-
+               
+               
                /**
                 * Returns the name of the shape (same as getName()).
                 */