Added inference of Rocksim shock cords to try to be a little more predictive when a Rocksim file uses a buggy shock

cord from their component database.
This commit is contained in:
Doug Pedrick 2012-07-11 22:06:23 +00:00
parent c8f7665177
commit 960555c108
2 changed files with 131 additions and 63 deletions

View File

@ -18,7 +18,7 @@ import java.util.HashMap;
/** /**
* An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here. * 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 { public abstract class BaseHandler<C extends RocketComponent> extends AbstractElementHandler {
@ -51,10 +51,11 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* The SAX method called when the closing element tag is reached. * The SAX method called when the closing element tag is reached.
* *
* @param element the element name. * @param element the element name.
* @param attributes attributes of the element. * @param attributes attributes of the element.
* @param content the textual content of the element. * @param content the textual content of the element.
* @param warnings the warning set to store warnings in. * @param warnings the warning set to store warnings in.
*
* @throws SAXException * @throws SAXException
*/ */
@ -70,7 +71,7 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS); mass = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_MASS);
} }
if (RocksimCommonConstants.DENSITY.equals(element)) { 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)) { if (RocksimCommonConstants.KNOWN_CG.equals(element)) {
cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH); cg = Math.max(0d, Double.parseDouble(content) / RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
@ -105,13 +106,14 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic, * 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. * 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 * Some G10 fiberglass materials are in cubic units, other G10 fiberglass is in square units. And due to a Rocksim
* Rocksim bug, some densities are 0 when they clearly should not be. * bug, some densities are 0 when they clearly should not be.
* * <p/>
* This may be overridden for specific component density computations. * This may be overridden for specific component density computations.
* *
* @param type the rocksim density * @param type the rocksim density
* @param rawDensity the density as specified in the Rocksim design file * @param rawDensity the density as specified in the Rocksim design file
*
* @return a value in OpenRocket SURFACE density units * @return a value in OpenRocket SURFACE density units
*/ */
protected double computeDensity(RocksimDensityType type, double rawDensity) { protected double computeDensity(RocksimDensityType type, double rawDensity) {
@ -119,14 +121,14 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
} }
/** /**
* If the Rocksim component does not override the mass, then create a Material based upon the density defined * If the Rocksim component does not override the mass, then create a Material based upon the density defined for
* for that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket. * that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
* *
* @param component the component * @param component the component
* @param type the type of the material * @param type the type of the material
* @param density the density in g/cm^3 * @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 * @param definedMaterial the material that is currently defined on the component; used only to get the name as it
* as it appears in Rocksim * appears in Rocksim
*/ */
public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type, public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
double density) { double density) {
@ -139,10 +141,10 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* Override the mass and Cg of the component. * Override the mass and Cg of the component.
* *
* @param component the component * @param component the component
* @param override true if any override should happen * @param override true if any override should happen
* @param mass the override mass * @param mass the override mass
* @param cg the override cg * @param cg the override cg
*/ */
public static void setOverride(RocketComponent component, boolean override, double mass, double cg) { public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
if (override) { if (override) {
@ -171,7 +173,7 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket. * 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) { protected void setCG(double theCG) {
cg = theCG; cg = theCG;
@ -180,35 +182,62 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* Set the material name as specified in the Rocksim design file. * 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) { protected void setMaterialName(String content) {
materialName = 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. * Add child to parent only if the child is compatible. Otherwise add to warning set.
* *
* @param parent the parent component * @param parent the parent component
* @param child the child component * @param child the child component
* @param warnings the warning set * @param warnings the warning set
* *
* @return true if the child is compatible with parent * @return true if the child is compatible with parent
*/ */
protected static boolean isCompatible(RocketComponent parent, Class<? extends RocketComponent> child, WarningSet warnings) { 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)) { if (!parent.isCompatible(child)) {
warnings.add(child.getName() + " can not be attached to " if (!suppressWarnings) {
+ parent.getComponentName() + ", ignoring component."); warnings.add(child.getName() + " can not be attached to "
+ parent.getComponentName() + ", ignoring component.");
}
return false; return false;
} }
else { else {
return true; return true;
} }
} }
/** /**
* Create a custom material based on the density. The name of the material is prepended with 'RS: ' to * Create a custom material based on the density. The name of the material is prepended with 'RS: ' to indicate it
* indicate it came from a RockSim material. * came from a RockSim material.
* *
* @param type the type of the material * @param type the type of the material
* @param name the name of the component * @param name the name of the component
@ -224,8 +253,8 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
* Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent * 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. * have the setMaterial method. Unfortunately the supertype cannot be used.
* *
* @param component the component who's material is to be set * @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 material the material to be set on the component (defined by getComponent())
*/ */
private static void setMaterial(RocketComponent component, Material material) { private static void setMaterial(RocketComponent component, Material material) {
try { try {
@ -243,9 +272,9 @@ public abstract class BaseHandler<C extends RocketComponent> extends AbstractEle
/** /**
* Find a method by name and argument list. * Find a method by name and argument list.
* *
* @param component the component who's material is to be seta * @param component the component who's material is to be set
* @param name the method name * @param name the method name
* @param args the class types of the parameters * @param args the class types of the parameters
* *
* @return the Method instance, or null * @return the Method instance, or null
*/ */

View File

@ -5,9 +5,11 @@ package net.sf.openrocket.file.rocksim.importt;
import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.file.rocksim.RocksimCommonConstants; 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.ElementHandler;
import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.Coaxial;
import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.MassObject; import net.sf.openrocket.rocketcomponent.MassObject;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -21,12 +23,12 @@ import java.util.HashMap;
*/ */
class MassObjectHandler extends PositionDependentHandler<MassObject> { class MassObjectHandler extends PositionDependentHandler<MassObject> {
/** /**
* The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point * 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 * 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 * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that Rocksim
* Rocksim does not expose the length of a mass object in the UI and actually treats mass objects as point objects - * does not expose the length of a mass object in the UI and actually treats mass objects as point objects - not 3
* not 3 or even 2 dimensional. * or even 2 dimensional.
*/ */
public static final int MASS_LEN_FUDGE_FACTOR = 100; public static final int MASS_LEN_FUDGE_FACTOR = 100;
@ -51,12 +53,12 @@ class MassObjectHandler extends PositionDependentHandler<MassObject> {
private int typeCode = 0; private int typeCode = 0;
/** /**
* Constructor. * Constructor. l
*l *
* @param c the parent component * @param c the parent component
* @param warnings the warning set * @param warnings the warning set
* *
* @throws IllegalArgumentException thrown if <code>c</code> is null * @throws IllegalArgumentException thrown if <code>c</code> is null
*/ */
public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException { public MassObjectHandler(RocketComponent c, WarningSet warnings) throws IllegalArgumentException {
if (c == null) { if (c == null) {
@ -89,7 +91,7 @@ class MassObjectHandler extends PositionDependentHandler<MassObject> {
//length) and because Rocksim sets the CG of the mass object to really be relative to the front of //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. //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. //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)) { if (RocksimCommonConstants.TYPE_CODE.equals(element)) {
typeCode = Integer.parseInt(content); typeCode = Integer.parseInt(content);
@ -104,32 +106,68 @@ class MassObjectHandler extends PositionDependentHandler<MassObject> {
} }
@Override @Override
public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException { public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws
if (typeCode == 0) { //General Mass Object SAXException {
if (inferAsShockCord(typeCode, warnings)) { //Shock Cord
mapMassObjectAsShockCord(element, attributes, content, warnings);
}
else { // typeCode == 0 General Mass Object
if (isCompatible(parent, MassComponent.class, warnings)) { if (isCompatible(parent, MassComponent.class, warnings)) {
parent.addChild(mass); parent.addChild(mass);
} }
super.endHandler(element, attributes, content, warnings); 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()); /**
* 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);
}
cord.setRadialDirection(mass.getRadialDirection()); /**
cord.setRadialPosition(mass.getRadialPosition()); * If it appears that the mass object is a shock cord, then create an OR shock cord instance.
cord.setRadius(mass.getRadius()); *
* @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());
//Rocksim does not distinguish between total length of the cord and the packed length. Fudge the setOverride(cord, mass.isMassOverridden(), mass.getOverrideMass(), mass.getOverrideCGX());
//packed length and set the real length.
cord.setCordLength(mass.getLength()); cord.setRadialDirection(mass.getRadialDirection());
cord.setLength(cord.getCordLength()/MASS_LEN_FUDGE_FACTOR); 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());
} }
} }
@ -154,7 +192,8 @@ class MassObjectHandler extends PositionDependentHandler<MassObject> {
} }
/** /**
* 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 * @return LINE
*/ */