Merge pull request #1684 from SiboVG/issue-1617

[#1617] Mark empty stages as inactive
This commit is contained in:
Sibo Van Gool 2022-09-27 10:44:12 +02:00 committed by GitHub
commit 4a949394c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 201 additions and 21 deletions

View File

@ -64,6 +64,8 @@ RocketPanel.lbl.Stability = Stability:
RocketPanel.checkbox.ShowCGCP = Show CG/CP
RocketPanel.checkbox.ShowCGCP.ttip = Disabling this checkbox hides the CG and CP markings in the rocket view.
RocketPanel.lbl.Stages = Stages:
RocketPanel.btn.Stages.Toggle.ttip = Toggle this button to activate or deactivate the corresponding stage in your design.
RocketPanel.btn.Stages.NoChildren.ttip = <html>This stages does not have any child components and is therefore marked as inactive.<br>Add components to the stage to activate it.</html>
RocketPanel.ttip.Rotation = Change the rocket's roll rotation (only affects the rocket view)
! BasicFrame

View File

@ -1,8 +1,5 @@
package net.sf.openrocket.rocketcomponent;
import java.util.ArrayList;
import java.util.Collection;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.startup.Application;

View File

@ -241,8 +241,10 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if( -1 == stageNumber ) {
return true;
}
return stages.get(stageNumber) != null && stages.get(stageNumber).active;
AxialStage stage = rocket.getStage(stageNumber);
return stage != null && stage.getChildCount() > 0 &&
stages.get(stageNumber) != null && stages.get(stageNumber).active;
}
public Collection<RocketComponent> getAllComponents() {
@ -379,12 +381,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
List<AxialStage> activeStages = new ArrayList<>();
for (StageFlags flags : this.stages.values()) {
if (flags.active) {
AxialStage stage = rocket.getStage(flags.stageNumber);
if (stage == null) {
continue;
}
activeStages.add(stage);
if (isStageActive(flags.stageNumber)) {
activeStages.add( rocket.getStage(flags.stageNumber));
}
}
@ -392,13 +390,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
}
public int getActiveStageCount() {
int activeCount = 0;
for (StageFlags cur : this.stages.values()) {
if (cur.active) {
activeCount++;
}
}
return activeCount;
return getActiveStages().size();
}
/**
@ -407,7 +399,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
public AxialStage getBottomStage() {
AxialStage bottomStage = null;
for (StageFlags curFlags : this.stages.values()) {
if (curFlags.active) {
if (isStageActive(curFlags.stageNumber)) {
bottomStage = rocket.getStage( curFlags.stageNumber);
}
}
@ -851,7 +843,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
buf.append(String.format(fmt, "#", "?actv", "Name"));
for (StageFlags flags : stages.values()) {
final int stageNumber = flags.stageNumber;
buf.append(String.format(fmt, stageNumber, (flags.active?" on": "off"), rocket.getStage( stageNumber).getName()));
buf.append(String.format(fmt, stageNumber, (isStageActive(flags.stageNumber) ?" on": "off"), rocket.getStage( stageNumber).getName()));
}
buf.append("\n");
return buf.toString();

View File

@ -46,6 +46,26 @@ public class BarrowmanCalculatorTest {
// Application.setInjector(injector);
// }
}
/**
* Test a completely empty rocket.
*/
@Test
public void testEmptyRocket() {
// First test completely empty rocket
Rocket rocket = new Rocket();
FlightConfiguration config = rocket.getSelectedConfiguration();
BarrowmanCalculator calc = new BarrowmanCalculator();
FlightConditions conditions = new FlightConditions(config);
WarningSet warnings = new WarningSet();
Coordinate cp_calc = calc.getCP(config, conditions, warnings);
assertEquals(" Empty rocket CNa value is incorrect:", 0.0, cp_calc.weight , 0.0);
assertEquals(" Empty rocket cp x value is incorrect:", 0.0, cp_calc.x , 0.0);
assertEquals(" Empty rocket cp y value is incorrect:", 0.0, cp_calc.y , 0.0);
assertEquals(" Empty rocket cp z value is incorrect:", 0.0, cp_calc.z , 0.0);
}
@Test
public void testCPSimpleDry() {
@ -351,4 +371,67 @@ public class BarrowmanCalculatorTest {
assertEquals(" Alpha III With Pods rocket cp z value is incorrect:", cpNoPods.z, cpPods.z, EPSILON);
assertEquals(" Alpha III With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON);
}
/**
* Tests whether adding extra empty stages has an effect.
*/
@Test
public void testEmptyStages() {
// Reference rocket
Rocket rocketRef = TestRockets.makeEstesAlphaIII();
FlightConfiguration configRef = rocketRef.getSelectedConfiguration();
BarrowmanCalculator calcRef = new BarrowmanCalculator();
FlightConditions conditionsRef = new FlightConditions(configRef);
WarningSet warnings = new WarningSet();
Coordinate cp_calcRef = calcRef.getCP(configRef, conditionsRef, warnings);
// First test with adding an empty stage in the front of the design
Rocket rocketFront = TestRockets.makeEstesAlphaIII();
AxialStage stage1 = new AxialStage(); // To be placed in front of the design
rocketFront.addChild(stage1, 0);
FlightConfiguration configFront = rocketFront.getSelectedConfiguration();
BarrowmanCalculator calcFront = new BarrowmanCalculator();
FlightConditions conditionsFront = new FlightConditions(configFront);
warnings = new WarningSet();
Coordinate cp_calcFront = calcFront.getCP(configFront, conditionsFront, warnings);
assertEquals(" Estes Alpha III with front empty stage CNa value is incorrect:", cp_calcRef.weight, cp_calcFront.weight , EPSILON);
assertEquals(" Estes Alpha III with front empty stage cp x value is incorrect:", cp_calcRef.x, cp_calcFront.x , EPSILON);
assertEquals(" Estes Alpha III with front empty stage cp y value is incorrect:", cp_calcRef.y, cp_calcFront.y , EPSILON);
assertEquals(" Estes Alpha III with front empty stage cp z value is incorrect:", cp_calcRef.z, cp_calcFront.z , EPSILON);
// Now test with adding an empty stage in the rear of the design
Rocket rocketRear = TestRockets.makeEstesAlphaIII();
AxialStage stage2 = new AxialStage(); // To be placed in the rear of the design
rocketRear.addChild(stage2);
FlightConfiguration configRear = rocketRear.getSelectedConfiguration();
BarrowmanCalculator calcRear = new BarrowmanCalculator();
FlightConditions conditionsRear = new FlightConditions(configRear);
warnings = new WarningSet();
Coordinate cp_calcRear = calcRear.getCP(configRear, conditionsRear, warnings);
assertEquals(" Estes Alpha III with rear empty stage CNa value is incorrect:", cp_calcRef.weight, cp_calcRear.weight , EPSILON);
assertEquals(" Estes Alpha III with rear empty stage cp x value is incorrect:", cp_calcRef.x, cp_calcRear.x , EPSILON);
assertEquals(" Estes Alpha III with rear empty stage cp y value is incorrect:", cp_calcRef.y, cp_calcRear.y , EPSILON);
assertEquals(" Estes Alpha III with rear empty stage cp z value is incorrect:", cp_calcRef.z, cp_calcRear.z , EPSILON);
// Test with multiple empty stages
Rocket rocketMulti = rocketFront;
AxialStage stage3 = new AxialStage(); // To be placed in the rear of the design
rocketMulti.addChild(stage3);
FlightConfiguration configMulti = rocketMulti.getSelectedConfiguration();
BarrowmanCalculator calcMulti = new BarrowmanCalculator();
FlightConditions conditionsMulti = new FlightConditions(configMulti);
warnings = new WarningSet();
Coordinate cp_calcMulti = calcMulti.getCP(configMulti, conditionsMulti, warnings);
assertEquals(" Estes Alpha III with multiple empty stages CNa value is incorrect:", cp_calcRef.weight, cp_calcMulti.weight , EPSILON);
assertEquals(" Estes Alpha III with multiple empty stages cp x value is incorrect:", cp_calcRef.x, cp_calcMulti.x , EPSILON);
assertEquals(" Estes Alpha III with multiple empty stages cp y value is incorrect:", cp_calcRef.y, cp_calcMulti.y , EPSILON);
assertEquals(" Estes Alpha III with multiple empty stages cp z value is incorrect:", cp_calcRef.z, cp_calcMulti.z , EPSILON);
}
}

View File

@ -23,6 +23,29 @@ public class MassCalculatorTest extends BaseTestCase {
// tolerance for compared double test results
private static final double EPSILON = 0.00000001; // note: this precision matches MathUtil.java
@Test
public void testEmptyRocket() {
Rocket rocket = new Rocket();
FlightConfiguration config = rocket.getEmptyConfiguration();
final RigidBody actualStructure = MassCalculator.calculateStructure(config);
final double actualRocketDryMass = actualStructure.cm.weight;
final Coordinate actualRocketDryCM = actualStructure.cm;
assertEquals(" Empty Rocket Empty Mass is incorrect: ", 0, actualRocketDryMass, 0);
Coordinate expCM = new Coordinate(0, 0, 0, 0);
assertEquals("Empty Rocket CM.x is incorrect: ", expCM.x, actualRocketDryCM.x, 0);
assertEquals("Empty Rocket CM.y is incorrect: ", expCM.y, actualRocketDryCM.y, 0);
assertEquals("Empty Rocket CM.z is incorrect: ", expCM.z, actualRocketDryCM.z, 0);
assertEquals("Empty Rocket CM is incorrect: ", expCM, actualRocketDryCM);
double actualMOIrot = actualStructure.getRotationalInertia();
double actualMOIlong = actualStructure.getLongitudinalInertia();
assertEquals("Empty Rocket Rotational MOI calculated incorrectly: ", 0, actualMOIrot, 0);
assertEquals("Empty Rocket Longitudinal MOI calculated incorrectly: ", 0, actualMOIlong, 0);
}
@Test
public void testAlphaIIIStructure() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
@ -1144,5 +1167,69 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM());
}
@Test
public void testEmptyStages() {
Rocket rocketRef = TestRockets.makeEstesAlphaIII(); // Reference rocket
FlightConfiguration configRef = rocketRef.getEmptyConfiguration();
configRef.setAllStages();
final RigidBody structureRef = MassCalculator.calculateStructure(configRef);
final double rocketDryMassRef = structureRef.cm.weight;
final Coordinate rocketDryCMRef = structureRef.cm;
Rocket rocket = TestRockets.makeEstesAlphaIII();
AxialStage stage1 = new AxialStage(); // To be added to the front of the rocket
AxialStage stage2 = new AxialStage(); // To be added to the rear of the rocket
rocket.addChild(stage1, 0);
rocket.addChild(stage2);
FlightConfiguration config = rocket.getEmptyConfiguration();
config.setAllStages();
final RigidBody structure = MassCalculator.calculateStructure(config);
final double rocketDryMass = structure.cm.weight;
final Coordinate rocketDryCM = structure.cm;
assertEquals(" Empty Stages Rocket Empty Mass is incorrect: ", rocketDryMassRef, rocketDryMass, EPSILON);
assertEquals("Empty Stages Rocket CM.x is incorrect: ", rocketDryCMRef.x, rocketDryCM.x, EPSILON);
assertEquals("Empty Stages Rocket CM.y is incorrect: ", rocketDryCMRef.y, rocketDryCM.y, EPSILON);
assertEquals("Empty Stages Rocket CM.z is incorrect: ", rocketDryCMRef.z, rocketDryCM.z, EPSILON);
assertEquals("Empty Stages Rocket CM is incorrect: ", rocketDryCMRef, rocketDryCM);
double MOIrotRef = structureRef.getRotationalInertia();
double MOIlongRef = structureRef.getLongitudinalInertia();
double MOIrot = structure.getRotationalInertia();
double MOIlong = structure.getLongitudinalInertia();
assertEquals("Empty Stages Rocket Rotational MOI calculated incorrectly: ", MOIrotRef, MOIrot, EPSILON);
assertEquals("Empty Stages Rocket Longitudinal MOI calculated incorrectly: ", MOIlongRef, MOIlong, EPSILON);
// if we use a mass override, setting to same mass, we should get same result
AxialStage sustainerRef = (AxialStage) rocketRef.getChild(0);
sustainerRef.setSubcomponentsOverridden(true);
sustainerRef.setMassOverridden(true);
sustainerRef.setOverrideMass(rocketDryMassRef);
AxialStage sustainer = (AxialStage) rocket.getChild(0);
sustainer.setSubcomponentsOverridden(true);
sustainer.setMassOverridden(true);
sustainer.setOverrideMass(rocketDryMass);
final RigidBody overrideStructureRef = MassCalculator.calculateStructure(configRef);
final Coordinate overrideRocketDryCMRef = overrideStructureRef.cm;
final RigidBody overrideStructure = MassCalculator.calculateStructure(config);
final Coordinate overrideRocketDryCM = overrideStructure.cm;
assertEquals("Empty Stages Rocket Override CM is incorrect: ", overrideRocketDryCMRef, overrideRocketDryCM);
double overrideMOIrotRef = overrideStructureRef.getRotationalInertia();
double overrideMOIlongRef = overrideStructureRef.getLongitudinalInertia();
double overrideMOIrot = overrideStructure.getRotationalInertia();
double overrideMOIlong = overrideStructure.getLongitudinalInertia();
assertEquals("Empty Stages Rocket Rotational MOI calculated incorrectly: ", overrideMOIrotRef, overrideMOIrot, EPSILON);
assertEquals("Empty Stages Rocket Longitudinal MOI calculated incorrectly: ", overrideMOIlongRef, overrideMOIlong, EPSILON);
}
}

View File

@ -11,18 +11,21 @@ import javax.swing.JToggleButton;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.widgets.SelectColorToggleButton;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.StateChangeListener;
@SuppressWarnings("serial")
public class StageSelector extends JPanel implements StateChangeListener {
private static final Translator trans = Application.getTranslator();
private final Rocket rocket;
private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
@ -61,9 +64,16 @@ public class StageSelector extends JPanel implements StateChangeListener {
private class StageAction extends AbstractAction {
private final AxialStage stage;
public StageAction(final AxialStage stage) {
this.stage = stage;
if (this.stage.getChildCount() == 0) {
putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.NoChildren.ttip"));
setEnabled(false);
} else {
putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.Toggle.ttip"));
}
updateUI();
}
@Override
@ -77,6 +87,15 @@ public class StageSelector extends JPanel implements StateChangeListener {
@Override
public void actionPerformed(ActionEvent e) {
// Don't toggle the state if the stage has no children (and is therefore inactive)
if (stage.getChildCount() == 0) {
putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.NoChildren.ttip"));
setEnabled(false);
return;
} else {
setEnabled(true);
putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.Toggle.ttip"));
}
rocket.getSelectedConfiguration().toggleStage(stage.getStageNumber());
rocket.fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE | ComponentChangeEvent.MOTOR_CHANGE );
}