preset component framework
This commit is contained in:
parent
9efabf5fb2
commit
50e8b972a3
@ -7,6 +7,17 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
import net.sf.openrocket.util.TextUtil;
|
import net.sf.openrocket.util.TextUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that generated a "digest" of a motor. A digest is a string value that
|
||||||
|
* uniquely identifies a motor (like a hash code or checksum). Two motors that have
|
||||||
|
* the same digest behave similarly with a very high probability. The digest can
|
||||||
|
* therefore be used to identify motors that otherwise have the same specifications.
|
||||||
|
* <p>
|
||||||
|
* The digest only uses a limited amount of precision, so that rounding errors won't
|
||||||
|
* cause differing digest results.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
public class MotorDigest {
|
public class MotorDigest {
|
||||||
|
|
||||||
private static final double EPSILON = 0.00000000001;
|
private static final double EPSILON = 0.00000000001;
|
||||||
@ -27,13 +38,16 @@ public class MotorDigest {
|
|||||||
|
|
||||||
private final int order;
|
private final int order;
|
||||||
private final int multiplier;
|
private final int multiplier;
|
||||||
|
|
||||||
DataType(int order, int multiplier) {
|
DataType(int order, int multiplier) {
|
||||||
this.order = order;
|
this.order = order;
|
||||||
this.multiplier = multiplier;
|
this.multiplier = multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMultiplier() {
|
public int getMultiplier() {
|
||||||
return multiplier;
|
return multiplier;
|
||||||
}
|
}
|
||||||
@ -54,11 +68,11 @@ public class MotorDigest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void update(DataType type, int ... values) {
|
public void update(DataType type, int... values) {
|
||||||
|
|
||||||
// Check for correct order
|
// Check for correct order
|
||||||
if (lastOrder >= type.getOrder()) {
|
if (lastOrder >= type.getOrder()) {
|
||||||
throw new IllegalArgumentException("Called with type="+type+" order="+type.getOrder()+
|
throw new IllegalArgumentException("Called with type=" + type + " order=" + type.getOrder() +
|
||||||
" while lastOrder=" + lastOrder);
|
" while lastOrder=" + lastOrder);
|
||||||
}
|
}
|
||||||
lastOrder = type.getOrder();
|
lastOrder = type.getOrder();
|
||||||
@ -70,17 +84,17 @@ public class MotorDigest {
|
|||||||
digest.update(bytes(values.length));
|
digest.update(bytes(values.length));
|
||||||
|
|
||||||
// Digest the values
|
// Digest the values
|
||||||
for (int v: values) {
|
for (int v : values) {
|
||||||
digest.update(bytes(v));
|
digest.update(bytes(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void update(DataType type, int multiplier, double ... values) {
|
private void update(DataType type, int multiplier, double... values) {
|
||||||
|
|
||||||
int[] intValues = new int[values.length];
|
int[] intValues = new int[values.length];
|
||||||
for (int i=0; i<values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
double v = values[i];
|
double v = values[i];
|
||||||
v = next(v);
|
v = next(v);
|
||||||
v *= multiplier;
|
v *= multiplier;
|
||||||
@ -90,7 +104,7 @@ public class MotorDigest {
|
|||||||
update(type, intValues);
|
update(type, intValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(DataType type, double ... values) {
|
public void update(DataType type, double... values) {
|
||||||
update(type, type.getMultiplier(), values);
|
update(type, type.getMultiplier(), values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +126,8 @@ public class MotorDigest {
|
|||||||
|
|
||||||
private byte[] bytes(int value) {
|
private byte[] bytes(int value) {
|
||||||
return new byte[] {
|
return new byte[] {
|
||||||
(byte) ((value>>>24) & 0xFF), (byte) ((value>>>16) & 0xFF),
|
(byte) ((value >>> 24) & 0xFF), (byte) ((value >>> 16) & 0xFF),
|
||||||
(byte) ((value>>>8) & 0xFF), (byte) (value & 0xFF)
|
(byte) ((value >>> 8) & 0xFF), (byte) (value & 0xFF) };
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,7 +147,7 @@ public class MotorDigest {
|
|||||||
Coordinate[] cg = m.getCGPoints();
|
Coordinate[] cg = m.getCGPoints();
|
||||||
double[] cgx = new double[cg.length];
|
double[] cgx = new double[cg.length];
|
||||||
double[] mass = new double[cg.length];
|
double[] mass = new double[cg.length];
|
||||||
for (int i=0; i<cg.length; i++) {
|
for (int i = 0; i < cg.length; i++) {
|
||||||
cgx[i] = cg[i].x;
|
cgx[i] = cg[i].x;
|
||||||
mass[i] = cg[i].weight;
|
mass[i] = cg[i].weight;
|
||||||
}
|
}
|
||||||
|
29
src/net/sf/openrocket/preset/ExternalComponentPreset.java
Normal file
29
src/net/sf/openrocket/preset/ExternalComponentPreset.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package net.sf.openrocket.preset;
|
||||||
|
|
||||||
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
|
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||||
|
|
||||||
|
public class ExternalComponentPreset extends RocketComponentPreset {
|
||||||
|
|
||||||
|
private final double mass;
|
||||||
|
private final String materialName;
|
||||||
|
|
||||||
|
public ExternalComponentPreset(Class<? extends RocketComponent> componentClass, Manufacturer manufacturer, String partName,
|
||||||
|
String partNo, String partDescription, double mass, String materialName) {
|
||||||
|
super(componentClass, manufacturer, partName, partNo, partDescription);
|
||||||
|
|
||||||
|
this.materialName = materialName;
|
||||||
|
this.mass = mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getMaterialName() {
|
||||||
|
return materialName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double getMass() {
|
||||||
|
return mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
src/net/sf/openrocket/preset/RocketComponentPreset.java
Normal file
68
src/net/sf/openrocket/preset/RocketComponentPreset.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package net.sf.openrocket.preset;
|
||||||
|
|
||||||
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
|
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model for a preset component.
|
||||||
|
* <p>
|
||||||
|
* A preset component contains a component class type, manufacturer information,
|
||||||
|
* part information, and getter methods for various properties of the component.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
|
public abstract class RocketComponentPreset {
|
||||||
|
|
||||||
|
private final Class<? extends RocketComponent> componentClass;
|
||||||
|
private final Manufacturer manufacturer;
|
||||||
|
private final String partName;
|
||||||
|
private final String partNo;
|
||||||
|
private final String partDescription;
|
||||||
|
|
||||||
|
|
||||||
|
public RocketComponentPreset(Class<? extends RocketComponent> componentClass, Manufacturer manufacturer,
|
||||||
|
String partName, String partNo, String partDescription) {
|
||||||
|
this.componentClass = componentClass;
|
||||||
|
this.manufacturer = manufacturer;
|
||||||
|
this.partName = partName;
|
||||||
|
this.partNo = partNo;
|
||||||
|
this.partDescription = partDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the component class that this preset defines.
|
||||||
|
*/
|
||||||
|
public Class<? extends RocketComponent> getComponentClass() {
|
||||||
|
return componentClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the manufacturer of this preset component.
|
||||||
|
*/
|
||||||
|
public Manufacturer getManufacturer() {
|
||||||
|
return manufacturer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the part name. This is a short, human-readable name of the part.
|
||||||
|
*/
|
||||||
|
public String getPartName() {
|
||||||
|
return partName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the part number. This is the part identifier (e.g. "BT-50").
|
||||||
|
*/
|
||||||
|
public String getPartNo() {
|
||||||
|
return partNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the part description. This is a longer description of the component.
|
||||||
|
*/
|
||||||
|
public String getPartDescription() {
|
||||||
|
return partDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,7 +13,7 @@ public class ComponentChangeEvent extends ChangeEvent {
|
|||||||
/** A change that affects the aerodynamic properties of the rocket */
|
/** A change that affects the aerodynamic properties of the rocket */
|
||||||
public static final int AERODYNAMIC_CHANGE = 4;
|
public static final int AERODYNAMIC_CHANGE = 4;
|
||||||
/** A change that affects the mass and aerodynamic properties of the rocket */
|
/** A change that affects the mass and aerodynamic properties of the rocket */
|
||||||
public static final int BOTH_CHANGE = MASS_CHANGE|AERODYNAMIC_CHANGE; // Mass & Aerodynamic
|
public static final int BOTH_CHANGE = MASS_CHANGE | AERODYNAMIC_CHANGE; // Mass & Aerodynamic
|
||||||
|
|
||||||
/** A change that affects the rocket tree structure */
|
/** A change that affects the rocket tree structure */
|
||||||
public static final int TREE_CHANGE = 8;
|
public static final int TREE_CHANGE = 8;
|
||||||
@ -21,7 +21,7 @@ public class ComponentChangeEvent extends ChangeEvent {
|
|||||||
public static final int UNDO_CHANGE = 16;
|
public static final int UNDO_CHANGE = 16;
|
||||||
/** A change in the motor configurations or names */
|
/** A change in the motor configurations or names */
|
||||||
public static final int MOTOR_CHANGE = 32;
|
public static final int MOTOR_CHANGE = 32;
|
||||||
/** A change in the events occurring during flight. */
|
/** A change that affects the events occurring during flight. */
|
||||||
public static final int EVENT_CHANGE = 64;
|
public static final int EVENT_CHANGE = 64;
|
||||||
|
|
||||||
/** A bit-field that contains all possible change types. */
|
/** A bit-field that contains all possible change types. */
|
||||||
|
@ -4,6 +4,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import net.sf.openrocket.l10n.Translator;
|
import net.sf.openrocket.l10n.Translator;
|
||||||
import net.sf.openrocket.material.Material;
|
import net.sf.openrocket.material.Material;
|
||||||
|
import net.sf.openrocket.material.Material.Type;
|
||||||
|
import net.sf.openrocket.preset.ExternalComponentPreset;
|
||||||
|
import net.sf.openrocket.preset.RocketComponentPreset;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.Prefs;
|
import net.sf.openrocket.util.Prefs;
|
||||||
@ -111,6 +114,7 @@ public abstract class ExternalComponent extends RocketComponent {
|
|||||||
if (material.equals(mat))
|
if (material.equals(mat))
|
||||||
return;
|
return;
|
||||||
material = mat;
|
material = mat;
|
||||||
|
clearPreset();
|
||||||
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
|
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +130,27 @@ public abstract class ExternalComponent extends RocketComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadFromPreset(RocketComponentPreset preset) {
|
||||||
|
super.loadFromPreset(preset);
|
||||||
|
|
||||||
|
ExternalComponentPreset p = (ExternalComponentPreset) preset;
|
||||||
|
String materialName = p.getMaterialName();
|
||||||
|
double mass = p.getMass();
|
||||||
|
|
||||||
|
double volume = getComponentVolume();
|
||||||
|
double density;
|
||||||
|
if (volume > 0.00001) {
|
||||||
|
density = mass / volume;
|
||||||
|
} else {
|
||||||
|
density = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material mat = Material.newMaterial(Type.BULK, materialName, density, true);
|
||||||
|
setMaterial(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<RocketComponent> copyFrom(RocketComponent c) {
|
protected List<RocketComponent> copyFrom(RocketComponent c) {
|
||||||
ExternalComponent src = (ExternalComponent) c;
|
ExternalComponent src = (ExternalComponent) c;
|
||||||
|
@ -12,6 +12,7 @@ import javax.swing.event.ChangeListener;
|
|||||||
|
|
||||||
import net.sf.openrocket.l10n.Translator;
|
import net.sf.openrocket.l10n.Translator;
|
||||||
import net.sf.openrocket.logging.LogHelper;
|
import net.sf.openrocket.logging.LogHelper;
|
||||||
|
import net.sf.openrocket.preset.RocketComponentPreset;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.util.ArrayList;
|
import net.sf.openrocket.util.ArrayList;
|
||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
@ -123,6 +124,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
|||||||
// Unique ID of the component
|
// Unique ID of the component
|
||||||
private String id = null;
|
private String id = null;
|
||||||
|
|
||||||
|
// Preset component this component is based upon
|
||||||
|
private RocketComponentPreset presetComponent = null;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
|
* Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
|
||||||
*/
|
*/
|
||||||
@ -659,6 +664,89 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the preset component that this component is based upon.
|
||||||
|
*
|
||||||
|
* @return the preset component, or <code>null</code> if this is not based on a preset.
|
||||||
|
*/
|
||||||
|
public final RocketComponentPreset getPresetComponent() {
|
||||||
|
return presetComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the preset component this component is based upon and load all of the
|
||||||
|
* preset values.
|
||||||
|
*
|
||||||
|
* @param preset the preset component to load, or <code>null</code> to clear the preset.
|
||||||
|
*/
|
||||||
|
public final void loadPreset(RocketComponentPreset preset) {
|
||||||
|
if (presetComponent == preset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset == null) {
|
||||||
|
clearPreset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset.getComponentClass() != this.getClass()) {
|
||||||
|
throw new IllegalArgumentException("Attempting to load preset of type " + preset.getComponentClass()
|
||||||
|
+ " into component of type " + this.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
RocketComponent root = getRoot();
|
||||||
|
final Rocket rocket;
|
||||||
|
if (root instanceof Rocket) {
|
||||||
|
rocket = (Rocket) root;
|
||||||
|
} else {
|
||||||
|
rocket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (rocket != null) {
|
||||||
|
rocket.freeze();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFromPreset(preset);
|
||||||
|
|
||||||
|
this.presetComponent = preset;
|
||||||
|
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (rocket != null) {
|
||||||
|
rocket.thaw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load component properties from the specified preset. The preset is guaranteed
|
||||||
|
* to be of the correct type.
|
||||||
|
* <p>
|
||||||
|
* This method should fire the appropriate events related to the changes. The rocket
|
||||||
|
* is frozen by the caller, so the events will be automatically combined.
|
||||||
|
*
|
||||||
|
* @param preset the preset to load from
|
||||||
|
*/
|
||||||
|
protected void loadFromPreset(RocketComponentPreset preset) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the current component preset. This does not affect the component properties
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public final void clearPreset() {
|
||||||
|
if (presetComponent == null)
|
||||||
|
return;
|
||||||
|
presetComponent = null;
|
||||||
|
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the unique ID of the component.
|
* Returns the unique ID of the component.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user