[#2048] Support inline, flush assemblies in prev/next sym comp

This commit is contained in:
SiboVG 2023-02-21 03:15:47 +01:00
parent 09918a3d22
commit 074fee3663
6 changed files with 1069 additions and 456 deletions

View File

@ -1296,6 +1296,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
mutex.verify(); mutex.verify();
return 0; return 0;
} }
public double getRadiusOffset(RadiusMethod method) {
double radius = getRadiusMethod().getRadius(parent, this, getRadiusOffset());
return method.getAsOffset(parent, this, radius);
}
public RadiusMethod getRadiusMethod() { public RadiusMethod getRadiusMethod() {
return RadiusMethod.COAXIAL; return RadiusMethod.COAXIAL;
@ -2059,20 +2064,37 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return all the component assemblies that are a child of this component * Return all the component assemblies that are a direct/indirect child of this component
* @return list of ComponentAssembly components that are a child of this component * @return list of ComponentAssembly components that are a direct/indirect child of this component
*/ */
public final List<RocketComponent> getChildAssemblies() { public final List<ComponentAssembly> getAllChildAssemblies() {
checkState(); checkState();
Iterator<RocketComponent> children = iterator(false); Iterator<RocketComponent> children = iterator(false);
List<RocketComponent> result = new ArrayList<>(); List<ComponentAssembly> result = new ArrayList<>();
while (children.hasNext()) { while (children.hasNext()) {
RocketComponent child = children.next(); RocketComponent child = children.next();
if (child instanceof ComponentAssembly) { if (child instanceof ComponentAssembly) {
result.add(child); result.add((ComponentAssembly) child);
}
}
return result;
}
/**
* Return all the component assemblies that are a direct child of this component
* @return list of ComponentAssembly components that are a direct child of this component
*/
public final List<ComponentAssembly> getDirectChildAssemblies() {
checkState();
List<ComponentAssembly> result = new ArrayList<>();
for (RocketComponent child : this.getChildren()) {
if (child instanceof ComponentAssembly) {
result.add((ComponentAssembly) child);
} }
} }
return result; return result;

View File

@ -7,10 +7,10 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.BoundingBox;
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.rocketcomponent.position.RadiusMethod;
/** /**
* Class for an axially symmetric rocket component generated by rotating * Class for an axially symmetric rocket component generated by rotating
@ -622,7 +622,8 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
while (0 <= searchSiblingIndex) { while (0 <= searchSiblingIndex) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex); final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) { if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
return (SymmetricComponent) searchSibling; return getPreviousSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
(SymmetricComponent) searchSibling, 0);
} }
--searchSiblingIndex; --searchSiblingIndex;
} }
@ -636,14 +637,81 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
} }
} }
// one last thing -- I could be the child of a PodSet, and in line with // one last thing -- I could be the child of a ComponentAssembly, and in line with
// the SymmetricComponent that is my grandParent // the SymmetricComponent that is my grandParent
if ((grandParent instanceof SymmetricComponent) && inline(grandParent)) { if ((grandParent instanceof SymmetricComponent) && inline(grandParent)) {
return (SymmetricComponent) grandParent; // If the grandparent is actually before me, then this is the previous component
if ((parent.getAxialOffset(AxialMethod.TOP) + getAxialOffset(AxialMethod.TOP)) > 0) {
return (SymmetricComponent) grandParent;
}
// If not, then search for the component before the grandparent
else {
// NOTE: will be incorrect if the ComponentAssembly is even further to the front than the
// previous component of the grandparent. But that would be really bad rocket design...
return ((SymmetricComponent) grandParent).getPreviousSymmetricComponent();
}
} }
return null; return null;
} }
/**
* Checks if parent has component assemblies that have potential previous components.
* A child symmetric component of a component assembly is a potential previous component if:
* - it is inline with the parent
* - it is flush with the end of the parent
* - it is larger in aft radius than the parent
* @param parent parent component to check for child component assemblies
* @param previous the current previous component candidate
* @param flushOffset an extra offset to be added to check for flushness. This is used when recursively running this
* method to check children of children of the original parent are flush with the end of the
* original parent.
* @return the previous component if it is found
*/
private SymmetricComponent getPreviousSymmetricComponentFromComponentAssembly(SymmetricComponent parent,
SymmetricComponent previous, double flushOffset) {
if (previous == null) {
return parent;
}
if (parent == null) {
return previous;
}
double maxRadius = previous.isAftRadiusAutomatic() ? 0 : previous.getAftRadius();
SymmetricComponent previousComponent = previous;
for (ComponentAssembly assembly : parent.getDirectChildAssemblies()) {
if (assembly.getChildCount() == 0) {
continue;
}
/*
* Check if the component assembly's last child is a symmetric component that is:
* - inline with the parent
* - flush with the end of the parent
* - larger in aft radius than the parent
* in that case, this component assembly is the new previousComponent.
*/
RocketComponent lastChild = assembly.getChild(assembly.getChildCount() - 1);
if (!( (lastChild instanceof SymmetricComponent) && parent.inline(lastChild) )) {
continue;
}
SymmetricComponent lastSymmetricChild = (SymmetricComponent) lastChild;
double flushDeviation = flushOffset + assembly.getAxialOffset(AxialMethod.BOTTOM); // How much the last child is flush with the parent
// If the last symmetric child from the assembly if flush with the end of the parent and larger than the
// current previous component, then this is the new previous component
if (MathUtil.equals(flushDeviation, 0) && !lastSymmetricChild.isAftRadiusAutomatic() &&
lastSymmetricChild.getAftRadius() > maxRadius) {
previousComponent = lastSymmetricChild;
maxRadius = previousComponent.getAftRadius();
}
// It could be that there is a child component assembly that is flush with the end of the parent or larger
// Recursively check assembly's children
previousComponent = getPreviousSymmetricComponentFromComponentAssembly(lastSymmetricChild, previousComponent, flushDeviation);
maxRadius = previousComponent != null ? previousComponent.getAftRadius() : maxRadius;
}
return previousComponent;
}
/** /**
* Return the next symmetric component, or null if none exists. * Return the next symmetric component, or null if none exists.
@ -670,7 +738,8 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
while (searchSiblingIndex < searchParent.getChildCount()) { while (searchSiblingIndex < searchParent.getChildCount()) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex); final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) { if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
return (SymmetricComponent) searchSibling; return getNextSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
(SymmetricComponent) searchSibling, 0);
} }
++searchSiblingIndex; ++searchSiblingIndex;
} }
@ -681,13 +750,22 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
searchSiblingIndex = 0; searchSiblingIndex = 0;
} }
// One last thing -- I could have a child that is a PodSet that is in line // One last thing -- I could have a child that is a ComponentAssembly that is in line
// with me // with me
for (RocketComponent child : getChildren()) { for (RocketComponent child : getChildren()) {
if (child instanceof PodSet) { if (child instanceof ComponentAssembly) {
for (RocketComponent grandchild : child.getChildren()) { for (RocketComponent grandchild : child.getChildren()) {
if ((grandchild instanceof SymmetricComponent) && inline(grandchild)) { if ((grandchild instanceof SymmetricComponent) && inline(grandchild)) {
return (SymmetricComponent) grandchild; // If the grandparent is actually after me, then this is the next component
if ((parent.getAxialOffset(AxialMethod.BOTTOM) + getAxialOffset(AxialMethod.BOTTOM)) < 0) {
return (SymmetricComponent) grandchild;
}
// If not, then search for the component after the grandparent
else {
// NOTE: will be incorrect if the ComponentAssembly is even further to the back than the
// next component of the grandparent. But that would be really bad rocket design...
return ((SymmetricComponent) grandchild).getNextSymmetricComponent();
}
} }
} }
} }
@ -696,6 +774,64 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
return null; return null;
} }
/**
* Checks if parent has component assemblies that have potential next components.
* A child symmetric component of a component assembly is a potential next component if:
* - it is inline with the parent
* - it is flush with the front of the parent
* - it is larger in fore radius than the parent
* @param parent parent component to check for child component assemblies
* @param next the next previous component candidate
* @param flushOffset an extra offset to be added to check for flushness. This is used when recursively running this
* method to check children of children of the original parent are flush with the front of the
* original parent.
* @return the next component if it is found
*/
private SymmetricComponent getNextSymmetricComponentFromComponentAssembly(SymmetricComponent parent,
SymmetricComponent next, double flushOffset) {
if (next == null) {
return parent;
}
if (parent == null) {
return next;
}
double maxRadius = next.isForeRadiusAutomatic() ? 0 : next.getForeRadius();
SymmetricComponent nextComponent = next;
for (ComponentAssembly assembly : parent.getDirectChildAssemblies()) {
if (assembly.getChildCount() == 0) {
continue;
}
/*
* Check if the component assembly's last child is a symmetric component that is:
* - inline with the parent
* - flush with the front of the parent
* - larger in fore radius than the parent
* in that case, this component assembly is the new nextComponent.
*/
RocketComponent firstChild = assembly.getChild(0);
if (!( (firstChild instanceof SymmetricComponent) && parent.inline(firstChild) )) {
continue;
}
SymmetricComponent firstSymmetricChild = (SymmetricComponent) firstChild;
double flushDeviation = flushOffset + assembly.getAxialOffset(AxialMethod.TOP); // How much the last child is flush with the parent
// If the first symmetric child from the assembly if flush with the front of the parent and larger than the
// current next component, then this is the new next component
if (MathUtil.equals(flushDeviation, 0) && !firstSymmetricChild.isForeRadiusAutomatic() &&
firstSymmetricChild.getForeRadius() > maxRadius) {
nextComponent = firstSymmetricChild;
maxRadius = nextComponent.getForeRadius();
}
// It could be that there is a child component assembly that is flush with the front of the parent or larger
// Recursively check assembly's children
nextComponent = getNextSymmetricComponentFromComponentAssembly(firstSymmetricChild, nextComponent, flushDeviation);
maxRadius = nextComponent != null ? nextComponent.getForeRadius() : maxRadius;
}
return nextComponent;
}
/*** /***
* Determine whether a candidate symmetric component is in line with us * Determine whether a candidate symmetric component is in line with us
* *

View File

@ -469,7 +469,6 @@ public class BarrowmanCalculatorTest {
final FlightConfiguration testConfig = testRocket.getSelectedConfiguration(); final FlightConfiguration testConfig = testRocket.getSelectedConfiguration();
final FlightConditions testConditions = new FlightConditions(testConfig); final FlightConditions testConditions = new FlightConditions(testConfig);
TestRockets.dumpRocket(testRocket, "/home/joseph/rockets/openrocket/git/openrocket/work/testrocket.ork");
final BarrowmanCalculator testCalc = new BarrowmanCalculator(); final BarrowmanCalculator testCalc = new BarrowmanCalculator();
double testCP = testCalc.getCP(testConfig, testConditions, warnings).x; double testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
final AerodynamicForces testForces = testCalc.getAerodynamicForces(testConfig, testConditions, warnings); final AerodynamicForces testForces = testCalc.getAerodynamicForces(testConfig, testConditions, warnings);
@ -487,8 +486,15 @@ public class BarrowmanCalculatorTest {
// move the pod forward. // move the pod forward.
warnings.clear(); warnings.clear();
pod.setAxialOffset(pod.getAxialOffset() - 0.2); pod.setAxialOffset(pod.getAxialOffset() - 0.3);
testCP = testCalc.getCP(testConfig, testConditions, warnings).x; testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
assertFalse("should be warning from airframe overlap", warnings.isEmpty());
// move the pod back.
warnings.clear();
pod.setAxialOffset(pod.getAxialOffset() + 0.1);
TestRockets.dumpRocket(testRocket, "/Users/SiboVanGool/Downloads/sfs/test.ork");
testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
assertFalse("should be warning from airframe overlap", warnings.isEmpty()); assertFalse("should be warning from airframe overlap", warnings.isEmpty());
} }

View File

@ -0,0 +1,511 @@
package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import net.sf.openrocket.util.TestRockets;
import org.junit.Test;
public class SymmetricComponentTest extends BaseTestCase {
@Test
public void testPreviousSymmetricComponent() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
assertNull(payloadFairingNoseCone.getPreviousSymmetricComponent());
assertEquals(payloadFairingNoseCone, payloadBody.getPreviousSymmetricComponent());
assertEquals(payloadBody, payloadFairingTail.getPreviousSymmetricComponent());
assertEquals(payloadFairingTail, upperStageBody.getPreviousSymmetricComponent());
assertEquals(upperStageBody, interstageBody.getPreviousSymmetricComponent());
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
assertNull(boosterCone.getPreviousSymmetricComponent());
assertEquals(boosterCone, boosterBody.getPreviousSymmetricComponent());
}
@Test
public void testPreviousSymmetricComponentInlineComponentAssembly() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
// Stage 0
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
// Stage 1
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
// Booster stage
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
boosterStage.setInstanceCount(1);
boosterStage.setRadius(RadiusMethod.RELATIVE, 0);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
// Add inline pod set
PodSet podSet = new PodSet();
podSet.setName("Inline Pod Set");
podSet.setInstanceCount(1);
podSet.setRadius(RadiusMethod.FREE, 0);
coreBody.addChild(podSet);
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
NoseCone podSetCone = new NoseCone();
podSetCone.setLength(0.1);
podSetCone.setBaseRadius(0.05);
podSet.addChild(podSetCone);
BodyTube podSetBody = new BodyTube(0.2, 0.05, 0.001);
podSetBody.setName("Pod Set Body");
podSet.addChild(podSetBody);
TrapezoidFinSet finSet = new TrapezoidFinSet();
podSetBody.addChild(finSet);
// Add last stage
AxialStage lastStage = new AxialStage();
BodyTube lastStageBody = new BodyTube(0.2, 0.05, 0.001);
lastStageBody.setName("Last Stage Body");
lastStage.addChild(lastStageBody);
rocket.addChild(lastStage);
assertNull(payloadFairingNoseCone.getPreviousSymmetricComponent());
assertEquals(payloadFairingNoseCone, payloadBody.getPreviousSymmetricComponent());
assertEquals(payloadBody, payloadFairingTail.getPreviousSymmetricComponent());
assertEquals(payloadFairingTail, upperStageBody.getPreviousSymmetricComponent());
assertEquals(upperStageBody, interstageBody.getPreviousSymmetricComponent());
assertNull(boosterCone.getPreviousSymmetricComponent());
assertEquals(boosterCone, boosterBody.getPreviousSymmetricComponent());
// case 1: pod set is larger, and at the back of the core stage
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 2: pod set is smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 3: pod set is equal, and at the back of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 4: pod set is larger, and at the front of the core stage
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 5: pod set is smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 6: pod set is equal, and at the front of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 7: pod set is same length as core stage, and larger, and at the front of the core stage
podSetBody.setOuterRadius(0.05);
podSetBody.setLength(0.7);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 8: pod set is same length as core stage, and smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 9: pod set is in larger, and in the middle of the core stage
podSetBody.setLength(0.2);
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.MIDDLE, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 10: pod set is in larger, and behind the back of the core stage
podSet.setAxialOffset(AxialMethod.BOTTOM, 1);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// Add a booster inside the pod set
ParallelStage insideBooster = new ParallelStage();
insideBooster.setName("Inside Booster");
insideBooster.setInstanceCount(1);
insideBooster.setRadius(RadiusMethod.FREE, 0);
podSetBody.addChild(insideBooster);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
BodyTube insideBoosterBody = new BodyTube(0.2, 0.06, 0.001);
insideBoosterBody.setName("Inside Booster Body");
insideBooster.addChild(insideBoosterBody);
// Case 1: inside booster is larger than pod set and flush to its end (both are at the back of the core stage)
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 2: inside booster is smaller than pod set and flush to its end
insideBoosterBody.setOuterRadius(0.04);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 3: inside booster is equal the pod set and flush to its end
insideBoosterBody.setOuterRadius(0.05);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 4: inside booster is larger than pod set and before the back (pod set at the back of the core stage)
insideBooster.setAxialOffset(AxialMethod.BOTTOM, -1);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 5: inside booster is larger than pod set and after the back (pod set at the back of the core stage)
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 1);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetBody, insideBoosterBody.getPreviousSymmetricComponent());
// Case 6: inside booster is larger than pod set, pod set is before the back of the core stage, inside booster is an equal amount after the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, -1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 1.5);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetBody, insideBoosterBody.getPreviousSymmetricComponent());
// Case 7: inside booster is larger than pod set, pod set is after the back of the core stage, inside booster is an equal amount before the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, 1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, -1.5);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 8: inside booster is larger than pod set, pod set is before the back of the core stage, inside booster is flush with the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, -1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 9: inside booster is larger than pod set, pod set is after the back of the core stage, inside booster is flush with the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, 1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
}
@Test
public void testNextSymmetricComponent() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
assertEquals(payloadBody, payloadFairingNoseCone.getNextSymmetricComponent());
assertEquals(payloadFairingTail, payloadBody.getNextSymmetricComponent());
assertEquals(upperStageBody, payloadFairingTail.getNextSymmetricComponent());
assertEquals(interstageBody, upperStageBody.getNextSymmetricComponent());
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertNull(coreBody.getNextSymmetricComponent());
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
assertEquals(boosterBody, boosterCone.getNextSymmetricComponent());
assertNull(boosterBody.getNextSymmetricComponent());
}
@Test
public void testNextSymmetricComponentInlineComponentAssembly() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
// Stage 0
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
// Stage 1
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
// Booster stage
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
boosterStage.setInstanceCount(1);
boosterStage.setRadius(RadiusMethod.RELATIVE, 0);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
// Add inline pod set
PodSet podSet = new PodSet();
podSet.setName("Inline Pod Set");
podSet.setInstanceCount(1);
podSet.setRadius(RadiusMethod.FREE, 0);
coreBody.addChild(podSet);
podSet.setAxialOffset(AxialMethod.TOP, 0);
BodyTube podSetBody = new BodyTube(0.2, 0.05, 0.001);
podSetBody.setName("Pod Set Body");
podSet.addChild(podSetBody);
TrapezoidFinSet finSet = new TrapezoidFinSet();
podSetBody.addChild(finSet);
NoseCone podSetCone = new NoseCone();
podSetCone.setLength(0.1);
podSetCone.setBaseRadius(0.05);
podSetCone.setFlipped(true);
podSet.addChild(podSetCone);
// Add last stage
AxialStage lastStage = new AxialStage();
BodyTube lastStageBody = new BodyTube(0.2, 0.05, 0.001);
lastStageBody.setName("Last Stage Body");
lastStage.addChild(lastStageBody);
rocket.addChild(lastStage);
assertEquals(payloadBody, payloadFairingNoseCone.getNextSymmetricComponent());
assertEquals(payloadFairingTail, payloadBody.getNextSymmetricComponent());
assertEquals(upperStageBody, payloadFairingTail.getNextSymmetricComponent());
assertEquals(interstageBody, upperStageBody.getNextSymmetricComponent());
assertNull(lastStageBody.getNextSymmetricComponent());
assertEquals(boosterBody, boosterCone.getNextSymmetricComponent());
// case 1: pod set is larger, and at the front of the core stage
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 2: pod set is smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 3: pod set is equal, and at the front of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 4: pod set is larger, and at the back of the core stage
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 5: pod set is smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 6: pod set is equal, and at the back of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 7: pod set is same length as core stage, and larger, and at the back of the core stage
podSetBody.setOuterRadius(0.05);
podSetBody.setLength(0.7);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 8: pod set is same length as core stage, and smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 9: pod set is in larger, and in the middle of the core stage
podSetBody.setLength(0.2);
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.MIDDLE, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 10: pod set is in larger, and behind the front of the core stage
podSet.setAxialOffset(AxialMethod.TOP, 1);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// Add a booster inside the pod set
ParallelStage insideBooster = new ParallelStage();
insideBooster.setName("Inside Booster");
insideBooster.setInstanceCount(1);
insideBooster.setRadius(RadiusMethod.FREE, 0);
podSetBody.addChild(insideBooster);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
BodyTube insideBoosterBody = new BodyTube(0.2, 0.06, 0.001);
insideBoosterBody.setName("Inside Booster Body");
insideBooster.addChild(insideBoosterBody);
// Case 1: inside booster is larger than pod set and flush to its front (both are at the front of the core stage)
podSet.setAxialOffset(AxialMethod.TOP, 0);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 2: inside booster is smaller than pod set and flush to its front
insideBoosterBody.setOuterRadius(0.04);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 3: inside booster is equal the pod set and flush to its front
insideBoosterBody.setOuterRadius(0.05);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 4: inside booster is larger than pod set and before the front (pod set at the front of the core stage)
insideBooster.setAxialOffset(AxialMethod.TOP, -1);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 5: inside booster is larger than pod set and after the front (pod set at the front of the core stage)
insideBooster.setAxialOffset(AxialMethod.TOP, 1);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 6: inside booster is larger than pod set, pod set is before the front of the core stage, inside booster is an equal amount after the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, -1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 1.5);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 7: inside booster is larger than pod set, pod set is after the front of the core stage, inside booster is an equal amount before the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, 1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, -1.5);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 8: inside booster is larger than pod set, pod set is before the front of the core stage, inside booster is flush with the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, -1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 9: inside booster is larger than pod set, pod set is after the front of the core stage, inside booster is flush with the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, 1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
}
}

View File

@ -8,443 +8,380 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test; import org.junit.Test;
public class SymmetricComponentVolumeTest extends BaseTestCase { public class SymmetricComponentVolumeTest extends BaseTestCase {
@Test @Test
public void simpleConeFilled() { public void testVolumeSimpleConeFilled() {
NoseCone nc = new NoseCone(); NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001; final double epsilonPercent = 0.001;
final double density = 2.0; final double density = 2.0;
nc.setLength(1.0); nc.setLength(1.0);
nc.setFilled(true); nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL); nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0); nc.setAftRadius(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true)); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG(); Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0;
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); double mass = density * volume;
//System.out.println(cg);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
double volume = Math.PI / 3.0; assertEquals(mass, nc.getMass(), epsilonPercent * mass);
double mass = density * volume; assertEquals(0.75, cg.x, epsilonPercent * 0.75);
assertEquals(mass, cg.weight, epsilonPercent * mass);
//System.out.println(volume); }
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); @Test
assertEquals(mass, nc.getMass(), epsilonPercent * mass); public void testVolumeSimpleConeWithShoulderFilled() {
NoseCone nc = new NoseCone();
assertEquals(0.75, cg.x, epsilonPercent * 0.75);
assertEquals(mass, cg.weight, epsilonPercent * mass); final double epsilonPercent = 0.001;
} final double density = 2.0;
@Test nc.setLength(1.0);
public void simpleConeWithShoulderFilled() { nc.setFilled(true);
NoseCone nc = new NoseCone(); nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
final double epsilonPercent = 0.001; nc.setAftShoulderRadius(1.0);
final double density = 2.0; nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(1.0);
nc.setLength(1.0); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL); Coordinate cg = nc.getCG();
nc.setAftRadius(1.0);
nc.setAftShoulderRadius(1.0); double volume = Math.PI / 3.0;
nc.setAftShoulderLength(1.0); volume += Math.PI;
nc.setAftShoulderThickness(1.0); double mass = density * volume;
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
Coordinate cg = nc.getCG(); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); assertEquals(1.312, cg.x, epsilonPercent * 1.071);
//System.out.println(cg); assertEquals(mass, cg.weight, epsilonPercent * mass);
}
double volume = Math.PI / 3.0;
volume += Math.PI; @Test
public void testVolumeSimpleConeHollow() {
double mass = density * volume; NoseCone nc = new NoseCone();
//System.out.println(volume + "\t" + mass); final double epsilonPercent = 0.001;
final double density = 2.0;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass); nc.setLength(1.0);
nc.setAftRadius(1.0);
assertEquals(1.312, cg.x, epsilonPercent * 1.071); nc.setThickness(0.5);
assertEquals(mass, cg.weight, epsilonPercent * mass); nc.setType(Transition.Shape.CONICAL);
} nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
@Test Coordinate cg = nc.getCG();
public void simpleConeHollow() {
NoseCone nc = new NoseCone(); double volume = Math.PI / 3.0; // outer volume
final double epsilonPercent = 0.001; // manually projected Thickness of 0.5 on to radius to determine
final double density = 2.0; // the innerConeDimen. Since the outer cone is "square" (height = radius),
// we only need to compute this one dimension in order to compute the
nc.setLength(1.0); // volume of the inner cone.
nc.setAftRadius(1.0); double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
nc.setThickness(0.5); double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
nc.setType(Transition.Shape.CONICAL); volume -= innerVolume;
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
double mass = density * volume;
Coordinate cg = nc.getCG();
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
//System.out.println(cg);
assertEquals(0.7454, cg.x, epsilonPercent * 0.7454);
double volume = Math.PI / 3.0; // outer volume assertEquals(mass, cg.weight, epsilonPercent * mass);
}
// manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius), @Test
// we only need to compute this one dimension in order to compute the public void testVolumeSimpleConeWithShoulderHollow() {
// volume of the inner cone. NoseCone nc = new NoseCone();
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen; final double epsilonPercent = 0.001;
volume -= innerVolume; final double density = 2.0;
double mass = density * volume; nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
//System.out.println(volume); nc.setAftRadius(1.0);
nc.setThickness(0.5);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); nc.setAftShoulderRadius(1.0);
assertEquals(mass, nc.getMass(), epsilonPercent * mass); nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(0.5);
assertEquals(0.7454, cg.x, epsilonPercent * 0.7454); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
assertEquals(mass, cg.weight, epsilonPercent * mass);
} Coordinate cg = nc.getCG();
@Test double volume = Math.PI / 3.0; // outer volume
public void simpleConeWithShoulderHollow() {
NoseCone nc = new NoseCone(); // manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius),
final double epsilonPercent = 0.001; // we only need to compute this one dimension in order to compute the
final double density = 2.0; // volume of the inner cone.
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
nc.setLength(1.0); double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
nc.setType(Transition.Shape.CONICAL); volume -= innerVolume;
nc.setAftRadius(1.0); volume += Math.PI - Math.PI * 0.5 * 0.5;
nc.setThickness(0.5); double mass = density * volume;
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderLength(1.0); assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
nc.setAftShoulderThickness(0.5); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
assertEquals(1.2719, cg.x, epsilonPercent * 1.2719);
Coordinate cg = nc.getCG(); assertEquals(mass, cg.weight, epsilonPercent * mass);
}
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg); @Test
public void testVolumeSimpleTransitionFilled() {
double volume = Math.PI / 3.0; // outer volume Transition nc = new Transition();
// manually projected Thickness of 0.5 on to radius to determine final double epsilonPercent = 0.001;
// the innerConeDimen. Since the outer cone is "square" (height = radius), final double density = 2.0;
// we only need to compute this one dimension in order to compute the
// volume of the inner cone. nc.setLength(4.0);
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0; nc.setFilled(true);
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen; nc.setType(Transition.Shape.CONICAL);
volume -= innerVolume; nc.setForeRadius(1.0);
nc.setAftRadius(2.0);
volume += Math.PI - Math.PI * 0.5 * 0.5; nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double mass = density * volume; double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.4285, cg.x, epsilonPercent * 2.4285);
assertEquals(1.2719, cg.x, epsilonPercent * 1.2719); assertEquals(mass, cg.weight, epsilonPercent * mass);
assertEquals(mass, cg.weight, epsilonPercent * mass); }
}
@Test
@Test public void testVolumeSimpleTransitionWithShouldersFilled() {
public void simpleTransitionFilled() { Transition nc = new Transition();
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double epsilonPercent = 0.001; final double density = 2.0;
final double density = 2.0;
nc.setLength(4.0);
nc.setLength(4.0); nc.setFilled(true);
nc.setFilled(true); nc.setType(Transition.Shape.CONICAL);
nc.setType(Transition.Shape.CONICAL); nc.setForeRadius(1.0);
nc.setForeRadius(1.0); nc.setAftRadius(2.0);
nc.setAftRadius(2.0); nc.setAftShoulderLength(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true)); nc.setAftShoulderRadius(2.0);
nc.setAftShoulderThickness(2.0);
Coordinate cg = nc.getCG(); nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(1.0);
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); nc.setForeShoulderThickness(1.0);
//System.out.println(cg); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0; Coordinate cg = nc.getCG();
double mass = density * volume; double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
// plus aft shoulder:
//System.out.println(volume); volume += Math.PI * 1.0 * 2.0 * 2.0;
// plus fore shoulder:
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); volume += Math.PI * 1.0 * 1.0 * 1.0;
assertEquals(mass, nc.getMass(), epsilonPercent * mass); double mass = density * volume;
assertEquals(2.4285, cg.x, epsilonPercent * 2.4285); assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, cg.weight, epsilonPercent * mass); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
}
assertEquals(2.8023, cg.x, epsilonPercent * 2.8023);
@Test assertEquals(mass, cg.weight, epsilonPercent * mass);
public void simpleTransitionWithShouldersFilled() { }
Transition nc = new Transition();
@Test
final double epsilonPercent = 0.001; public void testVolumeSimpleTransitionHollow1() {
final double density = 2.0; Transition nc = new Transition();
nc.setLength(4.0); final double epsilonPercent = 0.001;
nc.setFilled(true); final double density = 2.0;
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(1.0); nc.setLength(1.0);
nc.setAftRadius(2.0); nc.setType(Transition.Shape.CONICAL);
nc.setAftShoulderLength(1.0); nc.setForeRadius(0.5);
nc.setAftShoulderRadius(2.0); nc.setAftRadius(1.0);
nc.setAftShoulderThickness(2.0); nc.setThickness(0.5);
nc.setForeShoulderLength(1.0); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
nc.setForeShoulderRadius(1.0);
nc.setForeShoulderThickness(1.0); Coordinate cg = nc.getCG();
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
// Volume of filled transition =
Coordinate cg = nc.getCG(); double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); // magic 2D cad drawing...
//System.out.println(cg); //
// Since the thickness >= fore radius, the
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0; // hollowed out portion of the transition
// plus aft shoulder: // forms a cone.
volume += Math.PI * 1.0 * 2.0 * 2.0; // the dimensions of this cone were determined
// plus fore shoulder: // using a 2d cad tool.
volume += Math.PI * 1.0 * 1.0 * 1.0;
double innerConeRadius = 0.441;
double mass = density * volume; double innerConeLength = 0.882;
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
//System.out.println(volume); double volume = filledVolume - innerVolume;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass); assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.8023, cg.x, epsilonPercent * 2.8023);
assertEquals(mass, cg.weight, epsilonPercent * mass); assertEquals(0.5884, cg.x, epsilonPercent * 0.5884);
} assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionHollow1() { @Test
Transition nc = new Transition(); public void testVolumeSimpleTransitionWithShouldersHollow1() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0; final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL); nc.setLength(1.0);
nc.setForeRadius(0.5); nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0); nc.setForeRadius(0.5);
nc.setThickness(0.5); nc.setAftRadius(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true)); nc.setThickness(0.5);
nc.setAftShoulderLength(1.0);
Coordinate cg = nc.getCG(); nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.5);
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass()); nc.setForeShoulderLength(1.0);
//System.out.println(cg); nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled.
// Volume of filled transition = nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
Coordinate cg = nc.getCG();
// magic 2D cad drawing...
// // Volume of filled transition =
// Since the thickness >= fore radius, the double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// hollowed out portion of the transition
// forms a cone. // magic 2D cad drawing...
// the dimensions of this cone were determined //
// using a 2d cad tool. // Since the thickness >= fore radius, the
// hollowed out portion of the transition
double innerConeRadius = 0.441; // forms a cone.
double innerConeLength = 0.882; // the dimensions of this cone were determined
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius; // using a 2d cad tool.
double volume = filledVolume - innerVolume; double innerConeRadius = 0.441;
double innerConeLength = 0.882;
double mass = density * volume; double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
//System.out.println(volume); double volume = filledVolume - innerVolume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); // Now add aft shoulder
assertEquals(mass, nc.getMass(), epsilonPercent * mass); volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5;
// Now add fore shoulder
assertEquals(0.5884, cg.x, epsilonPercent * 0.5884); volume += Math.PI * 1.0 * 0.5 * 0.5;
assertEquals(mass, cg.weight, epsilonPercent * mass);
} double mass = density * volume;
@Test assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
public void simpleTransitionWithShouldersHollow1() { assertEquals(mass, nc.getMass(), epsilonPercent * mass);
Transition nc = new Transition();
assertEquals(0.8581, cg.x, epsilonPercent * 0.8581);
final double epsilonPercent = 0.001; assertEquals(mass, cg.weight, epsilonPercent * mass);
final double density = 2.0; }
nc.setLength(1.0); @Test
nc.setType(Transition.Shape.CONICAL); public void testVolumeSimpleTransitionHollow2() {
nc.setForeRadius(0.5); Transition nc = new Transition();
nc.setAftRadius(1.0);
nc.setThickness(0.5); final double epsilonPercent = 0.001;
nc.setAftShoulderLength(1.0); final double density = 2.0;
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.5); nc.setLength(1.0);
nc.setForeShoulderLength(1.0); nc.setType(Transition.Shape.CONICAL);
nc.setForeShoulderRadius(0.5); nc.setForeRadius(0.5);
nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled. nc.setAftRadius(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true)); nc.setThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg); // Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0; // magic 2D cad drawing...
//
// magic 2D cad drawing... // Since the thickness < fore radius, the
// // hollowed out portion of the transition
// Since the thickness >= fore radius, the // forms a transition.
// hollowed out portion of the transition // the dimensions of this transition were determined
// forms a cone. // using a 2d cad tool.
// the dimensions of this cone were determined
// using a 2d cad tool. double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerConeRadius = 0.441; double innerVolume = Math.PI / 3.0
double innerConeLength = 0.882; * (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
double volume = filledVolume - innerVolume;
double volume = filledVolume - innerVolume; double mass = density * volume;
// Now add aft shoulder assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5; assertEquals(mass, nc.getMass(), epsilonPercent * mass);
// Now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5; assertEquals(0.56827, cg.x, epsilonPercent * 0.56827);
assertEquals(mass, cg.weight, epsilonPercent * mass);
double mass = density * volume; }
//System.out.println(volume); @Test
public void testVolumeSimpleTransitionWithShouldersHollow2() {
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); Transition nc = new Transition();
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
final double epsilonPercent = 0.001;
assertEquals(0.8581, cg.x, epsilonPercent * 0.8581); final double density = 2.0;
assertEquals(mass, cg.weight, epsilonPercent * mass);
} nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
@Test nc.setForeRadius(0.5);
public void simpleTransitionHollow2() { nc.setAftRadius(1.0);
Transition nc = new Transition(); nc.setThickness(0.25);
nc.setAftShoulderLength(1.0);
final double epsilonPercent = 0.001; nc.setAftShoulderRadius(1.0);
final double density = 2.0; nc.setAftShoulderThickness(0.25);
nc.setForeShoulderLength(1.0);
nc.setLength(1.0); nc.setForeShoulderRadius(0.5);
nc.setType(Transition.Shape.CONICAL); nc.setForeShoulderThickness(0.25);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0); nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
nc.setThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true)); Coordinate cg = nc.getCG();
Coordinate cg = nc.getCG(); // Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg); // magic 2D cad drawing...
//
// Volume of filled transition = // Since the thickness < fore radius, the
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0; // hollowed out portion of the transition
// forms a transition.
// magic 2D cad drawing... // the dimensions of this transition were determined
// // using a 2d cad tool.
// Since the thickness < fore radius, the
// hollowed out portion of the transition double innerTransitionAftRadius = 0.7205;
// forms a transition. double innerTransitionForeRadius = 0.2205;
// the dimensions of this transition were determined double innerVolume = Math.PI / 3.0
// using a 2d cad tool. * (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double innerTransitionAftRadius = 0.7205; double volume = filledVolume - innerVolume;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0 // now add aft shoulder
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius); volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.75 * 0.75;
// now add fore shoulder
double volume = filledVolume - innerVolume; volume += Math.PI * 1.0 * 0.5 * 0.5 - Math.PI * 1.0 * 0.25 * 0.25;
double mass = density * volume;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume); assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7829, cg.x, epsilonPercent * 0.7829);
assertEquals(0.56827, cg.x, epsilonPercent * 0.56827); assertEquals(mass, cg.weight, epsilonPercent * mass);
assertEquals(mass, cg.weight, epsilonPercent * mass); }
}
@Test
public void simpleTransitionWithShouldersHollow2() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.25);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.25);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness < fore radius, the
// hollowed out portion of the transition
// forms a transition.
// the dimensions of this transition were determined
// using a 2d cad tool.
double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double volume = filledVolume - innerVolume;
// now add aft shoulder
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.75 * 0.75;
// now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5 - Math.PI * 1.0 * 0.25 * 0.25;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7829, cg.x, epsilonPercent * 0.7829);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
} }

View File

@ -14,6 +14,7 @@ import net.sf.openrocket.gui.widgets.SelectColorToggleButton;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Rocket;
@ -40,7 +41,7 @@ public class StageSelector extends JPanel implements StateChangeListener {
private void updateButtons( final FlightConfiguration configuration ) { private void updateButtons( final FlightConfiguration configuration ) {
buttons.clear(); buttons.clear();
this.removeAll(); this.removeAll();
List<RocketComponent> assemblies = configuration.getRocket().getChildAssemblies(); List<ComponentAssembly> assemblies = configuration.getRocket().getAllChildAssemblies();
for (RocketComponent stage : assemblies) { for (RocketComponent stage : assemblies) {
if (!(stage instanceof AxialStage)) continue; if (!(stage instanceof AxialStage)) continue;