preset component framework
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Thu, 18 Aug 2011 05:28:08 +0000 (05:28 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Thu, 18 Aug 2011 05:28:08 +0000 (05:28 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@159 180e2498-e6e9-4542-8430-84ac67f01cd8

src/net/sf/openrocket/motor/MotorDigest.java
src/net/sf/openrocket/preset/ExternalComponentPreset.java [new file with mode: 0644]
src/net/sf/openrocket/preset/RocketComponentPreset.java [new file with mode: 0644]
src/net/sf/openrocket/rocketcomponent/ComponentChangeEvent.java
src/net/sf/openrocket/rocketcomponent/ExternalComponent.java
src/net/sf/openrocket/rocketcomponent/RocketComponent.java

index e7fb9f5fd7b31261ff7bc90d6cece110b25f3388..cec3cbdc6082a37c7a43354815d7afa5effa9e12 100644 (file)
@@ -7,6 +7,17 @@ import java.security.NoSuchAlgorithmException;
 import net.sf.openrocket.util.Coordinate;
 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 {
        
        private static final double EPSILON = 0.00000000001;
@@ -24,22 +35,25 @@ public class MotorDigest {
                CG_PER_TIME(4, 1000),
                /** Thrust force per time (in mN) */
                FORCE_PER_TIME(5, 1000);
-
+               
                private final int order;
                private final int multiplier;
+               
                DataType(int order, int multiplier) {
                        this.order = order;
                        this.multiplier = multiplier;
                }
+               
                public int getOrder() {
                        return order;
                }
+               
                public int getMultiplier() {
                        return multiplier;
                }
        }
        
-
+       
        private final MessageDigest digest;
        private boolean used = false;
        private int lastOrder = -1;
@@ -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
                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);
                }
                lastOrder = type.getOrder();
@@ -70,17 +84,17 @@ public class MotorDigest {
                digest.update(bytes(values.length));
                
                // Digest the values
-               for (int v: values) {
+               for (int v : values) {
                        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];
-               for (int i=0; i<values.length; i++) {
+               for (int i = 0; i < values.length; i++) {
                        double v = values[i];
                        v = next(v);
                        v *= multiplier;
@@ -90,7 +104,7 @@ public class MotorDigest {
                update(type, intValues);
        }
        
-       public void update(DataType type, double ... values) {
+       public void update(DataType type, double... values) {
                update(type, type.getMultiplier(), values);
        }
        
@@ -107,16 +121,15 @@ public class MotorDigest {
                byte[] result = digest.digest();
                return TextUtil.hexString(result);
        }
-
        
        
+
        private byte[] bytes(int value) {
                return new byte[] {
-                               (byte) ((value>>>24) & 0xFF), (byte) ((value>>>16) & 0xFF),
-                               (byte) ((value>>>8) & 0xFF), (byte) (value & 0xFF) 
-               };
+                               (byte) ((value >>> 24) & 0xFF), (byte) ((value >>> 16) & 0xFF),
+                               (byte) ((value >>> 8) & 0xFF), (byte) (value & 0xFF) };
        }
-
+       
        
        /**
         * Digest the contents of a thrust curve motor.  The result is a string uniquely
@@ -126,7 +139,7 @@ public class MotorDigest {
         * @return              the digest
         */
        public static String digestMotor(ThrustCurveMotor m) {
-
+               
                // Create the motor digest from data available in RASP files
                MotorDigest motorDigest = new MotorDigest();
                motorDigest.update(DataType.TIME_ARRAY, m.getTimePoints());
@@ -134,7 +147,7 @@ public class MotorDigest {
                Coordinate[] cg = m.getCGPoints();
                double[] cgx = 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;
                        mass[i] = cg[i].weight;
                }
@@ -161,7 +174,7 @@ public class MotorDigest {
                } catch (UnsupportedEncodingException e) {
                        throw new IllegalStateException("UTF-8 encoding not supported by JRE", e);
                }
-
+               
                return TextUtil.hexString(digest.digest());
        }
        
diff --git a/src/net/sf/openrocket/preset/ExternalComponentPreset.java b/src/net/sf/openrocket/preset/ExternalComponentPreset.java
new file mode 100644 (file)
index 0000000..da37040
--- /dev/null
@@ -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;
+       }
+       
+}
diff --git a/src/net/sf/openrocket/preset/RocketComponentPreset.java b/src/net/sf/openrocket/preset/RocketComponentPreset.java
new file mode 100644 (file)
index 0000000..34940b0
--- /dev/null
@@ -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;
+       }
+       
+}
index 58d3297b5cee3b1be392bf110e62b93d5fe8f259..60123e87656c730040f2e3dfdec15438fd3f41b4 100644 (file)
@@ -4,8 +4,8 @@ import javax.swing.event.ChangeEvent;
 
 public class ComponentChangeEvent extends ChangeEvent {
        private static final long serialVersionUID = 1L;
-
        
+
        /** A change that does not affect simulation results in any way (name, color, etc.) */
        public static final int NONFUNCTIONAL_CHANGE = 1;
        /** A change that affects the mass properties of the rocket */
@@ -13,15 +13,15 @@ public class ComponentChangeEvent extends ChangeEvent {
        /** A change that affects the aerodynamic properties of the rocket */
        public static final int AERODYNAMIC_CHANGE = 4;
        /** 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 */
        public static final int TREE_CHANGE = 8;
        /** A change caused by undo/redo. */
        public static final int UNDO_CHANGE = 16;
        /** A change in the motor configurations or names */
        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;
        
        /** A bit-field that contains all possible change types. */
@@ -29,7 +29,7 @@ public class ComponentChangeEvent extends ChangeEvent {
        
        private final int type;
        
-
+       
        public ComponentChangeEvent(RocketComponent component, int type) {
                super(component);
                if (type == 0) {
@@ -46,8 +46,8 @@ public class ComponentChangeEvent extends ChangeEvent {
        public RocketComponent getSource() {
                return (RocketComponent) super.getSource();
        }
-
-
+       
+       
        public boolean isAerodynamicChange() {
                return (type & AERODYNAMIC_CHANGE) != 0;
        }
@@ -71,7 +71,7 @@ public class ComponentChangeEvent extends ChangeEvent {
        public boolean isMotorChange() {
                return (type & MOTOR_CHANGE) != 0;
        }
-
+       
        public int getType() {
                return type;
        }
index 64c1a50ec095509c5348f648ace514f3677c253a..23cea24074368f56983fd6fa6e20a2e8b8f122ab 100644 (file)
@@ -4,6 +4,9 @@ import java.util.List;
 
 import net.sf.openrocket.l10n.Translator;
 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.unit.UnitGroup;
 import net.sf.openrocket.util.Prefs;
@@ -111,6 +114,7 @@ public abstract class ExternalComponent extends RocketComponent {
                if (material.equals(mat))
                        return;
                material = mat;
+               clearPreset();
                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
        protected List<RocketComponent> copyFrom(RocketComponent c) {
                ExternalComponent src = (ExternalComponent) c;
index f9b28ea381d048f5dbbda7d516ca6f6955dad81b..b0c7b47e32c97c2926825af079e08a51af4dbfa0 100644 (file)
@@ -12,6 +12,7 @@ import javax.swing.event.ChangeListener;
 
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.preset.RocketComponentPreset;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.ArrayList;
 import net.sf.openrocket.util.BugException;
@@ -123,6 +124,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        // Unique ID of the component
        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)}.
         */
@@ -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.
         *