Merge pull request #998 from SiboVG/issue-871

[fixes #871] Automatic base/outer diameter unexpected results/warnings
This commit is contained in:
Joe Pfeiffer 2021-10-05 19:05:39 -06:00 committed by GitHub
commit c8f985ea32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 264 additions and 47 deletions

View File

@ -769,6 +769,9 @@ BodyTubecfg.tab.Generalproperties = General properties
BodyTubecfg.tab.Motor = Motor
BodyTubecfg.tab.Motormountconf = Motor mount configuration
BodyTubecfg.checkbox.Automatic = Automatic
BodyTubecfg.checkbox.ttip.Automatic = Use the diameter of the previous/next component
BodyTubecfg.checkbox.ttip.Automatic_noReferenceComponent = There is no previous/next component to take the diameter of
BodyTubecfg.checkbox.ttip.Automatic_alreadyAuto = The previous/next component already has its auto setting turned on
BodyTubecfg.checkbox.Filled = Filled
! FinSetConfig
@ -1039,6 +1042,9 @@ NoseConeCfg.lbl.Shapeparam = Shape parameter:
NoseConeCfg.lbl.Noseconelength = Nose cone length:
NoseConeCfg.lbl.Basediam = Base diameter:
NoseConeCfg.checkbox.Automatic = Automatic
NoseConeCfg.checkbox.ttip.Automatic = Use the diameter of the next component
NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent = There is no next component to take the diameter of
NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto = The next component already has its auto setting turned on
NoseConeCfg.lbl.Wallthickness = Wall thickness:
NoseConeCfg.checkbox.Filled = Filled
NoseConeCfg.tab.General = General
@ -1140,6 +1146,9 @@ TransitionCfg.lbl.Shapeparam = Shape parameter:
TransitionCfg.lbl.Transitionlength = Transition length:
TransitionCfg.lbl.Forediam = Fore diameter:
TransitionCfg.checkbox.Automatic = Automatic
TransitionCfg.checkbox.ttip.Automatic = Use the diameter of the previous/next component
TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent = There is no previous/next component to take the diameter of
TransitionCfg.checkbox.ttip.Automatic_alreadyAuto = The previous/next component already has its auto setting turned on
TransitionCfg.lbl.Aftdiam = Aft diameter:
TransitionCfg.lbl.Wallthickness = Wall thickness:
TransitionCfg.checkbox.Filled = Filled

View File

@ -152,7 +152,7 @@ class DocumentConfig {
// BodyTube
setters.put("BodyTube:radius", new DoubleSetter(
Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class),
"auto",
"auto", " ",
Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class)));
// Parallel Stage
@ -204,11 +204,11 @@ class DocumentConfig {
setters.put("Transition:foreradius", new DoubleSetter(
Reflection.findMethod(Transition.class, "setForeRadius", double.class),
"auto",
"auto", " ",
Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class)));
setters.put("Transition:aftradius", new DoubleSetter(
Reflection.findMethod(Transition.class, "setAftRadius", double.class),
"auto",
"auto", " ",
Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class)));
setters.put("Transition:foreshoulderradius", new DoubleSetter(

View File

@ -1,6 +1,8 @@
package net.sf.openrocket.file.openrocket.importt;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import net.sf.openrocket.aerodynamics.Warning;
import net.sf.openrocket.aerodynamics.WarningSet;
@ -17,6 +19,7 @@ class DoubleSetter implements Setter {
private final String specialString;
private final Reflection.Method specialMethod;
private final double multiplier;
private String separator;
/**
* Set only the double value.
@ -59,6 +62,23 @@ class DoubleSetter implements Setter {
this.specialMethod = specialMethod;
this.multiplier = 1.0;
}
/**
* Set the double value, or if the value equals the special string, use the
* special setter and set it to true. If the input string contains more information
* besides the special string, you can specify which separator should be used for
* this extra information. The part before the separator is then the special string
* and the part after the separator is the set value.
*
* @param set double setter.
* @param special special string
* @param specialMethod boolean setter.
*/
public DoubleSetter(Reflection.Method set, String special, String separator,
Reflection.Method specialMethod) {
this(set, special, specialMethod);
this.separator = separator;
}
/**
@ -80,26 +100,39 @@ class DoubleSetter implements Setter {
WarningSet warnings) {
s = s.trim();
// Check for special case
if (specialMethod != null && s.equalsIgnoreCase(specialString)) {
specialMethod.invoke(c, true);
return;
String special = s;
String data = s;
String[] args = null;
// Extract special string and data if s contains multiple data elements, separated by separator
if (separator != null) {
args = s.split(this.separator);
if (args.length > 1) {
special = args[0];
data = String.join(separator, Arrays.copyOfRange(args, 1, args.length));
}
}
// Normal case
try {
double d = Double.parseDouble(s);
if (configGetter == null) {
setMethod.invoke(c, d * multiplier);
} else {
FlightConfigurableParameterSet<?> config = (FlightConfigurableParameterSet<?>) configGetter.invoke(c);
Object obj = config.getDefault();
setMethod.invoke(obj, d * multiplier);
if (!special.equalsIgnoreCase(specialString) || (args != null && args.length > 1)) {
try {
double d = Double.parseDouble(data);
if (configGetter == null) {
setMethod.invoke(c, d * multiplier);
} else {
FlightConfigurableParameterSet<?> config = (FlightConfigurableParameterSet<?>) configGetter.invoke(c);
Object obj = config.getDefault();
setMethod.invoke(obj, d * multiplier);
}
} catch (NumberFormatException e) {
warnings.add(Warning.FILE_INVALID_PARAMETER + " data: '" + data + "' - " + c.getName());
}
} catch (NumberFormatException e) {
warnings.add(Warning.FILE_INVALID_PARAMETER);
}
// Check for special case
if (specialMethod != null && special.equalsIgnoreCase(specialString)) {
specialMethod.invoke(c, true);
}
}
}

View File

@ -22,8 +22,9 @@ public class BodyTubeSaver extends SymmetricComponentSaver {
super.addParams(c, elements);
net.sf.openrocket.rocketcomponent.BodyTube tube = (net.sf.openrocket.rocketcomponent.BodyTube) c;
if (tube.isOuterRadiusAutomatic())
elements.add("<radius>auto</radius>");
if (tube.isOuterRadiusAutomatic()) {
elements.add("<radius>auto " + tube.getOuterRadiusNoAutomatic() + "</radius>");
}
else
elements.add("<radius>" + tube.getOuterRadius() + "</radius>");

View File

@ -45,13 +45,13 @@ public class TransitionSaver extends SymmetricComponentSaver {
if (!nosecone) {
if (trans.isForeRadiusAutomatic())
elements.add("<foreradius>auto</foreradius>");
elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>");
else
elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>");
}
if (trans.isAftRadiusAutomatic())
elements.add("<aftradius>auto</aftradius>");
elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>");
else
elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>");

View File

@ -1,10 +1,7 @@
package net.sf.openrocket.rocketcomponent;
import java.util.EventObject;
import java.util.Iterator;
import net.sf.openrocket.appearance.Appearance;
import net.sf.openrocket.appearance.Decal;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
@ -25,6 +22,8 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
private double outerRadius = 0;
private boolean autoRadius = false; // Radius chosen automatically based on parent component
private SymmetricComponent refComp = null; // Reference component that is used for the autoRadius
// When changing the inner radius, thickness is modified
private double overhang = 0;
@ -79,13 +78,17 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
// Return auto radius from front or rear
double r = -1;
SymmetricComponent c = this.getPreviousSymmetricComponent();
if (c != null) {
// Don't use the radius of a component who already has its auto diameter enabled
if (c != null && !c.usesNextCompAutomatic()) {
r = c.getFrontAutoRadius();
refComp = c;
}
if (r < 0) {
c = this.getNextSymmetricComponent();
if (c != null) {
// Don't use the radius of a component who already has its auto diameter enabled
if (c != null && !c.usesPreviousCompAutomatic()) {
r = c.getRearAutoRadius();
refComp = c;
}
}
if (r < 0)
@ -94,6 +97,14 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
}
return outerRadius;
}
/**
* Return the outer radius that was manually entered, so not the value that the component received from automatic
* outer radius.
*/
public double getOuterRadiusNoAutomatic() {
return outerRadius;
}
/**
* Set the outer radius of the body tube. If the radius is less than the wall thickness,
@ -136,8 +147,17 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
clearPreset();
}
@Override
public boolean usesPreviousCompAutomatic() {
return isOuterRadiusAutomatic() && refComp == getPreviousSymmetricComponent();
}
@Override
public boolean usesNextCompAutomatic() {
return isOuterRadiusAutomatic() && refComp == getNextSymmetricComponent();
}
@Override
protected void loadFromPreset(ComponentPreset preset) {
this.autoRadius = false;

View File

@ -69,7 +69,12 @@ public class NoseCone extends Transition implements InsideColorComponent {
public void setForeRadiusAutomatic(boolean b) {
// No-op
}
@Override
public boolean usesPreviousCompAutomatic() {
return false;
}
@Override
public double getForeShoulderLength() {
return 0;

View File

@ -644,5 +644,15 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
}
return null;
}
/**
* Checks whether the component uses the previous symmetric component for its auto diameter.
*/
public abstract boolean usesPreviousCompAutomatic();
/**
* Checks whether the component uses the next symmetric component for its auto diameter.
*/
public abstract boolean usesNextCompAutomatic();
}

View File

@ -94,6 +94,14 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
return foreRadius;
}
/**
* Return the fore radius that was manually entered, so not the value that the component received from automatic
* fore radius.
*/
public double getForeRadiusNoAutomatic() {
return foreRadius;
}
public void setForeRadius(double radius) {
if ((this.foreRadius == radius) && (autoForeRadius == false))
return;
@ -142,7 +150,13 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
return aftRadius;
}
/**
* Return the aft radius that was manually entered, so not the value that the component received from automatic
* zft radius.
*/
public double getAftRadiusNoAutomatic() {
return aftRadius;
}
public void setAftRadius(double radius) {
if ((this.aftRadius == radius) && (autoAftRadius2 == false))
@ -192,7 +206,15 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
return getForeRadius();
}
@Override
public boolean usesPreviousCompAutomatic() {
return isForeRadiusAutomatic();
}
@Override
public boolean usesNextCompAutomatic() {
return isAftRadiusAutomatic();
}
//////// Type & shape /////////

View File

@ -15,8 +15,10 @@ import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
@ -24,6 +26,7 @@ import net.sf.openrocket.unit.UnitGroup;
public class BodyTubeConfig extends RocketComponentConfig {
private DoubleModel maxLength;
private final JCheckBox checkAutoOuterRadius;
private static final Translator trans = Application.getTranslator();
public BodyTubeConfig(OpenRocketDocument d, RocketComponent c) {
@ -58,9 +61,10 @@ public class BodyTubeConfig extends RocketComponentConfig {
//// Automatic
javax.swing.Action outerAutoAction = od.getAutomaticAction();
JCheckBox check = new JCheckBox(outerAutoAction);
check.setText(trans.get("BodyTubecfg.checkbox.Automatic"));
panel.add(check, "skip, span 2, wrap");
checkAutoOuterRadius = new JCheckBox(outerAutoAction);
checkAutoOuterRadius.setText(trans.get("BodyTubecfg.checkbox.Automatic"));
panel.add(checkAutoOuterRadius, "skip, span 2, wrap");
updateCheckboxAutoAftRadius();
//// Inner diameter
panel.add(new JLabel(trans.get("BodyTubecfg.lbl.Innerdiameter")));
@ -87,7 +91,7 @@ public class BodyTubeConfig extends RocketComponentConfig {
panel.add(new BasicSlider(thicknessModel.getSliderModel(0, 0.01)), "w 100lp, wrap 0px");
//// Filled
check = new JCheckBox(new BooleanModel(component, "Filled"));
JCheckBox check = new JCheckBox(new BooleanModel(component, "Filled"));
check.setText(trans.get("BodyTubecfg.checkbox.Filled"));
panel.add(check, "skip, span 2, wrap");
@ -113,4 +117,33 @@ public class BodyTubeConfig extends RocketComponentConfig {
super.updateFields();
}
/**
* Sets the checkAutoOuterRadius checkbox's enabled state and tooltip text, based on the state of its previous
* component. If there is no next and previous symmetric component, the checkAutoOuterRadius checkbox is disabled.
* If there is still a next or previous component which does not have its auto state enabled, meaning it can still
* serve as a reference component for this component, the auto checkbox is enabled.
*/
private void updateCheckboxAutoAftRadius() {
if (component == null || checkAutoOuterRadius == null) return;
// Disable check button if there is no component to get the diameter from
SymmetricComponent prevComp = ((BodyTube) component).getPreviousSymmetricComponent();
SymmetricComponent nextComp = ((BodyTube) component).getNextSymmetricComponent();
if (prevComp == null && nextComp == null) {
checkAutoOuterRadius.setEnabled(false);
((BodyTube) component).setOuterRadiusAutomatic(false);
checkAutoOuterRadius.setToolTipText(trans.get("BodyTubecfg.checkbox.ttip.Automatic_noReferenceComponent"));
return;
}
if (!(prevComp != null && nextComp == null && prevComp.usesNextCompAutomatic()) &&
!(nextComp != null && prevComp == null && nextComp.usesPreviousCompAutomatic()) &&
!(nextComp != null && prevComp != null && prevComp.usesNextCompAutomatic() && nextComp.usesPreviousCompAutomatic())) {
checkAutoOuterRadius.setEnabled(true);
checkAutoOuterRadius.setToolTipText(trans.get("BodyTubecfg.checkbox.ttip.Automatic"));
} else {
checkAutoOuterRadius.setEnabled(false);
((BodyTube) component).setOuterRadiusAutomatic(false);
checkAutoOuterRadius.setToolTipText(trans.get("BodyTubecfg.checkbox.ttip.Automatic_alreadyAuto"));
}
}
}

View File

@ -23,6 +23,7 @@ import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
@ -36,6 +37,7 @@ public class NoseConeConfig extends RocketComponentConfig {
private JLabel shapeLabel;
private JSpinner shapeSpinner;
private JSlider shapeSlider;
private final JCheckBox checkAutoAftRadius;
private static final Translator trans = Application.getTranslator();
// Prepended to the description from NoseCone.DESCRIPTIONS
@ -109,10 +111,11 @@ public class NoseConeConfig extends RocketComponentConfig {
panel.add(new UnitSelector(aftRadiusModel), "growx");
panel.add(new BasicSlider(aftRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px");
JCheckBox check = new JCheckBox(aftRadiusModel.getAutomaticAction());
checkAutoAftRadius = new JCheckBox(aftRadiusModel.getAutomaticAction());
//// Automatic
check.setText(trans.get("NoseConeCfg.checkbox.Automatic"));
panel.add(check, "skip, span 2, wrap");
checkAutoAftRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic"));
panel.add(checkAutoAftRadius, "skip, span 2, wrap");
updateCheckboxAutoAftRadius();
}
{//// Wall thickness:
@ -165,6 +168,30 @@ public class NoseConeConfig extends RocketComponentConfig {
shapeSpinner.setEnabled(e);
shapeSlider.setEnabled(e);
}
/**
* 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
* checkAutoAftRadius checkbox is disabled.
*/
private void updateCheckboxAutoAftRadius() {
if (component == null || checkAutoAftRadius == null) return;
// Disable check button if there is no component to get the diameter from
SymmetricComponent nextComp = ((NoseCone) component).getNextSymmetricComponent();
if (nextComp == null) {
checkAutoAftRadius.setEnabled(false);
((NoseCone) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return;
}
if (!nextComp.usesPreviousCompAutomatic()) {
checkAutoAftRadius.setEnabled(true);
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic"));
} else {
checkAutoAftRadius.setEnabled(false);
((NoseCone) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto"));
}
}
}

View File

@ -21,6 +21,7 @@ import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
@ -35,6 +36,8 @@ public class TransitionConfig extends RocketComponentConfig {
private JLabel shapeLabel;
private JSpinner shapeSpinner;
private BasicSlider shapeSlider;
private final JCheckBox checkAutoAftRadius;
private final JCheckBox checkAutoForeRadius;
private DescriptionArea description;
@ -120,10 +123,11 @@ public class TransitionConfig extends RocketComponentConfig {
panel.add(new UnitSelector(foreRadiusModel), "growx");
panel.add(new BasicSlider(foreRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px");
final JCheckBox checkbox = new JCheckBox(foreRadiusModel.getAutomaticAction());
checkAutoForeRadius = new JCheckBox(foreRadiusModel.getAutomaticAction());
//// Automatic
checkbox.setText(trans.get("TransitionCfg.checkbox.Automatic"));
panel.add(checkbox, "skip, span 2, wrap");
checkAutoForeRadius.setText(trans.get("TransitionCfg.checkbox.Automatic"));
panel.add(checkAutoForeRadius, "skip, span 2, wrap");
updateCheckboxAutoForeRadius();
}
{ //// Aft diameter:
@ -139,10 +143,11 @@ public class TransitionConfig extends RocketComponentConfig {
panel.add(new UnitSelector(aftRadiusModel), "growx");
panel.add(new BasicSlider(aftRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px");
final JCheckBox aftRadiusCheckbox = new JCheckBox(aftRadiusModel.getAutomaticAction());
checkAutoAftRadius = new JCheckBox(aftRadiusModel.getAutomaticAction());
//// Automatic
aftRadiusCheckbox.setText(trans.get("TransitionCfg.checkbox.Automatic"));
panel.add(aftRadiusCheckbox, "skip, span 2, wrap");
checkAutoAftRadius.setText(trans.get("TransitionCfg.checkbox.Automatic"));
panel.add(checkAutoAftRadius, "skip, span 2, wrap");
updateCheckboxAutoAftRadius();
}
{ /// Wall thickness:
@ -194,5 +199,57 @@ public class TransitionConfig extends RocketComponentConfig {
shapeSpinner.setEnabled(e);
shapeSlider.setEnabled(e);
}
/**
* 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
* checkAutoAftRadius checkbox is disabled.
*/
private void updateCheckboxAutoAftRadius() {
if (component == null || checkAutoAftRadius == null) return;
// Disable check button if there is no component to get the diameter from
SymmetricComponent nextComp = ((Transition) component).getNextSymmetricComponent();
if (nextComp == null) {
checkAutoAftRadius.setEnabled(false);
((Transition) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return;
}
if (!nextComp.usesPreviousCompAutomatic()) {
checkAutoAftRadius.setEnabled(true);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else {
checkAutoAftRadius.setEnabled(false);
((Transition) component).setAftRadiusAutomatic(false);
checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto"));
}
}
/**
* Sets the checkAutoForeRadius 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
* checkAutoForeRadius checkbox is disabled.
*/
private void updateCheckboxAutoForeRadius() {
if (component == null || checkAutoForeRadius == null) return;
// Disable check button if there is no component to get the diameter from
SymmetricComponent prevComp = ((Transition) component).getPreviousSymmetricComponent();
if (prevComp == null) {
checkAutoForeRadius.setEnabled(false);
((Transition) component).setForeRadiusAutomatic(false);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent"));
return;
}
if (!prevComp.usesNextCompAutomatic()) {
checkAutoForeRadius.setEnabled(true);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic"));
} else {
checkAutoForeRadius.setEnabled(false);
((Transition) component).setForeRadiusAutomatic(false);
checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto"));
}
}
}