Merge pull request #1911 from SiboVG/issue-1889

[#1889] Add support for tail cones
This commit is contained in:
Joe Pfeiffer 2022-12-24 10:53:09 -07:00 committed by GitHub
commit a761ec5656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 956 additions and 281 deletions

View File

@ -1148,6 +1148,8 @@ NoseConeCfg.tab.General = General
NoseConeCfg.tab.ttip.General = General properties NoseConeCfg.tab.ttip.General = General properties
NoseConeCfg.tab.Shoulder = Shoulder NoseConeCfg.tab.Shoulder = Shoulder
NoseConeCfg.tab.ttip.Shoulder = Shoulder properties NoseConeCfg.tab.ttip.Shoulder = Shoulder properties
NoseConeCfg.checkbox.Flip = Flip to tail cone
NoseConeCfg.checkbox.Flip.ttip = Flips the nose cone direction to a tail cone.
! ParachuteConfig ! ParachuteConfig
Parachute.Parachute = Parachute Parachute.Parachute = Parachute

View File

@ -10,9 +10,11 @@ import net.sf.openrocket.util.Reflection;
//// BooleanSetter - set a boolean value //// BooleanSetter - set a boolean value
class BooleanSetter implements Setter { class BooleanSetter implements Setter {
private final Reflection.Method setMethod; private final Reflection.Method setMethod;
private Object[] extraParameters = null;
public BooleanSetter(Reflection.Method set) { public BooleanSetter(Reflection.Method set, Object... parameters) {
setMethod = set; setMethod = set;
this.extraParameters = parameters;
} }
@Override @Override
@ -20,12 +22,23 @@ class BooleanSetter implements Setter {
WarningSet warnings) { WarningSet warnings) {
s = s.trim(); s = s.trim();
final boolean setValue;
if (s.equalsIgnoreCase("true")) { if (s.equalsIgnoreCase("true")) {
setMethod.invoke(c, true); setValue = true;
} else if (s.equalsIgnoreCase("false")) { } else if (s.equalsIgnoreCase("false")) {
setMethod.invoke(c, false); setValue = false;
} else { } else {
warnings.add(Warning.FILE_INVALID_PARAMETER); warnings.add(Warning.FILE_INVALID_PARAMETER);
return;
}
if (extraParameters != null) {
Object[] parameters = new Object[extraParameters.length + 1];
parameters[0] = setValue;
System.arraycopy(extraParameters, 0, parameters, 1, extraParameters.length);
setMethod.invoke(c, parameters);
} else {
setMethod.invoke(c, setValue);
} }
} }
} }

View File

@ -249,6 +249,8 @@ class DocumentConfig {
setters.put("Transition:aftshouldercapped", new BooleanSetter( setters.put("Transition:aftshouldercapped", new BooleanSetter(
Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class)));
setters.put("NoseCone:isflipped", new BooleanSetter(
Reflection.findMethod(NoseCone.class, "setFlipped", boolean.class, boolean.class), false));
// NoseCone - disable disallowed elements // NoseCone - disable disallowed elements
setters.put("NoseCone:foreradius", null); setters.put("NoseCone:foreradius", null);
setters.put("NoseCone:foreshoulderradius", null); setters.put("NoseCone:foreshoulderradius", null);

View File

@ -1,7 +1,10 @@
package net.sf.openrocket.file.openrocket.savers; package net.sf.openrocket.file.openrocket.savers;
import net.sf.openrocket.rocketcomponent.NoseCone;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
public class NoseConeSaver extends TransitionSaver { public class NoseConeSaver extends TransitionSaver {
@ -20,8 +23,23 @@ public class NoseConeSaver extends TransitionSaver {
@Override @Override
protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) {
NoseCone noseCone = (NoseCone) c;
super.addParams(c, elements); super.addParams(c, elements);
// Transition handles nose cone saving as well if (noseCone.isBaseRadiusAutomatic())
elements.add("<aftradius>auto " + noseCone.getBaseRadiusNoAutomatic() + "</aftradius>");
else
elements.add("<aftradius>" + noseCone.getBaseRadius() + "</aftradius>");
elements.add("<aftshoulderradius>" + noseCone.getShoulderRadius()
+ "</aftshoulderradius>");
elements.add("<aftshoulderlength>" + noseCone.getShoulderLength()
+ "</aftshoulderlength>");
elements.add("<aftshoulderthickness>" + noseCone.getShoulderThickness()
+ "</aftshoulderthickness>");
elements.add("<aftshouldercapped>" + noseCone.isShoulderCapped()
+ "</aftshouldercapped>");
elements.add("<isflipped>" + noseCone.isFlipped() + "</isflipped>");
} }
} }

View File

@ -30,8 +30,6 @@ public class TransitionSaver extends SymmetricComponentSaver {
protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) {
super.addParams(c, elements); super.addParams(c, elements);
net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c;
boolean nosecone = (trans instanceof NoseCone);
Transition.Shape shape = trans.getType(); Transition.Shape shape = trans.getType();
elements.add("<shape>" + shape.name().toLowerCase(Locale.ENGLISH) + "</shape>"); elements.add("<shape>" + shape.name().toLowerCase(Locale.ENGLISH) + "</shape>");
@ -42,30 +40,30 @@ public class TransitionSaver extends SymmetricComponentSaver {
elements.add("<shapeparameter>" + trans.getShapeParameter() + "</shapeparameter>"); elements.add("<shapeparameter>" + trans.getShapeParameter() + "</shapeparameter>");
} }
// Nose cones need other parameter saving, due to the isFlipped() parameter
if (!nosecone) { if (trans instanceof NoseCone) {
if (trans.isForeRadiusAutomatic()) return;
elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>");
else
elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>");
} }
if (trans.isForeRadiusAutomatic())
elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>");
else
elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>");
if (trans.isAftRadiusAutomatic()) if (trans.isAftRadiusAutomatic())
elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>"); elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>");
else else
elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>"); elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>");
if (!nosecone) { elements.add("<foreshoulderradius>" + trans.getForeShoulderRadius()
elements.add("<foreshoulderradius>" + trans.getForeShoulderRadius() + "</foreshoulderradius>");
+ "</foreshoulderradius>"); elements.add("<foreshoulderlength>" + trans.getForeShoulderLength()
elements.add("<foreshoulderlength>" + trans.getForeShoulderLength() + "</foreshoulderlength>");
+ "</foreshoulderlength>"); elements.add("<foreshoulderthickness>" + trans.getForeShoulderThickness()
elements.add("<foreshoulderthickness>" + trans.getForeShoulderThickness() + "</foreshoulderthickness>");
+ "</foreshoulderthickness>"); elements.add("<foreshouldercapped>" + trans.isForeShoulderCapped()
elements.add("<foreshouldercapped>" + trans.isForeShoulderCapped() + "</foreshouldercapped>");
+ "</foreshouldercapped>");
}
elements.add("<aftshoulderradius>" + trans.getAftShoulderRadius() elements.add("<aftshoulderradius>" + trans.getAftShoulderRadius()
+ "</aftshoulderradius>"); + "</aftshoulderradius>");

View File

@ -65,7 +65,11 @@ public class PodSetDTO extends BasePartDTO implements AttachableParts {
} else if (child instanceof BodyTube) { } else if (child instanceof BodyTube) {
addAttachedPart(new BodyTubeDTO((BodyTube) child)); addAttachedPart(new BodyTubeDTO((BodyTube) child));
} else if (child instanceof NoseCone) { } else if (child instanceof NoseCone) {
addAttachedPart(new NoseConeDTO((NoseCone) child)); if (((NoseCone) child).isFlipped()) {
addAttachedPart(new TransitionDTO((NoseCone) child));
} else {
addAttachedPart(new NoseConeDTO((NoseCone) child));
}
} else if (child instanceof Transition) { } else if (child instanceof Transition) {
addAttachedPart(new TransitionDTO((Transition) child)); addAttachedPart(new TransitionDTO((Transition) child));
} }

View File

@ -93,8 +93,12 @@ public class StageDTO {
externalPart.add(theExternalPartDTO); externalPart.add(theExternalPartDTO);
} }
private NoseConeDTO toNoseConeDTO(NoseCone nc) { private AbstractTransitionDTO toNoseConeDTO(NoseCone nc) {
return new NoseConeDTO(nc); if (nc.isFlipped()) {
return new TransitionDTO(nc);
} else {
return new NoseConeDTO(nc);
}
} }
private BodyTubeDTO toBodyTubeDTO(BodyTube bt) { private BodyTubeDTO toBodyTubeDTO(BodyTube bt) {

View File

@ -56,7 +56,7 @@ public class DefaultSimulationModifierService implements SimulationModifierServi
*/ */
addModifier("optimization.modifier.nosecone.length", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Length"); addModifier("optimization.modifier.nosecone.length", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Length");
addModifier("optimization.modifier.nosecone.diameter", UnitGroup.UNITS_LENGTH, 2.0, NoseCone.class, "AftRadius", "isAftRadiusAutomatic"); addModifier("optimization.modifier.nosecone.diameter", UnitGroup.UNITS_LENGTH, 2.0, NoseCone.class, "BaseRadius", "isBaseRadiusAutomatic");
addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness", "isFilled"); addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness", "isFilled");
addModifier("optimization.modifier.transition.length", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Length"); addModifier("optimization.modifier.transition.length", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Length");

View File

@ -13,6 +13,9 @@ import java.util.EventObject;
/** /**
* Rocket nose cones of various types. Implemented as a transition with the * Rocket nose cones of various types. Implemented as a transition with the
* fore radius == 0. * fore radius == 0.
* <p>
* The normal nose cone can be converted to a tail cone by setting the {@link #isFlipped} parameter.
* This will flip all the aft side dimensions with the fore side dimensions.
* *
* @author Sampo Niskanen <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
@ -21,6 +24,7 @@ public class NoseCone extends Transition implements InsideColorComponent {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this);
private boolean isFlipped; // If true, the nose cone is converted to a tail cone
/********* Constructors **********/ /********* Constructors **********/
public NoseCone() { public NoseCone() {
@ -29,16 +33,12 @@ public class NoseCone extends Transition implements InsideColorComponent {
public NoseCone(Transition.Shape type, double length, double radius) { public NoseCone(Transition.Shape type, double length, double radius) {
super(); super();
this.isFlipped = false;
super.setType(type); super.setType(type);
super.setForeRadiusAutomatic(false);
super.setForeRadius(0);
super.setForeShoulderLength(0);
super.setForeShoulderRadius(0.9 * radius);
super.setForeShoulderThickness(0);
super.setForeShoulderCapped(filled);
super.setThickness(0.002); super.setThickness(0.002);
super.setLength(length); super.setLength(length);
super.setClipped(false); super.setClipped(false);
resetForeRadius();
super.setAftRadiusAutomatic(false); super.setAftRadiusAutomatic(false);
super.setAftRadius(radius); super.setAftRadius(radius);
@ -47,72 +47,208 @@ public class NoseCone extends Transition implements InsideColorComponent {
super.displayOrder_back = 0; // Order for displaying the component in the 2D back view super.displayOrder_back = 0; // Order for displaying the component in the 2D back view
} }
/********** Nose cone dimensions **********/
/********** Get/set methods for component parameters **********/ /**
* Returns the base radius of the nose cone (independent of whether the nose cone is flipped or not).
@Override * This method should be used over {@link #getAftRadius()} because it works for both normal and flipped nose cones.
public double getForeRadius() { */
return 0; public double getBaseRadius() {
return isFlipped ? getForeRadius() : getAftRadius();
} }
@Override /**
public void setForeRadius(double r) { * Returns the raw base radius of the nose cone (independent of whether the nose cone is flipped or not).
// No-op * This method should be used over {@link #getAftRadiusNoAutomatic()} because it works for both normal and flipped nose cones.
*/
public double getBaseRadiusNoAutomatic() {
return isFlipped ? getForeRadiusNoAutomatic() : getAftRadiusNoAutomatic();
} }
@Override /**
public boolean isForeRadiusAutomatic() { * Sets the base radius of the nose cone (independent of whether the nose cone is flipped or not).
return false; * This method should be used over {@link #setAftRadius(double)} because it works for both normal and flipped nose cones.
*/
public void setBaseRadius(double radius) {
if (isFlipped) {
setForeRadius(radius);
} else {
setAftRadius(radius);
}
} }
@Override /**
public void setForeRadiusAutomatic(boolean b) { * Returns whether the base radius of the nose cone takes it settings from the previous/next component
// No-op * (independent of whether the nose cone is flipped or not).
* This method should be used over {@link #isAftRadiusAutomatic()} because it works for both normal and flipped nose cones.
*/
public boolean isBaseRadiusAutomatic() {
return isFlipped ? isForeRadiusAutomatic() : isAftRadiusAutomatic();
} }
@Override /**
public boolean usesPreviousCompAutomatic() { * Sets whether the base radius of the nose cone takes it settings from the previous/next component
return false; * (independent of whether the nose cone is flipped or not).
* This method should be used over {@link #setAftRadiusAutomatic(boolean)} because it works for both normal and flipped nose cones.
*/
public void setBaseRadiusAutomatic(boolean auto) {
if (isFlipped) {
setForeRadiusAutomatic(auto);
} else {
setAftRadiusAutomatic(auto);
}
} }
@Override /**
public double getForeShoulderLength() { * Returns the shoulder length, regardless of how the nose cone is flipped (independent of whether the nose cone is flipped or not).
return 0; * This method should be used over {@link #getAftShoulderLength()} because it works for both normal and flipped nose cones.
*/
public double getShoulderLength() {
return isFlipped ? getForeShoulderLength() : getAftShoulderLength();
} }
@Override /**
public double getForeShoulderRadius() { * Sets the shoulder length (independent of whether the nose cone is flipped or not).
return 0; * This method should be used over {@link #setAftShoulderLength(double)} because it works for both normal and flipped nose cones.
*/
public void setShoulderLength(double length) {
if (isFlipped) {
setForeShoulderLength(length);
} else {
setAftShoulderLength(length);
}
} }
@Override /**
public double getForeShoulderThickness() { * Returns the shoulder radius (independent of whether the nose cone is flipped or not).
return 0; * This method should be used over {@link #getAftShoulderRadius()} because it works for both normal and flipped nose cones.
*/
public double getShoulderRadius() {
return isFlipped ? getForeShoulderRadius() : getAftShoulderRadius();
} }
@Override /**
public boolean isForeShoulderCapped() { * Sets the shoulder radius (independent of whether the nose cone is flipped or not).
return false; * This method should be used over {@link #setAftShoulderRadius(double)} because it works for both normal and flipped nose cones.
*/
public void setShoulderRadius(double radius) {
if (isFlipped) {
setForeShoulderRadius(radius);
} else {
setAftShoulderRadius(radius);
}
} }
@Override /**
public void setForeShoulderCapped(boolean capped) { * Returns the shoulder thickness (independent of whether the nose cone is flipped or not).
// No-op * This method should be used over {@link #getAftShoulderThickness()} because it works for both normal and flipped nose cones.
*/
public double getShoulderThickness() {
return isFlipped ? getForeShoulderThickness() : getAftShoulderThickness();
} }
@Override /**
public void setForeShoulderLength(double foreShoulderLength) { * Sets the shoulder thickness (independent of whether the nose cone is flipped or not).
// No-op * This method should be used over {@link #setAftShoulderRadius(double)} because it works for both normal and flipped nose cones.
*/
public void setShoulderThickness(double thickness) {
if (isFlipped) {
setForeShoulderThickness(thickness);
} else {
setAftShoulderThickness(thickness);
}
} }
@Override /**
public void setForeShoulderRadius(double foreShoulderRadius) { * Returns the shoulder cap (independent of whether the nose cone is flipped or not).
// No-op * This method should be used over {@link #isAftShoulderCapped()} because it works for both normal and flipped nose cones.
*/
public boolean isShoulderCapped() {
return isFlipped ? isForeShoulderCapped() : isAftShoulderCapped();
} }
@Override /**
public void setForeShoulderThickness(double foreShoulderThickness) { * Sets the shoulder cap (independent of whether the nose cone is flipped or not).
// No-op * This method should be used over {@link #setAftShoulderCapped(boolean)} because it works for both normal and flipped nose cones.
*/
public void setShoulderCapped(boolean capped) {
if (isFlipped) {
setForeShoulderCapped(capped);
} else {
setAftShoulderCapped(capped);
}
}
/********** Other **********/
/**
* Return true if the nose cone is flipped, i.e. converted to a tail cone, false if it is a regular nose cone.
*/
public boolean isFlipped() {
return isFlipped;
}
/**
* Set the nose cone to be flipped, i.e. converted to a tail cone, or set it to be a regular nose cone.
* @param flipped if true, the nose cone is converted to a tail cone, if false it is a regular nose cone.
* @param sanityCheck whether to check if the auto radius parameter can be used for the new nose cone orientation
*/
public void setFlipped(boolean flipped, boolean sanityCheck) {
if (isFlipped == flipped) {
return;
}
setBypassChangeEvent(true);
if (flipped) {
setForeRadius(getAftRadiusNoAutomatic());
setForeRadiusAutomatic(isAftRadiusAutomatic(), sanityCheck);
setForeShoulderLength(getAftShoulderLength());
setForeShoulderRadius(getAftShoulderRadius());
setForeShoulderThickness(getAftShoulderThickness());
setForeShoulderCapped(isAftShoulderCapped());
resetAftRadius();
} else {
setAftRadius(getForeRadiusNoAutomatic());
setAftRadiusAutomatic(isForeRadiusAutomatic(), sanityCheck);
setAftShoulderLength(getForeShoulderLength());
setAftShoulderRadius(getForeShoulderRadius());
setAftShoulderThickness(getForeShoulderThickness());
setAftShoulderCapped(isForeShoulderCapped());
resetForeRadius();
}
setBypassChangeEvent(false);
isFlipped = flipped;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
/**
* Set the nose cone to be flipped, i.e. converted to a tail cone, or set it to be a regular nose cone.
* @param flipped if true, the nose cone is converted to a tail cone, if false it is a regular nose cone.
*/
public void setFlipped(boolean flipped) {
setFlipped(flipped, true);
}
private void resetForeRadius() {
setForeRadius(0);
setForeRadiusAutomatic(false);
setForeShoulderLength(0);
setForeShoulderRadius(0);
setForeShoulderThickness(0);
setForeShoulderCapped(false);
}
private void resetAftRadius() {
setAftRadius(0);
setAftRadiusAutomatic(false);
setAftShoulderLength(0);
setAftShoulderRadius(0);
setAftShoulderThickness(0);
setAftShoulderCapped(false);
} }
@Override @Override
@ -125,8 +261,6 @@ public class NoseCone extends Transition implements InsideColorComponent {
// No-op // No-op
} }
/********** RocketComponent methods **********/ /********** RocketComponent methods **********/
@Override @Override
@ -136,7 +270,7 @@ public class NoseCone extends Transition implements InsideColorComponent {
@Override @Override
protected void loadFromPreset(ComponentPreset preset) { protected void loadFromPreset(ComponentPreset preset) {
setFlipped(false);
//Many parameters are handled by the super class Transition.loadFromPreset //Many parameters are handled by the super class Transition.loadFromPreset
super.loadFromPreset(preset); super.loadFromPreset(preset);
} }

View File

@ -5,17 +5,13 @@ import static net.sf.openrocket.util.MathUtil.pow2;
import static net.sf.openrocket.util.MathUtil.pow3; import static net.sf.openrocket.util.MathUtil.pow3;
import java.util.Collection; import java.util.Collection;
import java.util.EventObject;
import net.sf.openrocket.appearance.Appearance;
import net.sf.openrocket.appearance.Decal;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type; import net.sf.openrocket.preset.ComponentPreset.Type;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.StateChangeListener;
public class Transition extends SymmetricComponent implements InsideColorComponent { public class Transition extends SymmetricComponent implements InsideColorComponent {
@ -27,8 +23,8 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
private double shapeParameter; private double shapeParameter;
private boolean clipped; // Not to be read - use isClipped(), which may be overridden private boolean clipped; // Not to be read - use isClipped(), which may be overridden
private double foreRadius, aftRadius; protected double foreRadius, aftRadius; // Warning: avoid using these directly, use getForeRadius() and getAftRadius() instead (because the definition of the two can change for flipped nose cones)
private boolean autoForeRadius, autoAftRadius2; // Whether the start radius is automatic protected boolean autoForeRadius, autoAftRadius; // Whether the start radius is automatic
private double foreShoulderRadius; private double foreShoulderRadius;
@ -53,7 +49,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
this.aftRadius = DEFAULT_RADIUS; this.aftRadius = DEFAULT_RADIUS;
this.length = DEFAULT_RADIUS * 3; this.length = DEFAULT_RADIUS * 3;
this.autoForeRadius = true; this.autoForeRadius = true;
this.autoAftRadius2 = true; this.autoAftRadius = true;
this.type = Shape.CONICAL; this.type = Shape.CONICAL;
this.shapeParameter = 0; this.shapeParameter = 0;
@ -81,19 +77,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
@Override @Override
public double getForeRadius() { public double getForeRadius() {
if (isForeRadiusAutomatic()) { if (isForeRadiusAutomatic()) {
// Get the automatic radius from the front return getAutoForeRadius();
double r = -1;
SymmetricComponent c = this.getPreviousSymmetricComponent();
if (c != null) {
r = c.getFrontAutoRadius();
}
if (r < 0)
r = DEFAULT_RADIUS;
return r;
} }
return foreRadius; return foreRadius;
} }
/**
* Returns the automatic radius from the front, taken from the previous component. Returns the default radius if there
* is no previous component.
*/
protected double getAutoForeRadius() {
SymmetricComponent c = this.getPreviousSymmetricComponent();
if (c != null) {
return c.getFrontAutoRadius();
} else {
return DEFAULT_RADIUS;
}
}
/** /**
* Return the fore radius that was manually entered, so not the value that the component received from automatic * Return the fore radius that was manually entered, so not the value that the component received from automatic
* fore radius. * fore radius.
@ -136,13 +137,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
return autoForeRadius; return autoForeRadius;
} }
public void setForeRadiusAutomatic(boolean auto) { /**
* Set the fore radius to automatic mode (takes its value from the previous symmetric component's radius).
*
* @param auto whether to set the fore radius to automatic mode
* @param sanityCheck whether to sanity check auto mode for whether there is a previous component of which you can take the radius
*/
public void setForeRadiusAutomatic(boolean auto, boolean sanityCheck) {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
if (listener instanceof Transition) { if (listener instanceof Transition) {
((Transition) listener).setForeRadiusAutomatic(auto); ((Transition) listener).setForeRadiusAutomatic(auto);
} }
} }
// You can only set the auto fore radius if it is possible
if (sanityCheck) {
auto = auto && canUsePreviousCompAutomatic();
}
if (autoForeRadius == auto) if (autoForeRadius == auto)
return; return;
@ -152,25 +164,34 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
} }
public void setForeRadiusAutomatic(boolean auto) {
setForeRadiusAutomatic(auto, false);
}
//////// Aft radius ///////// //////// Aft radius /////////
@Override @Override
public double getAftRadius() { public double getAftRadius() {
if (isAftRadiusAutomatic()) { if (isAftRadiusAutomatic()) {
// Return the auto radius from the rear return getAutoAftRadius();
double r = -1;
SymmetricComponent c = this.getNextSymmetricComponent();
if (c != null) {
r = c.getRearAutoRadius();
}
if (r < 0)
r = DEFAULT_RADIUS;
return r;
} }
return aftRadius; return aftRadius;
} }
/**
* Returns the automatic radius from the rear, taken from the next component. Returns the default radius if there
* is no next component.
*/
protected double getAutoAftRadius() {
SymmetricComponent c = this.getNextSymmetricComponent();
if (c != null) {
return c.getRearAutoRadius();
} else {
return DEFAULT_RADIUS;
}
}
/** /**
* Return the aft radius that was manually entered, so not the value that the component received from automatic * Return the aft radius that was manually entered, so not the value that the component received from automatic
* zft radius. * zft radius.
@ -191,10 +212,10 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
} }
} }
if ((this.aftRadius == radius) && (autoAftRadius2 == false)) if ((this.aftRadius == radius) && (autoAftRadius == false))
return; return;
this.autoAftRadius2 = false; this.autoAftRadius = false;
this.aftRadius = Math.max(radius, 0); this.aftRadius = Math.max(radius, 0);
if (doClamping && this.thickness > this.foreRadius && this.thickness > this.aftRadius) if (doClamping && this.thickness > this.foreRadius && this.thickness > this.aftRadius)
@ -210,25 +231,40 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
@Override @Override
public boolean isAftRadiusAutomatic() { public boolean isAftRadiusAutomatic() {
return autoAftRadius2; return autoAftRadius;
} }
public void setAftRadiusAutomatic(boolean auto) { /**
* Set the aft radius to automatic mode (takes its value from the next symmetric component's radius).
*
* @param auto whether to set the aft radius to automatic mode
* @param sanityCheck whether to sanity check auto mode for whether there is a next component of which you can take the radius
*/
public void setAftRadiusAutomatic(boolean auto, boolean sanityCheck) {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
if (listener instanceof Transition) { if (listener instanceof Transition) {
((Transition) listener).setAftRadiusAutomatic(auto); ((Transition) listener).setAftRadiusAutomatic(auto);
} }
} }
if (autoAftRadius2 == auto) // You can only set the auto aft radius if it is possible
if (sanityCheck) {
auto = auto && canUseNextCompAutomatic();
}
if (autoAftRadius == auto)
return; return;
autoAftRadius2 = auto; autoAftRadius = auto;
clearPreset(); clearPreset();
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
} }
public void setAftRadiusAutomatic(boolean auto) {
setAftRadiusAutomatic(auto, false);
}
//// Radius automatics //// Radius automatics
@ -257,6 +293,32 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
return isAftRadiusAutomatic(); return isAftRadiusAutomatic();
} }
/**
* Checks whether this component can use the automatic radius of the previous symmetric component.
* @return false if there is no previous symmetric component, or if the previous component already has this component
* as its auto dimension reference
*/
public boolean canUsePreviousCompAutomatic() {
SymmetricComponent referenceComp = getPreviousSymmetricComponent();
if (referenceComp == null) {
return false;
}
return !referenceComp.usesNextCompAutomatic();
}
/**
* Checks whether this component can use the automatic radius of the next symmetric component.
* @return false if there is no next symmetric component, or if the next component already has this component
* as its auto dimension reference
*/
public boolean canUseNextCompAutomatic() {
SymmetricComponent referenceComp = getNextSymmetricComponent();
if (referenceComp == null) {
return false;
}
return !referenceComp.usesPreviousCompAutomatic();
}
//////// Type & shape ///////// //////// Type & shape /////////

View File

@ -0,0 +1,398 @@
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.document.OpenRocketDocumentFactory;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import net.sf.openrocket.util.MathUtil;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class NoseConeTest extends BaseTestCase {
private final double EPSILON = MathUtil.EPSILON * 1000;
@Test
public void testNormalNoseCone() {
NoseCone noseCone = new NoseCone();
// First set the parameters using the normal transition setters (i.e. using AftRadius and AftShoulder instead of Base and Shoulder)
noseCone.setType(Transition.Shape.OGIVE);
noseCone.setLength(0.06);
noseCone.setAftRadius(0.1);
noseCone.setAftShoulderLength(0.01);
noseCone.setAftShoulderRadius(0.05);
noseCone.setAftShoulderCapped(false);
noseCone.setAftShoulderThickness(0.001);
assertEquals(Transition.Shape.OGIVE, noseCone.getType());
assertEquals(0.06, noseCone.getLength(), EPSILON);
assertEquals(0.1, noseCone.getAftRadius(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.1, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(0.01, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.01, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.05, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0.05, noseCone.getShoulderRadius(), EPSILON);
assertFalse(noseCone.isAftShoulderCapped());
assertFalse(noseCone.isShoulderCapped());
assertEquals(0.001, noseCone.getAftShoulderThickness(), EPSILON);
assertEquals(0.001, noseCone.getShoulderThickness(), EPSILON);
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
assertFalse(noseCone.isForeShoulderCapped());
assertFalse(noseCone.isForeRadiusAutomatic());
// Test setting the specific nose cone setters
noseCone.setBaseRadius(0.2);
noseCone.setShoulderLength(0.03);
noseCone.setShoulderRadius(0.04);
noseCone.setShoulderCapped(true);
noseCone.setShoulderThickness(0.005);
assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.2, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(0.03, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0.04, noseCone.getShoulderRadius(), EPSILON);
assertTrue(noseCone.isAftShoulderCapped());
assertTrue(noseCone.isShoulderCapped());
assertEquals(0.005, noseCone.getAftShoulderThickness(), EPSILON);
assertEquals(0.005, noseCone.getShoulderThickness(), EPSILON);
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
assertFalse(noseCone.isForeShoulderCapped());
assertFalse(noseCone.isForeRadiusAutomatic());
}
@Test
public void testFlippedNoseCone() {
NoseCone noseCone = new NoseCone();
// First set the parameters using the normal transition setters (i.e. using AftRadius and AftShoulder instead of Base and Shoulder)
noseCone.setType(Transition.Shape.OGIVE);
noseCone.setLength(0.06);
noseCone.setAftRadius(0.1);
noseCone.setAftShoulderLength(0.01);
noseCone.setAftShoulderRadius(0.05);
noseCone.setAftShoulderCapped(false);
noseCone.setAftShoulderThickness(0.001);
noseCone.setFlipped(true);
assertEquals(Transition.Shape.OGIVE, noseCone.getType());
assertEquals(0.06, noseCone.getLength(), EPSILON);
assertEquals(0.1, noseCone.getForeRadius(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.1, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(0.01, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0.01, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.05, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0.05, noseCone.getShoulderRadius(), EPSILON);
assertFalse(noseCone.isForeShoulderCapped());
assertFalse(noseCone.isShoulderCapped());
assertEquals(0.001, noseCone.getForeShoulderThickness(), EPSILON);
assertEquals(0.001, noseCone.getShoulderThickness(), EPSILON);
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(0, noseCone.getAftRadius(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderThickness(), EPSILON);
assertFalse(noseCone.isAftShoulderCapped());
assertFalse(noseCone.isAftRadiusAutomatic());
// Test setting the specific nose cone setters
noseCone.setBaseRadius(0.2);
noseCone.setShoulderLength(0.03);
noseCone.setShoulderRadius(0.04);
noseCone.setShoulderCapped(true);
noseCone.setShoulderThickness(0.005);
assertEquals(0.2, noseCone.getForeRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.2, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(0.03, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0.04, noseCone.getShoulderRadius(), EPSILON);
assertTrue(noseCone.isForeShoulderCapped());
assertTrue(noseCone.isShoulderCapped());
assertEquals(0.005, noseCone.getForeShoulderThickness(), EPSILON);
assertEquals(0.005, noseCone.getShoulderThickness(), EPSILON);
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(0, noseCone.getAftRadius(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderThickness(), EPSILON);
assertFalse(noseCone.isAftShoulderCapped());
assertFalse(noseCone.isAftRadiusAutomatic());
// Flip back to normal
noseCone.setFlipped(false);
assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.2, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(0.03, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0.04, noseCone.getShoulderRadius(), EPSILON);
assertTrue(noseCone.isAftShoulderCapped());
assertTrue(noseCone.isShoulderCapped());
assertEquals(0.005, noseCone.getAftShoulderThickness(), EPSILON);
assertEquals(0.005, noseCone.getShoulderThickness(), EPSILON);
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
assertFalse(noseCone.isForeShoulderCapped());
assertFalse(noseCone.isForeRadiusAutomatic());
}
@Test
public void testNormalNoseConeRadiusAutomatic() {
Rocket rocket = OpenRocketDocumentFactory.createNewRocket().getRocket();
AxialStage stage = rocket.getStage(0);
NoseCone noseCone = new NoseCone(Transition.Shape.CONICAL, 0.06, 0.01);
BodyTube tube1 = new BodyTube(0.06, 0.02);
tube1.setOuterRadiusAutomatic(false);
BodyTube tube2 = new BodyTube(0.06, 0.03);
tube2.setOuterRadiusAutomatic(false);
// Test no previous or next component
stage.addChild(noseCone);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(stage, noseCone.getPreviousComponent());
assertNull(noseCone.getPreviousSymmetricComponent());
assertNull(noseCone.getNextComponent());
assertNull(noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
noseCone.setForeRadiusAutomatic(true, true);
assertFalse(noseCone.isForeRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
// Test with next component
stage.addChild(tube1);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(stage, noseCone.getPreviousComponent());
assertNull(noseCone.getPreviousSymmetricComponent());
assertSame(tube1, noseCone.getNextComponent());
assertSame(tube1, noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertTrue(noseCone.usesNextCompAutomatic());
assertSame(stage, noseCone.getPreviousComponent());
assertNull(noseCone.getPreviousSymmetricComponent());
assertSame(tube1, noseCone.getNextComponent());
assertSame(tube1, noseCone.getNextSymmetricComponent());
assertTrue(noseCone.isAftRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getForeRadius(), noseCone.getAftRadius(), EPSILON);
assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setAftRadiusAutomatic(false, true);
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setForeRadiusAutomatic(true, true);
assertFalse(noseCone.isForeRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
// Test with previous component
stage.addChild(tube2, 0);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertTrue(noseCone.usesNextCompAutomatic());
assertSame(tube2, noseCone.getPreviousComponent());
assertSame(tube2, noseCone.getPreviousSymmetricComponent());
assertSame(tube1, noseCone.getNextComponent());
assertSame(tube1, noseCone.getNextSymmetricComponent());
assertTrue(noseCone.isAftRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getForeRadius(), noseCone.getAftRadius(), EPSILON);
assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
// Do a flip
noseCone.setFlipped(true);
assertTrue(noseCone.isForeRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isAftRadiusAutomatic());
}
@Test
public void testFlippedNoseConeRadiusAutomatic() {
Rocket rocket = OpenRocketDocumentFactory.createNewRocket().getRocket();
AxialStage stage = rocket.getStage(0);
NoseCone noseCone = new NoseCone(Transition.Shape.CONICAL, 0.06, 0.01);
noseCone.setFlipped(true);
BodyTube tube1 = new BodyTube(0.06, 0.02);
tube1.setOuterRadiusAutomatic(false);
BodyTube tube2 = new BodyTube(0.06, 0.03);
tube2.setOuterRadiusAutomatic(false);
// Test no previous or next component
stage.addChild(noseCone);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(stage, noseCone.getPreviousComponent());
assertNull(noseCone.getPreviousSymmetricComponent());
assertNull(noseCone.getNextComponent());
assertNull(noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
noseCone.setForeRadiusAutomatic(true, true);
assertFalse(noseCone.isForeRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
// Test with previous component
stage.addChild(tube1, 0);
assertFalse(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(tube1, noseCone.getPreviousComponent());
assertSame(tube1, noseCone.getPreviousSymmetricComponent());
assertNull(noseCone.getNextComponent());
assertNull(noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
assertTrue(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(tube1, noseCone.getPreviousComponent());
assertSame(tube1, noseCone.getPreviousSymmetricComponent());
assertNull(noseCone.getNextComponent());
assertNull(noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertTrue(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getAftRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
noseCone.setForeRadiusAutomatic(false, true);
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getAftRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertTrue(noseCone.isForeRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isAftRadiusAutomatic());
// Test with next component
stage.addChild(tube2);
assertTrue(noseCone.usesPreviousCompAutomatic());
assertFalse(noseCone.usesNextCompAutomatic());
assertSame(tube1, noseCone.getPreviousComponent());
assertSame(tube1, noseCone.getPreviousSymmetricComponent());
assertSame(tube2, noseCone.getNextComponent());
assertSame(tube2, noseCone.getNextSymmetricComponent());
assertFalse(noseCone.isAftRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
assertTrue(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
assertEquals(0, noseCone.getAftRadius(), EPSILON);
}
}

View File

@ -58,6 +58,7 @@ The following file format versions exist:
Added PhotoStudio settings saving (<photostudio>) Added PhotoStudio settings saving (<photostudio>)
Added override CD parameter (<overridecd>) Added override CD parameter (<overridecd>)
Added stage activeness remembrance (<stage> under <motorconfiguration>) Added stage activeness remembrance (<stage> under <motorconfiguration>)
Added <isflipped> parameter for Nose Cones
Separated <overridesubcomponents> into individual parameters for mass, CG, and CD. Separated <overridesubcomponents> into individual parameters for mass, CG, and CD.
Rename <fincount> to <instancecount> (<fincount> remains for backward compatibility) Rename <fincount> to <instancecount> (<fincount> remains for backward compatibility)
Rename <position> to <axialoffset> (<position> remains for backward compatibility) Rename <position> to <axialoffset> (<position> remains for backward compatibility)

View File

@ -3,6 +3,8 @@ package net.sf.openrocket.gui.configdialog;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
@ -40,7 +42,7 @@ public class NoseConeConfig extends RocketComponentConfig {
private JLabel shapeLabel; private JLabel shapeLabel;
private JSpinner shapeSpinner; private JSpinner shapeSpinner;
private JSlider shapeSlider; private JSlider shapeSlider;
private final JCheckBox checkAutoAftRadius; private final JCheckBox checkAutoBaseRadius;
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
// Prepended to the description from NoseCone.DESCRIPTIONS // Prepended to the description from NoseCone.DESCRIPTIONS
@ -106,21 +108,21 @@ public class NoseConeConfig extends RocketComponentConfig {
panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Basediam"))); panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Basediam")));
final DoubleModel aftRadiusModel = new DoubleModel(component, "AftRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius final DoubleModel baseRadius = new DoubleModel(component, "BaseRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius
final JSpinner radiusSpinner = new JSpinner(aftRadiusModel.getSpinnerModel()); final JSpinner radiusSpinner = new JSpinner(baseRadius.getSpinnerModel());
radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner)); radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner));
panel.add(radiusSpinner, "growx"); panel.add(radiusSpinner, "growx");
order.add(((SpinnerEditor) radiusSpinner.getEditor()).getTextField()); order.add(((SpinnerEditor) radiusSpinner.getEditor()).getTextField());
panel.add(new UnitSelector(aftRadiusModel), "growx"); panel.add(new UnitSelector(baseRadius), "growx");
panel.add(new BasicSlider(aftRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px"); panel.add(new BasicSlider(baseRadius.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px");
checkAutoAftRadius = new JCheckBox(aftRadiusModel.getAutomaticAction()); checkAutoBaseRadius = new JCheckBox(baseRadius.getAutomaticAction());
//// Automatic //// Automatic
checkAutoAftRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic")); checkAutoBaseRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic"));
panel.add(checkAutoAftRadius, "skip, span 2, wrap"); panel.add(checkAutoBaseRadius, "skip, span 2, wrap");
order.add(checkAutoAftRadius); order.add(checkAutoBaseRadius);
updateCheckboxAutoAftRadius(); updateCheckboxAutoBaseRadius(((NoseCone) component).isFlipped());
} }
{//// Wall thickness: {//// Wall thickness:
@ -142,10 +144,24 @@ public class NoseConeConfig extends RocketComponentConfig {
//// Filled //// Filled
filledCheckbox.setText(trans.get("NoseConeCfg.checkbox.Filled")); filledCheckbox.setText(trans.get("NoseConeCfg.checkbox.Filled"));
filledCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Filled.ttip")); filledCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Filled.ttip"));
panel.add(filledCheckbox, "skip, span 2, wrap"); panel.add(filledCheckbox, "skip, span 2, wrap para");
order.add(filledCheckbox); order.add(filledCheckbox);
} }
{//// Flip to tail cone:
final JCheckBox flipCheckbox = new JCheckBox(new BooleanModel(component, "Flipped"));
flipCheckbox.setText(trans.get("NoseConeCfg.checkbox.Flip"));
flipCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Flip.ttip"));
panel.add(flipCheckbox, "spanx, wrap");
order.add(flipCheckbox);
flipCheckbox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateCheckboxAutoBaseRadius(e.getStateChange() == ItemEvent.SELECTED);
}
});
}
panel.add(new JLabel(""), "growy"); panel.add(new JLabel(""), "growy");
//// Description //// Description
@ -189,25 +205,29 @@ public class NoseConeConfig extends RocketComponentConfig {
* Sets the checkAutoAftRadius checkbox's enabled state and tooltip text, based on the state of its next component. * Sets the checkAutoAftRadius checkbox's enabled state and tooltip text, based on the state of its next component.
* If there is no next symmetric component or if that component already has its auto checkbox checked, the * If there is no next symmetric component or if that component already has its auto checkbox checked, the
* checkAutoAftRadius checkbox is disabled. * checkAutoAftRadius checkbox is disabled.
*
* @param isFlipped whether the nose cone is flipped
*/ */
private void updateCheckboxAutoAftRadius() { private void updateCheckboxAutoBaseRadius(boolean isFlipped) {
if (component == null || checkAutoAftRadius == null) return; if (component == null || checkAutoBaseRadius == null) return;
// Disable check button if there is no component to get the diameter from // Disable check button if there is no component to get the diameter from
SymmetricComponent nextComp = ((NoseCone) component).getNextSymmetricComponent(); NoseCone noseCone = ((NoseCone) component);
if (nextComp == null) { SymmetricComponent referenceComp = isFlipped ? noseCone.getPreviousSymmetricComponent() : noseCone.getNextSymmetricComponent();
checkAutoAftRadius.setEnabled(false); if (referenceComp == null) {
((NoseCone) component).setAftRadiusAutomatic(false); checkAutoBaseRadius.setEnabled(false);
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent")); ((NoseCone) component).setBaseRadiusAutomatic(false);
checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return; return;
} }
if (!nextComp.usesPreviousCompAutomatic()) { if ((!isFlipped&& !referenceComp.usesPreviousCompAutomatic()) ||
checkAutoAftRadius.setEnabled(true); isFlipped && !referenceComp.usesNextCompAutomatic()) {
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic")); checkAutoBaseRadius.setEnabled(true);
checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic"));
} else { } else {
checkAutoAftRadius.setEnabled(false); checkAutoBaseRadius.setEnabled(false);
((NoseCone) component).setAftRadiusAutomatic(false); ((NoseCone) component).setBaseRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto")); checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto"));
} }
} }
} }

View File

@ -613,99 +613,38 @@ public class RocketComponentConfig extends JPanel {
protected JPanel shoulderTab() { protected JPanel shoulderTab() {
JPanel panel = new JPanel(new MigLayout("fill")); JPanel panel = new JPanel(new MigLayout("fill"));
JPanel sub;
DoubleModel m, m2;
DoubleModel m0 = new DoubleModel(0); DoubleModel m0 = new DoubleModel(0);
BooleanModel bm;
JCheckBox check;
JSpinner spin;
//// Fore shoulder, not for NoseCone //// Fore shoulder, not for NoseCone
if (!(component instanceof NoseCone)) { if (!(component instanceof NoseCone)) {
sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); addForeShoulderSection(panel, m0);
//// Fore shoulder
sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.border.Foreshoulder")));
//// Radius
//// Diameter:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter")));
m = new DoubleModel(component, "ForeShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, "ForeRadius", 2, UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
//// Length:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length")));
m = new DoubleModel(component, "ForeShoulderLength", UnitGroup.UNITS_LENGTH, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap");
//// Thickness:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness")));
m = new DoubleModel(component, "ForeShoulderThickness", UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, "ForeShoulderRadius", UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
//// Capped
bm = new BooleanModel(component, "ForeShoulderCapped");
check = new JCheckBox(bm);
//// End capped
check.setText(trans.get("RocketCompCfg.checkbox.Endcapped"));
check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip"));
sub.add(check, "spanx");
order.add(check);
panel.add(sub);
} }
//// Aft shoulder //// Aft shoulder
addAftShoulderSection(panel, m0);
return panel;
}
private void addForeShoulderSection(JPanel panel, DoubleModel m0) {
DoubleModel m;
JCheckBox check;
JPanel sub;
DoubleModel m2;
JSpinner spin;
BooleanModel bm;
sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
if (component instanceof NoseCone) //// Fore shoulder
//// Nose cone shoulder sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.border.Foreshoulder")));
sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Noseconeshoulder")));
else
//// Aft shoulder
sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Aftshoulder")));
//// Radius //// Radius
//// Diameter: //// Diameter:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter"))); sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter")));
m = new DoubleModel(component, "AftShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0); m = new DoubleModel(component, "ForeShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, "AftRadius", 2, UnitGroup.UNITS_LENGTH); m2 = new DoubleModel(component, "ForeRadius", 2, UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel()); spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin)); spin.setEditor(new SpinnerEditor(spin));
@ -719,7 +658,7 @@ public class RocketComponentConfig extends JPanel {
//// Length: //// Length:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length")));
m = new DoubleModel(component, "AftShoulderLength", UnitGroup.UNITS_LENGTH, 0); m = new DoubleModel(component, "ForeShoulderLength", UnitGroup.UNITS_LENGTH, 0);
spin = new JSpinner(m.getSpinnerModel()); spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin)); spin.setEditor(new SpinnerEditor(spin));
@ -733,8 +672,8 @@ public class RocketComponentConfig extends JPanel {
//// Thickness: //// Thickness:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness")));
m = new DoubleModel(component, "AftShoulderThickness", UnitGroup.UNITS_LENGTH, 0); m = new DoubleModel(component, "ForeShoulderThickness", UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, "AftShoulderRadius", UnitGroup.UNITS_LENGTH); m2 = new DoubleModel(component, "ForeShoulderRadius", UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel()); spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin)); spin.setEditor(new SpinnerEditor(spin));
@ -746,7 +685,7 @@ public class RocketComponentConfig extends JPanel {
//// Capped //// Capped
bm = new BooleanModel(component, "AftShoulderCapped"); bm = new BooleanModel(component, "ForeShoulderCapped");
check = new JCheckBox(bm); check = new JCheckBox(bm);
//// End capped //// End capped
check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); check.setText(trans.get("RocketCompCfg.checkbox.Endcapped"));
@ -754,15 +693,89 @@ public class RocketComponentConfig extends JPanel {
sub.add(check, "spanx"); sub.add(check, "spanx");
order.add(check); order.add(check);
panel.add(sub); panel.add(sub);
return panel;
} }
private void addAftShoulderSection(JPanel panel, DoubleModel m0) {
JSpinner spin;
JCheckBox check;
DoubleModel m;
DoubleModel m2;
JPanel sub;
BooleanModel bm;
sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
String valueNameShoulder = "AftShoulder";
String valueNameRadius = "AftRadius";
if (component instanceof NoseCone) {
// Nose cones have a special shoulder method to cope with flipped nose cones
valueNameShoulder = "Shoulder";
valueNameRadius = "BaseRadius";
//// Nose cone shoulder
sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Noseconeshoulder")));
} else {
//// Aft shoulder
sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Aftshoulder")));
}
//// Radius
//// Diameter:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter")));
m = new DoubleModel(component, valueNameShoulder+"Radius", 2, UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, valueNameRadius, 2, UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
//// Length:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length")));
m = new DoubleModel(component, valueNameShoulder+"Length", UnitGroup.UNITS_LENGTH, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap");
//// Thickness:
sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness")));
m = new DoubleModel(component, valueNameShoulder+"Thickness", UnitGroup.UNITS_LENGTH, 0);
m2 = new DoubleModel(component, valueNameShoulder+"Radius", UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
sub.add(spin, "growx");
order.add(((SpinnerEditor) spin.getEditor()).getTextField());
sub.add(new UnitSelector(m), "growx");
sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
//// Capped
bm = new BooleanModel(component, valueNameShoulder+"Capped");
check = new JCheckBox(bm);
//// End capped
check.setText(trans.get("RocketCompCfg.checkbox.Endcapped"));
check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip"));
sub.add(check, "spanx");
order.add(check);
panel.add(sub);
}
/* /*
* Private inner class to handle events in componentNameField. * Private inner class to handle events in componentNameField.

View File

@ -228,18 +228,16 @@ public class TransitionConfig extends RocketComponentConfig {
private void updateCheckboxAutoAftRadius() { private void updateCheckboxAutoAftRadius() {
if (component == null || checkAutoAftRadius == null) return; if (component == null || checkAutoAftRadius == null) return;
// Disable check button if there is no component to get the diameter from Transition transition = (Transition) component;
SymmetricComponent nextComp = ((Transition) component).getNextSymmetricComponent(); boolean enabled = transition.canUseNextCompAutomatic();
if (nextComp == null) { if (enabled) { // Can use auto radius
checkAutoAftRadius.setEnabled(true);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else if (transition.getNextSymmetricComponent() == null) { // No next component to take the auto radius from
checkAutoAftRadius.setEnabled(false); checkAutoAftRadius.setEnabled(false);
((Transition) component).setAftRadiusAutomatic(false); ((Transition) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return; } else { // Next component already has its auto radius checked
}
if (!nextComp.usesPreviousCompAutomatic()) {
checkAutoAftRadius.setEnabled(true);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else {
checkAutoAftRadius.setEnabled(false); checkAutoAftRadius.setEnabled(false);
((Transition) component).setAftRadiusAutomatic(false); ((Transition) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto"));
@ -254,18 +252,16 @@ public class TransitionConfig extends RocketComponentConfig {
private void updateCheckboxAutoForeRadius() { private void updateCheckboxAutoForeRadius() {
if (component == null || checkAutoForeRadius == null) return; if (component == null || checkAutoForeRadius == null) return;
// Disable check button if there is no component to get the diameter from Transition transition = (Transition) component;
SymmetricComponent prevComp = ((Transition) component).getPreviousSymmetricComponent(); boolean enabled = transition.canUsePreviousCompAutomatic();
if (prevComp == null) { if (enabled) { // Can use auto radius
checkAutoForeRadius.setEnabled(true);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else if (transition.getPreviousSymmetricComponent() == null) { // No next component to take the auto radius from
checkAutoForeRadius.setEnabled(false); checkAutoForeRadius.setEnabled(false);
((Transition) component).setForeRadiusAutomatic(false); ((Transition) component).setForeRadiusAutomatic(false);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return; } else { // Next component already has its auto radius checked
}
if (!prevComp.usesNextCompAutomatic()) {
checkAutoForeRadius.setEnabled(true);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else {
checkAutoForeRadius.setEnabled(false); checkAutoForeRadius.setEnabled(false);
((Transition) component).setForeRadiusAutomatic(false); ((Transition) component).setForeRadiusAutomatic(false);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto"));

View File

@ -83,7 +83,7 @@ public class ScaleDialog extends JDialog {
// SymmetricComponent // SymmetricComponent
addScaler(SymmetricComponent.class, "Thickness", "isFilled", SCALERS_NO_OFFSET); addScaler(SymmetricComponent.class, "Thickness", "isFilled", SCALERS_NO_OFFSET);
// Transition + Nose cone // Transition
addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic", SCALERS_NO_OFFSET); addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic", SCALERS_NO_OFFSET);
addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic", SCALERS_NO_OFFSET); addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic", SCALERS_NO_OFFSET);
addScaler(Transition.class, "ForeShoulderRadius", SCALERS_NO_OFFSET); addScaler(Transition.class, "ForeShoulderRadius", SCALERS_NO_OFFSET);
@ -93,6 +93,12 @@ public class ScaleDialog extends JDialog {
addScaler(Transition.class, "AftShoulderThickness", SCALERS_NO_OFFSET); addScaler(Transition.class, "AftShoulderThickness", SCALERS_NO_OFFSET);
addScaler(Transition.class, "AftShoulderLength", SCALERS_NO_OFFSET); addScaler(Transition.class, "AftShoulderLength", SCALERS_NO_OFFSET);
// Nose cone
addScaler(NoseCone.class, "BaseRadius", "isBaseRadiusAutomatic", SCALERS_NO_OFFSET);
addScaler(NoseCone.class, "ShoulderRadius", SCALERS_NO_OFFSET);
addScaler(NoseCone.class, "ShoulderThickness", SCALERS_NO_OFFSET);
addScaler(NoseCone.class, "ShoulderLength", SCALERS_NO_OFFSET);
// Body tube // Body tube
addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic", SCALERS_NO_OFFSET); addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic", SCALERS_NO_OFFSET);
addScaler(BodyTube.class, "MotorOverhang", SCALERS_NO_OFFSET); addScaler(BodyTube.class, "MotorOverhang", SCALERS_NO_OFFSET);
@ -559,6 +565,10 @@ public class ScaleDialog extends JDialog {
} }
Collections.reverse(classes); // Always do the super component scales first (can cause problems otherwise in the scale order) Collections.reverse(classes); // Always do the super component scales first (can cause problems otherwise in the scale order)
for (Class<?> cl : classes) { for (Class<?> cl : classes) {
// Don't use the super-class methods of transitions for nose cones
if (cl == Transition.class && component instanceof NoseCone) {
continue;
}
List<Scaler> list = SCALERS_NO_OFFSET.get(cl); List<Scaler> list = SCALERS_NO_OFFSET.get(cl);
if (list != null && list.size() > 0) { if (list != null && list.size() > 0) {
for (Scaler s : list) { for (Scaler s : list) {