diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index 137fdf3ea..fdb333378 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -228,6 +228,10 @@ edtmotorconfdlg.tbl.Separationheader = Separation
RenameConfigDialog.title = Rename Configuration
RenameConfigDialog.lbl.name = Name for flight configuration:
RenameConfigDialog.but.reset = Reset to default
+RenameConfigDialog.lbl.infoMotors = The text '{motors}' will be replaced with the motor designation(s).
\te.g. '{motors} \u2192 'M1350-0'
+RenameConfigDialog.lbl.infoManufacturers = The text '{manufacturers}' will be replaced with the motor manufacturer(s).
\te.g. '{manufacturers}' \u2192 'AeroTech'
+RenameConfigDialog.lbl.infoCombination = A combination of the above can be used.
\te.g. '{manufacturers motors}' \u2192 'AeroTech M1350-0'
+
! Example design dialog
exdesigndlg.but.open = Open
@@ -2266,6 +2270,7 @@ SeparationSelectionDialog.opt.override = Override for the {0} flight configurati
MotorConfigurationPanel.description = Select the motors and motor ignition events of the selected flight configuration.
Motor mounts: Select which components function as motor mounts.
Motor configurations: Select the motor and ignition event for each motor mount.
MotorDescriptionSubstitutor.description = Motors in the configuration
+MotorManufacturerSubstitutor.description = Motor manufacturers in the configuration
!Photo Panel
diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java
index bc75f02a6..63ede7ad9 100644
--- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java
+++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java
@@ -380,6 +380,20 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
}
}
+ } else {
+ /*
+ It could be that the component is a child of a PodSet or ParallelStage, and it is flush with
+ the previous component. In this case, the component is overlapping.
+ */
+ RocketComponent prevCompParent = prevComp.getParent();
+ RocketComponent compParent = comp.getParent();
+ int prevCompPos = prevCompParent.getChildPosition(prevComp);
+ RocketComponent nextComp = prevCompPos + 1 >= prevCompParent.getChildCount() ?
+ null : prevCompParent.getChild(prevCompPos + 1);
+ if ((compParent instanceof PodSet || compParent instanceof ParallelStage) &&
+ MathUtil.equals(symXfore, prevXaft) && (compParent.getParent() == nextComp)) {
+ warnings.add(Warning.PODSET_OVERLAP, comp.getParent().toString());
+ }
}
}
prevComp = sym;
diff --git a/core/src/net/sf/openrocket/formatting/MotorConfigurationSubstitutor.java b/core/src/net/sf/openrocket/formatting/MotorConfigurationSubstitutor.java
new file mode 100644
index 000000000..9c534d079
--- /dev/null
+++ b/core/src/net/sf/openrocket/formatting/MotorConfigurationSubstitutor.java
@@ -0,0 +1,250 @@
+package net.sf.openrocket.formatting;
+
+import com.google.inject.Inject;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.motor.MotorConfiguration;
+import net.sf.openrocket.motor.ThrustCurveMotor;
+import net.sf.openrocket.plugin.Plugin;
+import net.sf.openrocket.rocketcomponent.AxialStage;
+import net.sf.openrocket.rocketcomponent.FlightConfiguration;
+import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
+import net.sf.openrocket.rocketcomponent.MotorMount;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.ArrayList;
+import net.sf.openrocket.util.Chars;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * General substitutor for motor configurations. This currently includes substitutions for
+ * - {motors} - the motor designation (e.g. "M1350-0")
+ * - {manufacturers} - the motor manufacturer (e.g. "AeroTech")
+ * - a combination of motors and manufacturers, e.g. {motors | manufacturers} -> "M1350-0 | AeroTech"
+ * You can choose which comes first and what the separator is. E.g. {manufacturers, motors} -> "AeroTech, M1350-0".
+ *
+ *
+ * This substitutor is added through injection. All substitutors with the "@Plugin" tag in the formatting package will
+ * be included automatically.
+ */
+@Plugin
+public class MotorConfigurationSubstitutor implements RocketSubstitutor {
+ public static final String SUBSTITUTION_START = "{";
+ public static final String SUBSTITUTION_END = "}";
+ public static final String SUBSTITUTION_MOTORS = "motors";
+ public static final String SUBSTITUTION_MANUFACTURERS = "manufacturers";
+
+ // Substitutions for combinations of motors and manufacturers
+ private static final String SUBSTITUTION_PATTERN = "\\" + SUBSTITUTION_START +
+ "(" + SUBSTITUTION_MOTORS + "|" + SUBSTITUTION_MANUFACTURERS + ")" +
+ "(.*?)" +
+ "(" + SUBSTITUTION_MOTORS + "|" + SUBSTITUTION_MANUFACTURERS + ")" +
+ "\\" + SUBSTITUTION_END;
+
+ @Inject
+ private Translator trans;
+
+ @Override
+ public boolean containsSubstitution(String input) {
+ return getSubstitutionContent(input) != null;
+ }
+
+ @Override
+ public String substitute(String input, Rocket rocket, FlightConfigurationId configId) {
+ String description = getConfigurationSubstitution(input, rocket, configId);
+ String substitutionString = getSubstiutionString(input);
+ if (substitutionString != null) {
+ return input.replace(substitutionString, description);
+ }
+ return input;
+ }
+
+ @Override
+ public Map getDescriptions() {
+ return null;
+ }
+
+ public String getConfigurationSubstitution(String input, Rocket rocket, FlightConfigurationId fcid) {
+ StringBuilder configurations = new StringBuilder();
+ int motorCount = 0;
+
+ // Iterate over each stage and store the manufacturer of each motor
+ List> list = new ArrayList<>();
+ List currentList = new ArrayList<>();
+
+ String[] content = getSubstitutionContent(input);
+ if (content == null) {
+ return "";
+ }
+
+ FlightConfiguration config = rocket.getFlightConfiguration(fcid);
+ for (RocketComponent c : rocket) {
+ if (c instanceof AxialStage) {
+ currentList = new ArrayList<>();
+ list.add(currentList);
+ } else if (c instanceof MotorMount) {
+ MotorMount mount = (MotorMount) c;
+ MotorConfiguration inst = mount.getMotorConfig(fcid);
+ Motor motor = inst.getMotor();
+
+ if (mount.isMotorMount() && config.isComponentActive(mount) && (motor != null)) {
+ String motorDesignation = motor.getDesignation(inst.getEjectionDelay());
+ String manufacturer = "";
+ if (motor instanceof ThrustCurveMotor) {
+ manufacturer = ((ThrustCurveMotor) motor).getManufacturer().getDisplayName();
+ }
+
+ for (int i = 0; i < mount.getMotorCount(); i++) {
+ if (content.length == 2) {
+ if (SUBSTITUTION_MOTORS.equals(content[1])) {
+ currentList.add(motorDesignation);
+ } else if (SUBSTITUTION_MANUFACTURERS.equals(content[1])) {
+ currentList.add(manufacturer);
+ } else {
+ continue;
+ }
+ } else if (content.length == 4) {
+ String configString;
+ if (content[1].equals(SUBSTITUTION_MOTORS)) {
+ configString = motorDesignation;
+ } else if (content[1].equals(SUBSTITUTION_MANUFACTURERS)) {
+ configString = manufacturer;
+ } else {
+ continue;
+ }
+ configString += content[2];
+ if (content[3].equals(SUBSTITUTION_MOTORS)) {
+ configString += motorDesignation;
+ } else if (content[3].equals(SUBSTITUTION_MANUFACTURERS)) {
+ configString += manufacturer;
+ } else {
+ continue;
+ }
+ currentList.add(configString);
+ } else {
+ continue;
+ }
+ motorCount++;
+ }
+ }
+ }
+ }
+
+ if (motorCount == 0) {
+ return trans.get("Rocket.motorCount.Nomotor");
+ }
+
+ // Change multiple occurrences of a motor to n x motor
+ List stages = new ArrayList<>();
+ for (List stage : list) {
+ String stageName = "";
+ String previous = null;
+ int count = 0;
+
+ Collections.sort(stage);
+ for (String current : stage) {
+ if (current.equals(previous)) {
+ count++;
+ } else {
+ if (previous != null) {
+ String s = count > 1 ? count + Chars.TIMES + previous : previous;
+ stageName = stageName.equals("") ? s : stageName + "," + s;
+ }
+
+ previous = current;
+ count = 1;
+ }
+ }
+
+ if (previous != null) {
+ String s = count > 1 ? "" + count + Chars.TIMES + previous : previous;
+ stageName = stageName.equals("") ? s : stageName + "," + s;
+ }
+
+ stages.add(stageName);
+ }
+
+ for (int i = 0; i < stages.size(); i++) {
+ String s = stages.get(i);
+ if (s.equals("") && config.isStageActive(i)) {
+ s = trans.get("Rocket.motorCount.noStageMotors");
+ }
+
+ configurations.append(i == 0 ? s : "; " + s);
+ }
+
+ return configurations.toString();
+ }
+
+ /**
+ * Returns which string in input should be replaced, or null if no text needs to be replaced.
+ * @param input The input string
+ * @return The string to replace, or null if no text needs to be replaced.
+ */
+ private static String getSubstiutionString(String input) {
+ String[] content = getSubstitutionContent(input);
+ if (content != null) {
+ return content[0];
+ }
+ return null;
+ }
+
+ /**
+ * Fills in the content of the substitution tag and the separator.
+ * If there are both a motor and a manufacturer substitution tag, the array will contain the following:
+ * [0] = The full tag, including substitution start and end
+ * [1] = The motor/manufacturer substitution tag, depending on which one was found first.
+ * if there are two substitution tags, the array will also contain the following:
+ * ([2] = The separator)
+ * ([3] = The motor/manufacturer substitution tag, depending on which one was found first.)
+ * @param input The input string
+ * @return The content of the substitution tag and the separator, or null if no text needs to be replaced.
+ */
+ private static String[] getSubstitutionContent(String input) {
+ // First try with only the motors tag
+ String pattern = "\\" + SUBSTITUTION_START + "(" + SUBSTITUTION_MOTORS + ")" + "\\" + SUBSTITUTION_END;
+ Pattern regexPattern = Pattern.compile(pattern);
+ Matcher matcher = regexPattern.matcher(input);
+ if (matcher.find()) {
+ String[] content = new String[2];
+ content[0] = matcher.group(0);
+ content[1] = matcher.group(1);
+ return content;
+ }
+ // First try with only the manufacturers tag
+ pattern = "\\" + SUBSTITUTION_START + "(" + SUBSTITUTION_MANUFACTURERS + ")" + "\\" + SUBSTITUTION_END;
+ regexPattern = Pattern.compile(pattern);
+ matcher = regexPattern.matcher(input);
+ if (matcher.find()) {
+ String[] content = new String[2];
+ content[0] = matcher.group(0);
+ content[1] = matcher.group(1);
+ return content;
+ }
+
+ // Then try combined patterns
+ pattern = SUBSTITUTION_PATTERN;
+ regexPattern = Pattern.compile(pattern);
+ matcher = regexPattern.matcher(input);
+ if (matcher.find()) {
+ String[] content = new String[4];
+ content[0] = matcher.group(0);
+ content[1] = matcher.group(1);
+ if (matcher.groupCount() >= 3) {
+ content[2] = matcher.group(2);
+ content[3] = matcher.group(3);
+ for (int i = 4; i < matcher.groupCount(); i++) {
+ content[3] += matcher.group(i);
+ }
+ }
+ return content;
+ }
+ return null;
+ }
+}
+
diff --git a/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java b/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java
deleted file mode 100644
index f02f82540..000000000
--- a/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package net.sf.openrocket.formatting;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import com.google.inject.Inject;
-
-import net.sf.openrocket.l10n.Translator;
-import net.sf.openrocket.motor.Motor;
-import net.sf.openrocket.motor.MotorConfiguration;
-import net.sf.openrocket.plugin.Plugin;
-import net.sf.openrocket.rocketcomponent.AxialStage;
-import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
-import net.sf.openrocket.rocketcomponent.MotorMount;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.util.ArrayList;
-import net.sf.openrocket.util.Chars;
-
-@Plugin
-public class MotorDescriptionSubstitutor implements RocketSubstitutor {
- public static final String SUBSTITUTION = "{motors}";
-
- @Inject
- private Translator trans;
-
- @Override
- public boolean containsSubstitution(String str) {
- return str.contains(SUBSTITUTION);
- }
-
- @Override
- public String substitute(String str, Rocket rocket, FlightConfigurationId configId) {
- String description = getMotorConfigurationDescription(rocket, configId);
- return str.replace(SUBSTITUTION, description);
- }
-
- @Override
- public Map getDescriptions() {
- Map desc = new HashMap();
- desc.put(SUBSTITUTION, trans.get("MotorDescriptionSubstitutor.description"));
- return null;
- }
-
-
-
- public String getMotorConfigurationDescription(Rocket rocket, FlightConfigurationId fcid) {
- String name;
- int motorCount = 0;
-
- // Generate the description
-
- // First iterate over each stage and store the designations of each motor
- List> list = new ArrayList>();
- List currentList = Collections.emptyList();
-
- Iterator iterator = rocket.iterator();
- while (iterator.hasNext()) {
- RocketComponent c = iterator.next();
-
- if (c instanceof AxialStage) {
-
- currentList = new ArrayList();
- list.add(currentList);
-
- } else if (c instanceof MotorMount) {
-
- MotorMount mount = (MotorMount) c;
- MotorConfiguration inst = mount.getMotorConfig(fcid);
- Motor motor = inst.getMotor();
-
- if (mount.isMotorMount() && motor != null) {
- String designation = motor.getDesignation(inst.getEjectionDelay());
-
- for (int i = 0; i < mount.getMotorCount(); i++) {
- currentList.add(designation);
- motorCount++;
- }
- }
-
- }
- }
-
- if (motorCount == 0) {
- return trans.get("Rocket.motorCount.Nomotor");
- }
-
- // Change multiple occurrences of a motor to n x motor
- List stages = new ArrayList();
-
- for (List stage : list) {
- String stageName = "";
- String previous = null;
- int count = 0;
-
- Collections.sort(stage);
- for (String current : stage) {
- if (current.equals(previous)) {
-
- count++;
-
- } else {
-
- if (previous != null) {
- String s = "";
- if (count > 1) {
- s = "" + count + Chars.TIMES + previous;
- } else {
- s = previous;
- }
-
- if (stageName.equals(""))
- stageName = s;
- else
- stageName = stageName + "," + s;
- }
-
- previous = current;
- count = 1;
-
- }
- }
- if (previous != null) {
- String s = "";
- if (count > 1) {
- s = "" + count + Chars.TIMES + previous;
- } else {
- s = previous;
- }
-
- if (stageName.equals(""))
- stageName = s;
- else
- stageName = stageName + "," + s;
- }
-
- stages.add(stageName);
- }
-
- name = "";
- for (int i = 0; i < stages.size(); i++) {
- String s = stages.get(i);
- if (s.equals(""))
- s = trans.get("Rocket.motorCount.noStageMotors");
- if (i == 0)
- name = name + s;
- else
- name = name + "; " + s;
- }
- return name;
- }
-
-
-
-}
diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java
index 889d0f6d7..1b2ec0f0d 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java
@@ -8,6 +8,7 @@ import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
+import net.sf.openrocket.formatting.RocketDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,6 +37,7 @@ public class FlightConfiguration implements FlightConfigurableParameter getChildAssemblies() {
+ public final List getAllChildAssemblies() {
checkState();
Iterator children = iterator(false);
- List result = new ArrayList<>();
+ List result = new ArrayList<>();
while (children.hasNext()) {
RocketComponent child = children.next();
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 getDirectChildAssemblies() {
+ checkState();
+
+ List result = new ArrayList<>();
+
+ for (RocketComponent child : this.getChildren()) {
+ if (child instanceof ComponentAssembly) {
+ result.add((ComponentAssembly) child);
}
}
return result;
diff --git a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
index b4204ff85..9f1afe722 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
@@ -7,10 +7,10 @@ import java.util.Collection;
import java.util.List;
import net.sf.openrocket.preset.ComponentPreset;
+import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
-import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
/**
* 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) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
- return (SymmetricComponent) searchSibling;
+ return getPreviousSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
+ (SymmetricComponent) searchSibling, 0);
}
--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
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;
}
+
+ /**
+ * 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.
@@ -670,7 +738,8 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
while (searchSiblingIndex < searchParent.getChildCount()) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
- return (SymmetricComponent) searchSibling;
+ return getNextSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
+ (SymmetricComponent) searchSibling, 0);
}
++searchSiblingIndex;
}
@@ -681,13 +750,22 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
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
for (RocketComponent child : getChildren()) {
- if (child instanceof PodSet) {
+ if (child instanceof ComponentAssembly) {
for (RocketComponent grandchild : child.getChildren()) {
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;
}
+ /**
+ * 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
*
diff --git a/core/src/net/sf/openrocket/rocketcomponent/position/RadiusMethod.java b/core/src/net/sf/openrocket/rocketcomponent/position/RadiusMethod.java
index 5b466f0d8..f2573bdeb 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/position/RadiusMethod.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/position/RadiusMethod.java
@@ -10,7 +10,7 @@ public enum RadiusMethod implements DistanceMethod {
// just as a reminder:
// public T[] getEnumConstants()
- // both components are on the same axis
+ // Same axis as the target component
COAXIAL ( Application.getTranslator().get("RocketComponent.Position.Method.Radius.COAXIAL") ){
@Override
public double getRadius( final RocketComponent parentComponent, final RocketComponent thisComponent, final double requestedOffset ){
@@ -22,7 +22,8 @@ public enum RadiusMethod implements DistanceMethod {
return 0;
}
},
-
+
+ // Center of the parent component
FREE(Application.getTranslator().get("RocketComponent.Position.Method.Radius.FREE") ){
@Override
public boolean clampToZero() { return false; }
@@ -37,7 +38,8 @@ public enum RadiusMethod implements DistanceMethod {
return radius;
}
},
-
+
+ // Surface of the parent component
RELATIVE ( Application.getTranslator().get("RocketComponent.Position.Method.Radius.RELATIVE") ){
@Override
public boolean clampToZero() { return false; }
diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java
index 9293773bc..f51b9ee99 100644
--- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java
+++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java
@@ -469,7 +469,6 @@ public class BarrowmanCalculatorTest {
final FlightConfiguration testConfig = testRocket.getSelectedConfiguration();
final FlightConditions testConditions = new FlightConditions(testConfig);
- TestRockets.dumpRocket(testRocket, "/home/joseph/rockets/openrocket/git/openrocket/work/testrocket.ork");
final BarrowmanCalculator testCalc = new BarrowmanCalculator();
double testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
final AerodynamicForces testForces = testCalc.getAerodynamicForces(testConfig, testConditions, warnings);
@@ -483,13 +482,19 @@ public class BarrowmanCalculatorTest {
// move the pod back.
pod.setAxialOffset(pod.getAxialOffset() + 0.1);
testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
- assertFalse("should be warning from gap in airframe", warnings.isEmpty());
+ assertEquals("should be warning from gap in airframe", 1, warnings.size());
// move the pod forward.
warnings.clear();
- pod.setAxialOffset(pod.getAxialOffset() - 0.2);
- testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
- assertFalse("should be warning from airframe overlap", warnings.isEmpty());
+ pod.setAxialOffset(pod.getAxialOffset() - 0.3);
+ testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
+ assertEquals("should be warning from airframe overlap", 1, warnings.size());
+
+ // move the pod back.
+ warnings.clear();
+ pod.setAxialOffset(pod.getAxialOffset() + 0.1);
+ testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
+ assertEquals("should be warning from podset airframe overlap", 1, warnings.size());
}
}
diff --git a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java
index b671af3b3..372d3f2de 100644
--- a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java
+++ b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java
@@ -580,10 +580,114 @@ public class FlightConfigurationTest extends BaseTestCase {
}
+ @Test
+ public void testName() {
+ Rocket rocket = TestRockets.makeFalcon9Heavy();
+ FlightConfiguration selected = rocket.getSelectedConfiguration();
+
+ // Test only motors or only manufacturers
+ selected.setName("[{motors}] - [{manufacturers}]");
+
+ selected.setAllStages();
+ assertEquals("[[Rocket.motorCount.noStageMotors]; M1350-0; 4\u00D7G77-0] - [[Rocket.motorCount.noStageMotors]; AeroTech; 4\u00D7AeroTech]", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("[[Rocket.motorCount.Nomotor]] - [[Rocket.motorCount.Nomotor]]", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("[; M1350-0; ] - [; AeroTech; ]", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("[; M1350-0; 4\u00D7G77-0] - [; AeroTech; 4\u00D7AeroTech]", selected.getName());
+
+
+ // Test combination of motors and manufacturers
+ selected.setName("[{motors manufacturers}] -- [{manufacturers}] - [{motors}]");
+
+ selected.setAllStages();
+ assertEquals("[[Rocket.motorCount.noStageMotors]; M1350-0 AeroTech; 4\u00D7G77-0 AeroTech] -- [[Rocket.motorCount.noStageMotors]; AeroTech; 4\u00D7AeroTech] - [[Rocket.motorCount.noStageMotors]; M1350-0; 4\u00D7G77-0]", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("[[Rocket.motorCount.Nomotor]] -- [[Rocket.motorCount.Nomotor]] - [[Rocket.motorCount.Nomotor]]", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("[; M1350-0 AeroTech; ] -- [; AeroTech; ] - [; M1350-0; ]", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("[; M1350-0 AeroTech; 4\u00D7G77-0 AeroTech] -- [; AeroTech; 4\u00D7AeroTech] - [; M1350-0; 4\u00D7G77-0]", selected.getName());
+
+ // Test combination of manufacturers and motors
+ selected.setName("[{manufacturers | motors}]");
+
+ selected.setAllStages();
+ assertEquals("[[Rocket.motorCount.noStageMotors]; AeroTech | M1350-0; 4\u00D7AeroTech | G77-0]", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("[[Rocket.motorCount.Nomotor]]", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("[; AeroTech | M1350-0; ]", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("[; AeroTech | M1350-0; 4\u00D7AeroTech | G77-0]", selected.getName());
+
+ // Test empty tags
+ selected.setName("{}");
+
+ selected.setAllStages();
+ assertEquals("{}", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("{}", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("{}", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("{}", selected.getName());
+
+ // Test invalid tags (1)
+ selected.setName("{motorsm}");
+
+ selected.setAllStages();
+ assertEquals("{motorsm}", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("{motorsm}", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("{motorsm}", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("{motorsm}", selected.getName());
+
+ // Test invalid tags (2)
+ selected.setName("{motors manufacturers '}");
+
+ selected.setAllStages();
+ assertEquals("{motors manufacturers '}", selected.getName());
+
+ selected.setOnlyStage(0);
+ assertEquals("{motors manufacturers '}", selected.getName());
+
+ selected.setOnlyStage(1);
+ assertEquals("{motors manufacturers '}", selected.getName());
+
+ selected.setAllStages();
+ selected._setStageActive(0, false);
+ assertEquals("{motors manufacturers '}", selected.getName());
+ }
+
@Test
public void testCopy() throws NoSuchFieldException, IllegalAccessException {
Rocket rocket = TestRockets.makeFalcon9Heavy();
FlightConfiguration original = rocket.getSelectedConfiguration();
+ original.setName("[{motors}] - [{manufacturers}]");
original.setOnlyStage(0);
// vvvv Test Target vvvv
diff --git a/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentTest.java b/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentTest.java
new file mode 100644
index 000000000..5647485d2
--- /dev/null
+++ b/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentTest.java
@@ -0,0 +1,509 @@
+package net.sf.openrocket.rocketcomponent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import net.sf.openrocket.rocketcomponent.position.AxialMethod;
+import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
+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());
+ }
+}
diff --git a/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentVolumeTest.java b/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentVolumeTest.java
index e85ef71e3..0a2170188 100644
--- a/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentVolumeTest.java
+++ b/core/test/net/sf/openrocket/rocketcomponent/SymmetricComponentVolumeTest.java
@@ -8,443 +8,380 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test;
public class SymmetricComponentVolumeTest extends BaseTestCase {
-
- @Test
- public void simpleConeFilled() {
- NoseCone nc = new NoseCone();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(1.0);
- nc.setFilled(true);
- nc.setType(Transition.Shape.CONICAL);
- nc.setAftRadius(1.0);
- 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);
-
- double volume = Math.PI / 3.0;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(0.75, cg.x, epsilonPercent * 0.75);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleConeWithShoulderFilled() {
- NoseCone nc = new NoseCone();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(1.0);
- nc.setFilled(true);
- nc.setType(Transition.Shape.CONICAL);
- nc.setAftRadius(1.0);
- nc.setAftShoulderRadius(1.0);
- nc.setAftShoulderLength(1.0);
- nc.setAftShoulderThickness(1.0);
- 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);
-
- double volume = Math.PI / 3.0;
- volume += Math.PI;
-
- double mass = density * volume;
-
- //System.out.println(volume + "\t" + mass);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(1.312, cg.x, epsilonPercent * 1.071);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleConeHollow() {
- NoseCone nc = new NoseCone();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(1.0);
- nc.setAftRadius(1.0);
- nc.setThickness(0.5);
- nc.setType(Transition.Shape.CONICAL);
- 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);
-
- double volume = Math.PI / 3.0; // outer volume
-
- // manually projected Thickness of 0.5 on to radius to determine
- // the innerConeDimen. Since the outer cone is "square" (height = radius),
- // we only need to compute this one dimension in order to compute the
- // volume of the inner cone.
- double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
- double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
- volume -= innerVolume;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(0.7454, cg.x, epsilonPercent * 0.7454);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleConeWithShoulderHollow() {
- NoseCone nc = new NoseCone();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(1.0);
- nc.setType(Transition.Shape.CONICAL);
- nc.setAftRadius(1.0);
- nc.setThickness(0.5);
- nc.setAftShoulderRadius(1.0);
- nc.setAftShoulderLength(1.0);
- nc.setAftShoulderThickness(0.5);
- 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);
-
- double volume = Math.PI / 3.0; // outer volume
-
- // manually projected Thickness of 0.5 on to radius to determine
- // the innerConeDimen. Since the outer cone is "square" (height = radius),
- // we only need to compute this one dimension in order to compute the
- // volume of the inner cone.
- double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
- double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
- volume -= innerVolume;
-
- volume += Math.PI - Math.PI * 0.5 * 0.5;
-
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(1.2719, cg.x, epsilonPercent * 1.2719);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleTransitionFilled() {
- Transition nc = new Transition();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(4.0);
- nc.setFilled(true);
- nc.setType(Transition.Shape.CONICAL);
- nc.setForeRadius(1.0);
- nc.setAftRadius(2.0);
- 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);
-
- 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(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(2.4285, cg.x, epsilonPercent * 2.4285);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleTransitionWithShouldersFilled() {
- Transition nc = new Transition();
-
- final double epsilonPercent = 0.001;
- final double density = 2.0;
-
- nc.setLength(4.0);
- nc.setFilled(true);
- nc.setType(Transition.Shape.CONICAL);
- nc.setForeRadius(1.0);
- nc.setAftRadius(2.0);
- nc.setAftShoulderLength(1.0);
- nc.setAftShoulderRadius(2.0);
- nc.setAftShoulderThickness(2.0);
- nc.setForeShoulderLength(1.0);
- nc.setForeShoulderRadius(1.0);
- nc.setForeShoulderThickness(1.0);
- 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);
-
- double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
- // plus aft shoulder:
- volume += Math.PI * 1.0 * 2.0 * 2.0;
- // plus fore shoulder:
- volume += Math.PI * 1.0 * 1.0 * 1.0;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- 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);
- }
-
- @Test
- public void simpleTransitionHollow1() {
- 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.5);
- 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 cone.
- // the dimensions of this cone were determined
- // using a 2d cad tool.
-
- double innerConeRadius = 0.441;
- double innerConeLength = 0.882;
- double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
-
- double volume = filledVolume - innerVolume;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(0.5884, cg.x, epsilonPercent * 0.5884);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleTransitionWithShouldersHollow1() {
- 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.5);
- nc.setAftShoulderLength(1.0);
- nc.setAftShoulderRadius(1.0);
- nc.setAftShoulderThickness(0.5);
- nc.setForeShoulderLength(1.0);
- nc.setForeShoulderRadius(0.5);
- nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled.
- 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 cone.
- // the dimensions of this cone were determined
- // using a 2d cad tool.
-
- double innerConeRadius = 0.441;
- double innerConeLength = 0.882;
- double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
-
- double volume = filledVolume - innerVolume;
-
- // Now add aft shoulder
- volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5;
- // Now add fore shoulder
- volume += Math.PI * 1.0 * 0.5 * 0.5;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(0.8581, cg.x, epsilonPercent * 0.8581);
- assertEquals(mass, cg.weight, epsilonPercent * mass);
- }
-
- @Test
- public void simpleTransitionHollow2() {
- 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.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;
-
- double mass = density * volume;
-
- //System.out.println(volume);
-
- assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
- assertEquals(mass, nc.getMass(), epsilonPercent * mass);
-
- assertEquals(0.56827, cg.x, epsilonPercent * 0.56827);
- 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);
- }
-
+
+ @Test
+ public void testVolumeSimpleConeFilled() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setFilled(true);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setAftRadius(1.0);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+ double volume = Math.PI / 3.0;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(0.75, cg.x, epsilonPercent * 0.75);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleConeWithShoulderFilled() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setFilled(true);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setAftRadius(1.0);
+ nc.setAftShoulderRadius(1.0);
+ nc.setAftShoulderLength(1.0);
+ nc.setAftShoulderThickness(1.0);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+
+ double volume = Math.PI / 3.0;
+ volume += Math.PI;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(1.312, cg.x, epsilonPercent * 1.071);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleConeHollow() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setAftRadius(1.0);
+ nc.setThickness(0.5);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+
+ double volume = Math.PI / 3.0; // outer volume
+
+ // manually projected Thickness of 0.5 on to radius to determine
+ // the innerConeDimen. Since the outer cone is "square" (height = radius),
+ // we only need to compute this one dimension in order to compute the
+ // volume of the inner cone.
+ double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
+ double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
+ volume -= innerVolume;
+
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(0.7454, cg.x, epsilonPercent * 0.7454);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleConeWithShoulderHollow() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setAftRadius(1.0);
+ nc.setThickness(0.5);
+ nc.setAftShoulderRadius(1.0);
+ nc.setAftShoulderLength(1.0);
+ nc.setAftShoulderThickness(0.5);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+
+ double volume = Math.PI / 3.0; // outer volume
+
+ // manually projected Thickness of 0.5 on to radius to determine
+ // the innerConeDimen. Since the outer cone is "square" (height = radius),
+ // we only need to compute this one dimension in order to compute the
+ // volume of the inner cone.
+ double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
+ double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
+ volume -= innerVolume;
+ volume += Math.PI - Math.PI * 0.5 * 0.5;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(1.2719, cg.x, epsilonPercent * 1.2719);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionFilled() {
+ Transition nc = new Transition();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(4.0);
+ nc.setFilled(true);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setForeRadius(1.0);
+ nc.setAftRadius(2.0);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+ double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(2.4285, cg.x, epsilonPercent * 2.4285);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionWithShouldersFilled() {
+ Transition nc = new Transition();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(4.0);
+ nc.setFilled(true);
+ nc.setType(Transition.Shape.CONICAL);
+ nc.setForeRadius(1.0);
+ nc.setAftRadius(2.0);
+ nc.setAftShoulderLength(1.0);
+ nc.setAftShoulderRadius(2.0);
+ nc.setAftShoulderThickness(2.0);
+ nc.setForeShoulderLength(1.0);
+ nc.setForeShoulderRadius(1.0);
+ nc.setForeShoulderThickness(1.0);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ Coordinate cg = nc.getCG();
+
+ double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
+ // plus aft shoulder:
+ volume += Math.PI * 1.0 * 2.0 * 2.0;
+ // plus fore shoulder:
+ volume += Math.PI * 1.0 * 1.0 * 1.0;
+ double mass = density * volume;
+
+ 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);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionHollow1() {
+ 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.5);
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ 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;
+
+ // magic 2D cad drawing...
+ //
+ // Since the thickness >= fore radius, the
+ // hollowed out portion of the transition
+ // forms a cone.
+ // the dimensions of this cone were determined
+ // using a 2d cad tool.
+
+ double innerConeRadius = 0.441;
+ double innerConeLength = 0.882;
+ double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
+ double volume = filledVolume - innerVolume;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(0.5884, cg.x, epsilonPercent * 0.5884);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionWithShouldersHollow1() {
+ 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.5);
+ nc.setAftShoulderLength(1.0);
+ nc.setAftShoulderRadius(1.0);
+ nc.setAftShoulderThickness(0.5);
+ nc.setForeShoulderLength(1.0);
+ nc.setForeShoulderRadius(0.5);
+ nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled.
+ nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ 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;
+
+ // magic 2D cad drawing...
+ //
+ // Since the thickness >= fore radius, the
+ // hollowed out portion of the transition
+ // forms a cone.
+ // the dimensions of this cone were determined
+ // using a 2d cad tool.
+
+ double innerConeRadius = 0.441;
+ double innerConeLength = 0.882;
+ double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
+
+ double volume = filledVolume - innerVolume;
+
+ // Now add aft shoulder
+ volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5;
+ // Now add fore shoulder
+ volume += Math.PI * 1.0 * 0.5 * 0.5;
+
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(0.8581, cg.x, epsilonPercent * 0.8581);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionHollow2() {
+ 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.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
+
+ 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;
+
+ // 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;
+ double mass = density * volume;
+
+ assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals(mass, nc.getMass(), epsilonPercent * mass);
+
+ assertEquals(0.56827, cg.x, epsilonPercent * 0.56827);
+ assertEquals(mass, cg.weight, epsilonPercent * mass);
+ }
+
+ @Test
+ public void testVolumeSimpleTransitionWithShouldersHollow2() {
+ 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();
+
+ // 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;
+
+ 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);
+ }
}
diff --git a/swing/src/net/sf/openrocket/gui/components/StageSelector.java b/swing/src/net/sf/openrocket/gui/components/StageSelector.java
index 1609c16d9..3699e5dbd 100644
--- a/swing/src/net/sf/openrocket/gui/components/StageSelector.java
+++ b/swing/src/net/sf/openrocket/gui/components/StageSelector.java
@@ -14,6 +14,7 @@ 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.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
@@ -40,7 +41,7 @@ public class StageSelector extends JPanel implements StateChangeListener {
private void updateButtons( final FlightConfiguration configuration ) {
buttons.clear();
this.removeAll();
- List assemblies = configuration.getRocket().getChildAssemblies();
+ List assemblies = configuration.getRocket().getAllChildAssemblies();
for (RocketComponent stage : assemblies) {
if (!(stage instanceof AxialStage)) continue;
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
index b768b81b6..ee295ee9f 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
@@ -1,5 +1,6 @@
package net.sf.openrocket.gui.dialogs.flightconfiguration;
+import java.awt.Color;
import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.ActionEvent;
@@ -12,6 +13,8 @@ import javax.swing.JPanel;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.configdialog.CommonStrings;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
@@ -28,7 +31,7 @@ public class RenameConfigDialog extends JDialog {
JPanel panel = new JPanel(new MigLayout("fill"));
- panel.add(new JLabel(trans.get("RenameConfigDialog.lbl.name")), "span, wrap rel");
+ panel.add(new JLabel(trans.get("RenameConfigDialog.lbl.name") + " " + CommonStrings.dagger), "span, wrap rel");
final JTextField textbox = new JTextField(rocket.getFlightConfiguration(fcid).getNameRaw());
panel.add(textbox, "span, w 200lp, growx, wrap para");
@@ -63,7 +66,15 @@ public class RenameConfigDialog extends JDialog {
RenameConfigDialog.this.setVisible(false);
}
});
- panel.add(cancel);
+ panel.add(cancel, "wrap para");
+
+ // {motors} & {manufacturers} info
+ String text = "" + CommonStrings.dagger + " " + trans.get("RenameConfigDialog.lbl.infoMotors")
+ + trans.get("RenameConfigDialog.lbl.infoManufacturers")
+ + trans.get("RenameConfigDialog.lbl.infoCombination");
+ StyledLabel info = new StyledLabel(text, -2);
+ info.setFontColor(Color.DARK_GRAY);
+ panel.add(info, "spanx, growx, wrap");
this.add(panel);