/**
* An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here.
*
- * @param <C> the specific RocketComponent subtype for which the concrete handler can create
+ * @param <C> the specific RocketComponent subtype for which the concrete handler can create
*/
public abstract class BaseHandler<C extends RocketComponent> extends AbstractElementHandler {
/**
* The SAX method called when the closing element tag is reached.
*
- * @param element the element name.
- * @param attributes attributes of the element.
- * @param content the textual content of the element.
- * @param warnings the warning set to store warnings in.
+ * @param element the element name.
+ * @param attributes attributes of the element.
+ * @param content the textual content of the element.
+ * @param warnings the warning set to store warnings in.
+ *
* @throws SAXException
*/
mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS);
}
if (RocksimCommonConstants.DENSITY.equals(element)) {
- density = Math.max(0d, Double.parseDouble(content) );
+ density = Math.max(0d, Double.parseDouble(content));
}
if (RocksimCommonConstants.KNOWN_CG.equals(element)) {
cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
/**
* Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic,
* rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density.
- * Some G10 fiberglass materials are in cubic units, other G10 fiberglass is in square units. And due to a
- * Rocksim bug, some densities are 0 when they clearly should not be.
- *
+ * Some G10 fiberglass materials are in cubic units, other G10 fiberglass is in square units. And due to a Rocksim
+ * bug, some densities are 0 when they clearly should not be.
+ * <p/>
* This may be overridden for specific component density computations.
*
* @param type the rocksim density
* @param rawDensity the density as specified in the Rocksim design file
+ *
* @return a value in OpenRocket SURFACE density units
*/
protected double computeDensity(RocksimDensityType type, double rawDensity) {
}
/**
- * If the Rocksim component does not override the mass, then create a Material based upon the density defined
- * for that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
+ * If the Rocksim component does not override the mass, then create a Material based upon the density defined for
+ * that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
*
* @param component the component
* @param type the type of the material
* @param density the density in g/cm^3
- * @param definedMaterial the material that is currently defined on the component; used only to get the name
- * as it appears in Rocksim
+ * @param definedMaterial the material that is currently defined on the component; used only to get the name as it
+ * appears in Rocksim
*/
public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
double density) {
/**
* Override the mass and Cg of the component.
*
- * @param component the component
- * @param override true if any override should happen
- * @param mass the override mass
- * @param cg the override cg
+ * @param component the component
+ * @param override true if any override should happen
+ * @param mass the override mass
+ * @param cg the override cg
*/
public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
if (override) {
/**
* Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket.
*
- * @param theCG the CG value to really use when overriding CG on the OpenRocket component
+ * @param theCG the CG value to really use when overriding CG on the OpenRocket component
*/
protected void setCG(double theCG) {
cg = theCG;
/**
* Set the material name as specified in the Rocksim design file.
*
- * @param content the material name
+ * @param content the material name
*/
protected void setMaterialName(String content) {
materialName = content;
}
+ /**
+ * Get the Rocksim enum of the component's density type.
+ *
+ * @return a Rocksim density type
+ */
+ protected RocksimDensityType getDensityType() {
+ return densityType;
+ }
+
/**
* Add child to parent only if the child is compatible. Otherwise add to warning set.
- *
- * @param parent the parent component
- * @param child the child component
+ *
+ * @param parent the parent component
+ * @param child the child component
* @param warnings the warning set
- *
+ *
* @return true if the child is compatible with parent
*/
protected static boolean isCompatible(RocketComponent parent, Class<? extends RocketComponent> child, WarningSet warnings) {
+ return isCompatible(parent, child, warnings, false);
+ }
+
+ /**
+ * Add child to parent only if the child is compatible. Otherwise add to warning set.
+ *
+ * @param parent the parent component
+ * @param child the child component
+ * @param warnings the warning set
+ * @param suppressWarnings suppress warnings, just return the boolean
+ *
+ * @return true if the child is compatible with parent
+ */
+ protected static boolean isCompatible(RocketComponent parent, Class<? extends RocketComponent> child,
+ WarningSet warnings,
+ boolean suppressWarnings) {
if (!parent.isCompatible(child)) {
- warnings.add(child.getName() + " can not be attached to "
- + parent.getComponentName() + ", ignoring component.");
+ if (!suppressWarnings) {
+ warnings.add(child.getName() + " can not be attached to "
+ + parent.getComponentName() + ", ignoring component.");
+ }
return false;
}
else {
return true;
}
}
-
+
/**
- * Create a custom material based on the density. The name of the material is prepended with 'RS: ' to
- * indicate it came from a RockSim material.
+ * Create a custom material based on the density. The name of the material is prepended with 'RS: ' to indicate it
+ * came from a RockSim material.
*
* @param type the type of the material
* @param name the name of the component
* Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent
* have the setMaterial method. Unfortunately the supertype cannot be used.
*
- * @param component the component who's material is to be set
- * @param material the material to be set on the component (defined by getComponent())
+ * @param component the component who's material is to be set
+ * @param material the material to be set on the component (defined by getComponent())
*/
private static void setMaterial(RocketComponent component, Material material) {
try {
/**
* Find a method by name and argument list.
*
- * @param component the component who's material is to be seta
- * @param name the method name
- * @param args the class types of the parameters
+ * @param component the component who's material is to be set
+ * @param name the method name
+ * @param args the class types of the parameters
*
* @return the Method instance, or null
*/
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.file.rocksim.RocksimCommonConstants;
+import net.sf.openrocket.file.rocksim.RocksimDensityType;
import net.sf.openrocket.file.simplesax.ElementHandler;
import net.sf.openrocket.file.simplesax.PlainTextHandler;
import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.Coaxial;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.MassObject;
import net.sf.openrocket.rocketcomponent.RocketComponent;
*/
class MassObjectHandler extends PositionDependentHandler<MassObject> {
- /**
+ /**
* The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point
* that it looks ridiculous in OpenRocket. This fudge factor is here merely to get the typical mass object to
- * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that
- * Rocksim does not expose the length of a mass object in the UI and actually treats mass objects as point objects -
- * not 3 or even 2 dimensional.
+ * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that Rocksim
+ * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3
+ * or even 2 dimensional.
*/
public static final int MASS_LEN_FUDGE_FACTOR = 100;
private int typeCode = 0;
/**
- * Constructor.
- *l
- * @param c the parent component
- * @param warnings the warning set
- *
- * @throws IllegalArgumentException thrown if <code>c</code> is null
+ * Constructor. l
+ *
+ * @param c the parent component
+ * @param warnings the warning set
+ *
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
*/
public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException {
if (c == null) {
//length) and because Rocksim sets the CG of the mass object to really be relative to the front of
//the parent. But that value is already assumed in the position and position value for the component.
//Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object.
- super.setCG(0);
+ super.setCG(0);
}
if (RocksimCommonConstants.TYPE_CODE.equals(element)) {
typeCode = Integer.parseInt(content);
}
@Override
- public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
- if (typeCode == 0) { //General Mass Object
+ public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws
+ SAXException {
+ if (inferAsShockCord(typeCode, warnings)) { //Shock Cord
+ mapMassObjectAsShockCord(element, attributes, content, warnings);
+ }
+ else { // typeCode == 0 General Mass Object
if (isCompatible(parent, MassComponent.class, warnings)) {
parent.addChild(mass);
}
super.endHandler(element, attributes, content, warnings);
}
- else if (typeCode == 1) { //Shock Cord
- ShockCord cord = new ShockCord();
- current = cord;
- if (isCompatible(parent, ShockCord.class, warnings)) {
- parent.addChild(cord);
- }
- super.endHandler(element, attributes, content, warnings);
- cord.setName(mass.getName());
-
- setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX());
+ }
- cord.setRadialDirection(mass.getRadialDirection());
- cord.setRadialPosition(mass.getRadialPosition());
- cord.setRadius(mass.getRadius());
+ /**
+ * Rocksim does not have a separate entity for Shock Cords. It has to be inferred. Sometimes the typeCode
+ * indicates it's a shock cord, but most times it does not. This is due to bugs in the Rocksim Component and
+ * Material databases. Try to infer a shock cord based on it's length and it's material type. It's somewhat
+ * arbitrary, but if the mass object's length is more than twice the length of it's parent component and it's a LINE
+ * material, then assume a shock cord.
+ *
+ * @param theTypeCode the code from the RKT XML file
+ *
+ * @return true if we think it's a shock cord
+ */
+ private boolean inferAsShockCord(int theTypeCode, WarningSet warnings) {
+ return (theTypeCode == 1 || (mass.getLength() >= 2 * parent.getLength() && RocksimDensityType.ROCKSIM_LINE
+ .equals(getDensityType()))) && isCompatible(parent, ShockCord.class, warnings, true);
+ }
- //Rocksim does not distinguish between total length of the cord and the packed length. Fudge the
- //packed length and set the real length.
- cord.setCordLength(mass.getLength());
- cord.setLength(cord.getCordLength()/MASS_LEN_FUDGE_FACTOR);
+ /**
+ * If it appears that the mass object is a shock cord, then create an OR shock cord instance.
+ *
+ * @param element the element name
+ * @param attributes the attributes
+ * @param content the content of the element
+ * @param warnings the warning set to store warnings in.
+ *
+ * @throws org.xml.sax.SAXException not thrown
+ */
+ private void mapMassObjectAsShockCord(final String element, final HashMap<String, String> attributes,
+ final String content, final WarningSet warnings) throws SAXException {
+ ShockCord cord = new ShockCord();
+ current = cord;
+ if (isCompatible(parent, ShockCord.class, warnings)) {
+ parent.addChild(cord);
+ }
+ super.endHandler(element, attributes, content, warnings);
+ cord.setName(mass.getName());
+
+ setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX());
+
+ cord.setRadialDirection(mass.getRadialDirection());
+ cord.setRadialPosition(mass.getRadialPosition());
+ cord.setRadius(mass.getRadius());
+
+ //Rocksim does not distinguish between total length of the cord and the packed length. Fudge the
+ //packed length and set the real length.
+ cord.setCordLength(mass.getLength());
+ cord.setLength(cord.getCordLength() / MASS_LEN_FUDGE_FACTOR);
+ if (parent instanceof Coaxial) {
+ Coaxial parentCoaxial = (Coaxial) parent;
+ cord.setRadius(parentCoaxial.getInnerRadius());
}
}
}
/**
- * Get the required type of material for this component. Does not apply to MassComponents, but does apply to Shock Cords.
+ * Get the required type of material for this component. Does not apply to MassComponents, but does apply to Shock
+ * Cords.
*
* @return LINE
*/