X-Git-Url: https://git.gag.com/?a=blobdiff_plain;ds=sidebyside;f=core%2Fsrc%2Fnet%2Fsf%2Fopenrocket%2Frocketcomponent%2FTransition.java;fp=core%2Fsrc%2Fnet%2Fsf%2Fopenrocket%2Frocketcomponent%2FTransition.java;h=6b12f156ec15bc6963d109b0ffaa4cf453b67503;hb=9349577cdfdff682b2aabd6daa24fdc3a7449b58;hp=f3a6f9b6a8764925dcb53939fc0244922774da28;hpb=30ba0a882f0c061176ba14dbf86d3d6fad096c02;p=debian%2Fopenrocket diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java index f3a6f9b6..6b12f156 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -1,28 +1,31 @@ package net.sf.openrocket.rocketcomponent; -import static java.lang.Math.sin; -import static net.sf.openrocket.util.MathUtil.*; - -import java.util.Collection; - import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import java.util.Collection; + +import static java.lang.Math.sin; +import static net.sf.openrocket.util.MathUtil.pow2; +import static net.sf.openrocket.util.MathUtil.pow3; + 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; @@ -32,30 +35,39 @@ 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; } - - + + //////// Length //////// + @Override + public void setLength( double length ) { + if ( this.length == length ) { + return; + } + // Need to clearPreset when length changes. + clearPreset(); + super.setLength( length ); + } //////// Fore radius //////// - + @Override public double getForeRadius() { @@ -72,35 +84,39 @@ 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); + + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isForeRadiusAutomatic() { return autoRadius1; } - + public void setForeRadiusAutomatic(boolean auto) { if (autoRadius1 == auto) return; - + autoRadius1 = auto; + + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + //////// Aft radius ///////// - + @Override public double getAftRadius() { if (isAftRadiusAutomatic()) { @@ -116,62 +132,66 @@ 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); + + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isAftRadiusAutomatic() { return autoRadius2; } - + public void setAftRadiusAutomatic(boolean auto) { if (autoRadius2 == auto) return; - + autoRadius2 = auto; + + clearPreset(); 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"); @@ -183,142 +203,144 @@ 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; + clearPreset(); 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; + clearPreset(); 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 //////////// - + /** @@ -328,20 +350,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) @@ -352,7 +374,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) @@ -360,27 +382,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; @@ -389,7 +411,7 @@ public class Transition extends SymmetricComponent { if (n > 10) break; } - + while (true) { clipLength = (min + max) / 2; if ((max - min) < CLIP_PRECISION) @@ -402,14 +424,14 @@ public class Transition extends SymmetricComponent { } } } - - + + @Override public double getInnerRadius(double x) { return Math.max(getRadius(x) - thickness, 0); } - - + + @Override public Collection getComponentBounds() { @@ -420,33 +442,33 @@ public class Transition extends SymmetricComponent { addBound(bounds, getLength() + aftShoulderLength, aftShoulderRadius); return bounds; } - + @Override - public double getComponentMass() { - double mass = super.getComponentMass(); + public double getComponentVolume() { + double volume = super.getComponentVolume(); if (getForeShoulderLength() > 0.001) { final double or = getForeShoulderRadius(); final double ir = Math.max(getForeShoulderRadius() - getForeShoulderThickness(), 0); - mass += ringMass(or, ir, getForeShoulderLength(), getMaterial().getDensity()); + volume += ringVolume( or, ir, getForeShoulderLength() ); } if (isForeShoulderCapped()) { final double ir = Math.max(getForeShoulderRadius() - getForeShoulderThickness(), 0); - mass += ringMass(ir, 0, getForeShoulderThickness(), getMaterial().getDensity()); + volume += ringVolume(ir, 0, getForeShoulderThickness() ); } - + if (getAftShoulderLength() > 0.001) { final double or = getAftShoulderRadius(); final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); - mass += ringMass(or, ir, getAftShoulderLength(), getMaterial().getDensity()); + volume += ringVolume(or, ir, getAftShoulderLength() ); } if (isAftShoulderCapped()) { final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); - mass += ringMass(ir, 0, getAftShoulderThickness(), getMaterial().getDensity()); + volume += ringVolume(ir, 0, getAftShoulderThickness() ); } - - return mass; + + return volume; } - + @Override public Coordinate getComponentCG() { Coordinate cg = super.getComponentCG(); @@ -461,7 +483,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(), @@ -475,8 +497,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 @@ -493,13 +515,13 @@ public class Transition extends SymmetricComponent { //// 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. @@ -513,8 +535,63 @@ public class Transition extends SymmetricComponent { return true; return false; } - - + + @Override + public Type getPresetType() { + return ComponentPreset.Type.TRANSITION; + } + + + @Override + protected void loadFromPreset(ComponentPreset preset) { + + boolean presetFilled = false; + if ( preset.has(ComponentPreset.FILLED ) ) { + presetFilled = preset.get( ComponentPreset.FILLED); + } + + if ( preset.has(ComponentPreset.SHAPE) ) { + Shape s = preset.get(ComponentPreset.SHAPE); + this.setType(s); + } + if ( preset.has(ComponentPreset.AFT_OUTER_DIAMETER) ) { + double outerDiameter = preset.get(ComponentPreset.AFT_OUTER_DIAMETER); + this.setAftRadiusAutomatic(false); + this.setAftRadius(outerDiameter/2.0); + } + if ( preset.has(ComponentPreset.AFT_SHOULDER_LENGTH) ) { + double d = preset.get(ComponentPreset.AFT_SHOULDER_LENGTH); + this.setAftShoulderLength(d); + } + if ( preset.has(ComponentPreset.AFT_SHOULDER_DIAMETER) ) { + double d = preset.get(ComponentPreset.AFT_SHOULDER_DIAMETER); + this.setAftShoulderRadius(d/2.0); + if ( presetFilled ) { + this.setAftShoulderThickness(d/2.0); + } + } + if ( preset.has(ComponentPreset.FORE_OUTER_DIAMETER) ) { + double outerDiameter = preset.get(ComponentPreset.FORE_OUTER_DIAMETER); + this.setForeRadiusAutomatic(false); + this.setForeRadius(outerDiameter/2.0); + } + if ( preset.has(ComponentPreset.FORE_SHOULDER_LENGTH) ) { + double d = preset.get(ComponentPreset.FORE_SHOULDER_LENGTH); + this.setForeShoulderLength(d); + } + if ( preset.has(ComponentPreset.FORE_SHOULDER_DIAMETER) ) { + double d = preset.get(ComponentPreset.FORE_SHOULDER_DIAMETER); + this.setForeShoulderRadius(d/2.0); + if ( presetFilled ) { + this.setForeShoulderThickness(d/2.0); + } + } + + super.loadFromPreset(preset); + + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + + } /** * An enumeration listing the possible shapes of transitions. @@ -522,7 +599,7 @@ public class Transition extends SymmetricComponent { * @author Sampo Niskanen */ public static enum Shape { - + /** * Conical shape. */ @@ -540,7 +617,7 @@ 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 @@ -556,12 +633,12 @@ public class Transition extends SymmetricComponent { 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 +646,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 = MathUtil.safeSqrt((pow2(length) + pow2(radius)) * (pow2((2 - param) * length) + pow2(param * radius)) / (4 * pow2(param * radius))); @@ -589,7 +666,7 @@ public class Transition extends SymmetricComponent { return MathUtil.safeSqrt(R * R - (L - x) * (L - x)) - y0; } }, - + /** * Ellipsoidal shape. */ @@ -597,7 +674,7 @@ public class Transition extends SymmetricComponent { ELLIPSOID(trans.get("Shape.Ellipsoid"), //// An ellipsoidal nose cone has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. trans.get("Shape.Ellipsoid.desc1"), - //// An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. If the transition is not clipped, then the profile is extended at the center by the corresponding radius. + //// An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2×Length and Diameter. 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) { @@ -608,7 +685,7 @@ public class Transition extends SymmetricComponent { return MathUtil.safeSqrt(2 * radius * x - x * x); // radius/length * sphere } }, - + //// Power series POWER(trans.get("Shape.Powerseries"), trans.get("Shape.Powerseries.desc1"), @@ -617,12 +694,12 @@ public class Transition extends SymmetricComponent { 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; @@ -638,29 +715,29 @@ public class Transition extends SymmetricComponent { } return radius * Math.pow(x / length, param); } - + }, - + //// 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 full parabola which is tangent to the body tube, 0.75 produces a 3/4 parabola, 0.5 procudes a 1/2 parabola and 0 produces a conical 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 full parabola which is tangent to the body tube at the aft end, 0.75 produces a 3/4 parabola, 0.5 procudes a 1/2 parabola and 0 produces a conical 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; @@ -668,28 +745,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 series HAACK(trans.get("Shape.Haackseries"), //// The Haack series nose cones are designed to minimize drag. The shape parameter 0 produces an LD-Haack or Von Karman nose cone, which minimizes drag for fixed length and diameter, while a value of 0.333 produces an LV-Haack nose cone, which minimizes drag for fixed length and volume. trans.get("Shape.Haackseries.desc1"), - //// The Haack series nose cones 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 LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack shape. + //// The Haack series nose cones 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 LD-Haack or Von Karman shape, while a value of 0.333 produces an LV-Haack 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; @@ -697,7 +774,7 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 2; - + double theta = Math.acos(1 - 2 * x / length); if (MathUtil.equals(param, 0)) { return radius * MathUtil.safeSqrt((theta - sin(2 * theta) / 2) / Math.PI); @@ -705,7 +782,7 @@ public class Transition extends SymmetricComponent { return radius * MathUtil.safeSqrt((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 "+ @@ -737,18 +814,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; @@ -756,29 +833,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. @@ -786,35 +863,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 @@ -828,8 +905,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()). */ @@ -837,5 +914,22 @@ public class Transition extends SymmetricComponent { public String toString() { return name; } + + /** + * Lookup the Shape given the localized name. This differs from the standard valueOf as that looks up + * based on the canonical name, not the localized name which is an instance var. + * + * @param localizedName + * @return + */ + public static Shape toShape(String localizedName) { + Shape[] values = Shape.values(); + for (Shape value : values) { + if (value.getName().equals(localizedName)) { + return value; + } + } + return null; + } } }