diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 22ac8d20f..b6c138a29 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -74,11 +74,16 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { new LinkedHashMap(); // Add all components to the map - for (RocketComponent c : configuration) { - f = new AerodynamicForces(); - f.setComponent(c); + for (RocketComponent component : configuration.getActiveComponents()) { - map.put(c, f); + // Skip non-aerodynamic components + if (!component.isAerodynamic()) + continue; + + f = new AerodynamicForces(); + f.setComponent(component); + + map.put(component, f); } @@ -172,7 +177,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { if (calcMap == null) buildCalcMap(configuration); - for (RocketComponent component : configuration) { + for (RocketComponent component : configuration.getActiveComponents()) { // Skip non-aerodynamic components if (!component.isAerodynamic()) @@ -367,7 +372,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { double[] roughnessLimited = new double[Finish.values().length]; Arrays.fill(roughnessLimited, Double.NaN); - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { // Consider only SymmetricComponents and FinSets: if (!(c instanceof SymmetricComponent) && @@ -469,7 +474,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { base = calculateBaseCD(conditions.getMach()); total = 0; - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (!c.isAerodynamic()) continue; @@ -517,7 +522,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { base = calculateBaseCD(conditions.getMach()); total = 0; - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (!(c instanceof SymmetricComponent)) continue; @@ -646,7 +651,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { cacheLength = 0; cacheDiameter = 0; - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (c instanceof SymmetricComponent) { SymmetricComponent s = (SymmetricComponent) c; area += s.getComponentPlanformArea(); @@ -665,7 +670,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { // Fins // TODO: LOW: This could be optimized a lot... - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (c instanceof FinSet) { FinSet f = (FinSet) c; mul += 0.6 * Math.min(f.getFinCount(), 4) * f.getFinArea() * diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index aea41c406..0cdd9bf7e 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -2,7 +2,6 @@ package net.sf.openrocket.document; import java.util.EventListener; import java.util.EventObject; -import java.util.Iterator; import java.util.List; import net.sf.openrocket.aerodynamics.AerodynamicCalculator; @@ -10,14 +9,9 @@ import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.masscalc.MassCalculator; -import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; -import net.sf.openrocket.rocketcomponent.MotorConfiguration; -import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.BasicEventSimulationEngine; import net.sf.openrocket.simulation.DefaultSimulationOptionFactory; import net.sf.openrocket.simulation.FlightData; @@ -266,28 +260,14 @@ public class Simulation implements ChangeSource, Cloneable { } } + Configuration c = new Configuration(this.getRocket()); + MotorInstanceConfiguration motors = new MotorInstanceConfiguration(c); + c.setFlightConfigurationID(options.getMotorConfigurationID()); //Make sure this simulation has motors. - Configuration c = new Configuration(this.getRocket()); - MotorInstanceConfiguration motors = new MotorInstanceConfiguration(); - c.setFlightConfigurationID(options.getMotorConfigurationID()); - final String flightConfigId = c.getFlightConfigurationID(); - - Iterator iterator = c.motorIterator(); - boolean no_motors = true; - - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - RocketComponent component = (RocketComponent) mount; - MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId); - IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId); - Motor motor = motorConfig.getMotor(); - if (motor != null) - no_motors = false; - } - - if (no_motors) + if (0 == motors.getMotorCount()) { status = Status.CANT_RUN; + } return status; } diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index e402dab7d..7bc1227dd 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -4,16 +4,13 @@ import static net.sf.openrocket.util.MathUtil.pow2; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.motor.MotorId; import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.MassData; import net.sf.openrocket.util.Coordinate; @@ -80,35 +77,21 @@ public class MassCalculator implements Monitorable { checkCache(configuration); calculateStageCache(configuration); - Coordinate totalCG = null; + Coordinate dryCG = null; // Stage contribution - for (int stage : configuration.getActiveStages()) { - totalCG = cgCache[stage].average(totalCG); + for (AxialStage stage : configuration.getActiveStages()) { + int stageNumber = stage.getStageNumber(); + dryCG = cgCache[stageNumber].average(dryCG); } - if (totalCG == null) - totalCG = Coordinate.NUL; + if (dryCG == null) + dryCG = Coordinate.NUL; - // Add motor CGs - String motorId = configuration.getFlightConfigurationID(); - if (type != MassCalcType.NO_MOTORS && motorId != null) { - Iterator iterator = configuration.motorIterator(); - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - RocketComponent comp = (RocketComponent) mount; - Motor motor = mount.getMotorConfiguration().get(motorId).getMotor(); - if (motor == null) - continue; - - Coordinate motorCG = type.getCG(motor).add(mount.getMotorPosition(motorId)); - Coordinate[] cgs = comp.toAbsolute(motorCG); - for (Coordinate cg : cgs) { - totalCG = totalCG.average(cg); - } - } - } + MotorInstanceConfiguration motorConfig = new MotorInstanceConfiguration(configuration); + Coordinate motorCG = getMotorCG(configuration, motorConfig, MassCalcType.LAUNCH_MASS); + Coordinate totalCG = dryCG.average(motorCG); return totalCG; } @@ -123,23 +106,27 @@ public class MassCalculator implements Monitorable { checkCache(configuration); calculateStageCache(configuration); - Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS); + Coordinate dryCG = getCG(configuration, MassCalcType.NO_MOTORS); + Coordinate motorCG = getMotorCG(configuration, motors, MassCalcType.LAUNCH_MASS); + Coordinate totalCG = dryCG.average(motorCG); + return totalCG; + } + + public Coordinate getMotorCG(Configuration config, MotorInstanceConfiguration motors, MassCalcType type) { + Coordinate motorCG = Coordinate.ZERO; // Add motor CGs if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); - if (configuration.isStageActive(stage)) { - - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); - Coordinate cg = motor.getCG().add(position); - totalCG = totalCG.average(cg); - + for (MotorInstance inst : config.getActiveMotors(motors)) { + int stage = ((RocketComponent) inst.getMount()).getStageNumber(); + if (config.isStageActive(stage)) { + Coordinate position = inst.getPosition(); + Coordinate curCG = type.getCG(inst.getMotor()).add(position); + motorCG = motorCG.average(curCG); } } } - return totalCG; + return motorCG; } /** @@ -158,21 +145,21 @@ public class MassCalculator implements Monitorable { double totalInertia = 0; // Stages - for (int stage : configuration.getActiveStages()) { - Coordinate stageCG = cgCache[stage]; + for (AxialStage stage : configuration.getActiveStages()) { + int stageNumber = stage.getStageNumber(); + Coordinate stageCG = cgCache[stageNumber]; - totalInertia += (longitudinalInertiaCache[stage] + + totalInertia += (longitudinalInertiaCache[stageNumber] + stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x)); } // Motors if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); + for (MotorInstance motor : configuration.getActiveMotors(motors)) { + int stage = ((RocketComponent) motor.getMount()).getStageNumber(); if (configuration.isStageActive(stage)) { - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); + Coordinate position = motor.getPosition(); Coordinate cg = motor.getCG().add(position); double inertia = motor.getLongitudinalInertia(); @@ -200,10 +187,11 @@ public class MassCalculator implements Monitorable { double totalInertia = 0; // Stages - for (int stage : configuration.getActiveStages()) { - Coordinate stageCG = cgCache[stage]; + for (AxialStage stage : configuration.getActiveStages()) { + int stageNumber = stage.getStageNumber(); + Coordinate stageCG = cgCache[stageNumber]; - totalInertia += (rotationalInertiaCache[stage] + + totalInertia += (rotationalInertiaCache[stageNumber] + stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) + MathUtil.pow2(stageCG.z - totalCG.z))); } @@ -211,11 +199,10 @@ public class MassCalculator implements Monitorable { // Motors if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); + for (MotorInstance motor : configuration.getActiveMotors(motors)) { + int stage = ((RocketComponent) motor.getMount()).getStageNumber(); if (configuration.isStageActive(stage)) { - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); + Coordinate position = motor.getPosition(); Coordinate cg = motor.getCG().add(position); double inertia = motor.getRotationalInertia(); @@ -241,9 +228,8 @@ public class MassCalculator implements Monitorable { // add up the masses of all motors in the rocket if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - MotorInstance motor = motors.getMotorInstance(id); - mass = mass + motor.getCG().weight - motor.getParentMotor().getEmptyCG().weight; + for (MotorInstance motor : configuration.getActiveMotors(motors)) { + mass = mass + motor.getCG().weight - motor.getMotor().getEmptyCG().weight; } } return mass; @@ -266,13 +252,13 @@ public class MassCalculator implements Monitorable { Map map = new HashMap(); - for (RocketComponent c : configuration) { - Coordinate[] cgs = c.toAbsolute(c.getCG()); + for (RocketComponent comp : configuration.getActiveComponents()) { + Coordinate[] cgs = comp.toAbsolute(comp.getCG()); Coordinate totalCG = Coordinate.NUL; for (Coordinate cg : cgs) { totalCG = totalCG.average(cg); } - map.put(c, totalCG); + map.put(comp, totalCG); } map.put(configuration.getRocket(), getCG(configuration, type)); @@ -284,7 +270,8 @@ public class MassCalculator implements Monitorable { private void calculateStageCache(Configuration config) { if (cgCache == null) { - ArrayList stageList = config.getRocket().getStageList(); + ArrayList stageList = new ArrayList(); + stageList.addAll(config.getRocket().getStageList()); int stageCount = stageList.size(); cgCache = new Coordinate[stageCount]; diff --git a/core/src/net/sf/openrocket/motor/MotorId.java b/core/src/net/sf/openrocket/motor/MotorId.java index 126fff1c4..11d0ab1d7 100644 --- a/core/src/net/sf/openrocket/motor/MotorId.java +++ b/core/src/net/sf/openrocket/motor/MotorId.java @@ -8,10 +8,18 @@ package net.sf.openrocket.motor; * @author Sampo Niskanen */ public final class MotorId { - + private final String componentId; private final int number; + private final String COMPONENT_ERROR_ID = "Error Motor Instance"; + private final int ERROR_NUMBER = -1; + public final static MotorId ERROR_ID = new MotorId(); + + public MotorId() { + this.componentId = COMPONENT_ERROR_ID; + this.number = ERROR_NUMBER; + } /** * Sole constructor. @@ -20,7 +28,6 @@ public final class MotorId { * @param number a positive motor doun5 number */ public MotorId(String componentId, int number) { - super(); if (componentId == null) { throw new IllegalArgumentException("Component ID was null"); @@ -52,7 +59,7 @@ public final class MotorId { if (!(o instanceof MotorId)) return false; - MotorId other = (MotorId)o; + MotorId other = (MotorId) o; // Comparison with == ok since string is intern()'ed return this.componentId == other.componentId && this.number == other.number; } diff --git a/core/src/net/sf/openrocket/motor/MotorInstance.java b/core/src/net/sf/openrocket/motor/MotorInstance.java index ab474a8e9..a4908a2ad 100644 --- a/core/src/net/sf/openrocket/motor/MotorInstance.java +++ b/core/src/net/sf/openrocket/motor/MotorInstance.java @@ -1,11 +1,124 @@ package net.sf.openrocket.motor; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent; +import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Monitorable; -public interface MotorInstance extends Cloneable, Monitorable { - +public abstract class MotorInstance implements Cloneable, Monitorable { + + protected MotorId id = null; + protected Motor parentMotor = null; + protected MotorMount mount = null; + protected IgnitionConfiguration.IgnitionEvent ignitionEvent = null; + protected double ejectionDelay = 0.0; + protected double ignitionDelay = 0.0; + protected Coordinate position = null; + protected double ignitionTime = 0.0; + + protected int modID = 0; + + public MotorInstance() { + + modID++; + } + + /** + * Add a motor instance to this configuration. The motor is placed at + * the specified position and with an infinite ignition time (never ignited). + * + * @param id the ID of this motor instance. + * @param motor the motor instance. + * @param mount the motor mount containing this motor + * @param ignitionEvent the ignition event for the motor + * @param ignitionDelay the ignition delay for the motor + * @param position the position of the motor in absolute coordinates. + * @throws IllegalArgumentException if a motor with the specified ID already exists. + */ + public MotorInstance(MotorId _id, Motor _motor, MotorMount _mount, double _ejectionDelay, + IgnitionEvent _ignitionEvent, double _ignitionDelay, Coordinate _position) { + + this.id = _id; + this.parentMotor = _motor; + this.mount = _mount; + this.ejectionDelay = _ejectionDelay; + this.ignitionEvent = _ignitionEvent; + this.ignitionDelay = _ignitionDelay; + this.position = _position; + this.ignitionTime = Double.POSITIVE_INFINITY; + + modID++; + } + + public MotorId getID() { + return this.id; + } + + public void setID(final MotorId _id) { + this.id = _id; + } + + public double getEjectionDelay() { + return this.ejectionDelay; + } + + public void setEjectionDelay(final double newDelay) { + this.ejectionDelay = newDelay; + } + + @Override + public int getModID() { + return this.modID; + } + + public Motor getMotor() { + return this.parentMotor; + } + + public MotorMount getMount() { + return this.mount; + } + + public void setMount(final MotorMount _mount) { + this.mount = _mount; + } + + public Coordinate getPosition() { + return this.position; + } + + public void setPosition(Coordinate _position) { + this.position = _position; + modID++; + } + + public double getIgnitionTime() { + return this.ignitionTime; + } + + public void setIgnitionTime(double _time) { + this.ignitionTime = _time; + modID++; + } + + public double getIgnitionDelay() { + return ignitionDelay; + } + + public void setIgnitionDelay(final double _delay) { + this.ignitionDelay = _delay; + } + + public IgnitionEvent getIgnitionEvent() { + return ignitionEvent; + } + + public void setIgnitionEvent(final IgnitionEvent _event) { + this.ignitionEvent = _event; + } + /** * Step the motor instance forward in time. * @@ -13,51 +126,49 @@ public interface MotorInstance extends Cloneable, Monitorable { * @param acceleration the average acceleration during the step. * @param cond the average atmospheric conditions during the step. */ - public void step(double time, double acceleration, AtmosphericConditions cond); - - + public abstract void step(double time, double acceleration, AtmosphericConditions cond); + + /** * Return the time to which this motor has been stepped. * @return the current step time. */ - public double getTime(); + public abstract double getTime(); /** * Return the average thrust during the last step. */ - public double getThrust(); + public abstract double getThrust(); /** * Return the average CG location during the last step. */ - public Coordinate getCG(); + public abstract Coordinate getCG(); /** * Return the average longitudinal moment of inertia during the last step. * This is the actual inertia, not the unit inertia! */ - public double getLongitudinalInertia(); + public abstract double getLongitudinalInertia(); /** * Return the average rotational moment of inertia during the last step. * This is the actual inertia, not the unit inertia! */ - public double getRotationalInertia(); - + public abstract double getRotationalInertia(); + /** * Return whether this motor still produces thrust. If this method returns false * the motor has burnt out, and will not produce any significant thrust anymore. */ - public boolean isActive(); - + public abstract boolean isActive(); + /** * Create a new instance of this motor instance. The state of the motor is * identical to this instance and can be used independently from this one. */ - public MotorInstance clone(); - - - public Motor getParentMotor(); + @Override + public abstract MotorInstance clone(); } diff --git a/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java b/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java index 8c23ece43..85a95e30b 100644 --- a/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java +++ b/core/src/net/sf/openrocket/motor/MotorInstanceConfiguration.java @@ -1,13 +1,16 @@ package net.sf.openrocket.motor; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; -import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Monitorable; @@ -17,21 +20,55 @@ import net.sf.openrocket.util.Monitorable; * * @author Sampo Niskanen */ -public final class MotorInstanceConfiguration implements Monitorable, Cloneable { - - private final List ids = new ArrayList(); - private final List unmodifiableIds = Collections.unmodifiableList(ids); - private final List motors = new ArrayList(); - private final List ejectionDelays = new ArrayList(); - private final List mounts = new ArrayList(); - private final List ignitionEvents = new ArrayList(); - private final List ignitionDelays = new ArrayList(); - private final List positions = new ArrayList(); - private final List ignitionTimes = new ArrayList(); - +public class MotorInstanceConfiguration implements Cloneable, Iterable, Monitorable { + protected final HashMap motors = new HashMap(); private int modID = 0; + private MotorInstanceConfiguration() { + } + + /** + * Create a new motor instance configuration for the rocket configuration. + * + * @param configuration the rocket configuration. + */ + public MotorInstanceConfiguration(Configuration configuration) { + // motors == this + final String flightConfigId = configuration.getFlightConfigurationID(); + + Iterator iterator = configuration.getRocket().iterator(false); + while (iterator.hasNext()) { + RocketComponent component = iterator.next(); + if (component instanceof MotorMount) { + MotorMount mount = (MotorMount) component; + + MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId); + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId); + Motor motor = motorConfig.getMotor(); + + if (motor != null) { + int count = mount.getMotorConfiguration().size(); + Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(flightConfigId)); + for (int i = 0; i < positions.length; i++) { + Coordinate position = positions[i]; + MotorId id = new MotorId(component.getID(), i + 1); + MotorInstance inst = motor.getInstance(); + inst.setID(id); + inst.setEjectionDelay(motorConfig.getEjectionDelay()); + inst.setMount(mount); + inst.setIgnitionDelay(ignitionConfig.getIgnitionDelay()); + inst.setIgnitionEvent(ignitionConfig.getIgnitionEvent()); + inst.setPosition(position); + + motors.put(id, inst); + } + } + } + } + + + } /** * Add a motor instance to this configuration. The motor is placed at @@ -45,89 +82,70 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable * @param position the position of the motor in absolute coordinates. * @throws IllegalArgumentException if a motor with the specified ID already exists. */ - public void addMotor(MotorId id, MotorInstance motor, double ejectionDelay, MotorMount mount, - IgnitionEvent ignitionEvent, double ignitionDelay, Coordinate position) { - if (this.ids.contains(id)) { + // public void addMotor(MotorId _id, Motor _motor, double _ejectionDelay, MotorMount _mount, + // IgnitionEvent _ignitionEvent, double _ignitionDelay, Coordinate _position) { + // + // MotorInstance instanceToAdd = new MotorInstance(_id, _motor, _mount, _ejectionDelay, + // _ignitionEvent, _ignitionDelay, _position); + // + // + // // this.ids.add(id); + // // this.motors.add(motor); + // // this.ejectionDelays.add(ejectionDelay); + // // this.mounts.add(mount); + // // this.ignitionEvents.add(ignitionEvent); + // // this.ignitionDelays.add(ignitionDelay); + // // this.positions.add(position); + // // this.ignitionTimes.add(Double.POSITIVE_INFINITY); + // } + + + /** + * Add a motor instance to this configuration. + * + * @param motor the motor instance. + * @throws IllegalArgumentException if a motor with the specified ID already exists. + */ + public void addMotor(MotorInstance motor) { + MotorId id = motor.getID(); + if (this.motors.containsKey(id)) { throw new IllegalArgumentException("MotorInstanceConfiguration already " + "contains a motor with id " + id); } - this.ids.add(id); - this.motors.add(motor); - this.ejectionDelays.add(ejectionDelay); - this.mounts.add(mount); - this.ignitionEvents.add(ignitionEvent); - this.ignitionDelays.add(ignitionDelay); - this.positions.add(position); - this.ignitionTimes.add(Double.POSITIVE_INFINITY); + this.motors.put(id, motor); + modID++; } - /** - * Return a list of all motor IDs in this configuration (not only ones in active stages). - */ - public List getMotorIDs() { - return unmodifiableIds; + public Collection getAllMotors() { + return motors.values(); + } + + public int getMotorCount() { + return motors.size(); + } + + public Set getMotorIDs() { + return this.motors.keySet(); } public MotorInstance getMotorInstance(MotorId id) { - return motors.get(indexOf(id)); + return motors.get(id); } - public double getEjectionDelay(MotorId id) { - return ejectionDelays.get(indexOf(id)); + public boolean hasMotors() { + return (0 < motors.size()); } - public MotorMount getMotorMount(MotorId id) { - return mounts.get(indexOf(id)); - } - - public Coordinate getMotorPosition(MotorId id) { - return positions.get(indexOf(id)); - } - - public void setMotorPosition(MotorId id, Coordinate position) { - positions.set(indexOf(id), position); - modID++; - } - - public double getMotorIgnitionTime(MotorId id) { - return ignitionTimes.get(indexOf(id)); - } - - public void setMotorIgnitionTime(MotorId id, double time) { - this.ignitionTimes.set(indexOf(id), time); - modID++; - } - - public double getMotorIgnitionDelay(MotorId id) { - return ignitionDelays.get(indexOf(id)); - } - - public IgnitionEvent getMotorIgnitionEvent(MotorId id) { - return ignitionEvents.get(indexOf(id)); - } - - - private int indexOf(MotorId id) { - int index = ids.indexOf(id); - if (index < 0) { - throw new IllegalArgumentException("MotorInstanceConfiguration does not " + - "contain a motor with id " + id); - } - return index; - } - - - /** * Step all of the motor instances to the specified time minus their ignition time. * @param time the "global" time */ public void step(double time, double acceleration, AtmosphericConditions cond) { - for (int i = 0; i < motors.size(); i++) { - double t = time - ignitionTimes.get(i); + for (MotorInstance inst : motors.values()) { + double t = time - inst.getIgnitionTime(); if (t >= 0) { - motors.get(i).step(t, acceleration, cond); + inst.step(t, acceleration, cond); } } modID++; @@ -136,7 +154,7 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable @Override public int getModID() { int id = modID; - for (MotorInstance motor : motors) { + for (MotorInstance motor : motors.values()) { id += motor.getModID(); } return id; @@ -149,18 +167,17 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable @Override public MotorInstanceConfiguration clone() { MotorInstanceConfiguration clone = new MotorInstanceConfiguration(); - clone.ids.addAll(this.ids); - clone.mounts.addAll(this.mounts); - clone.positions.addAll(this.positions); - clone.ejectionDelays.addAll(this.ejectionDelays); - clone.ignitionTimes.addAll(this.ignitionTimes); - clone.ignitionEvents.addAll(this.ignitionEvents); - clone.ignitionDelays.addAll(this.ignitionDelays); - for (MotorInstance motor : this.motors) { - clone.motors.add(motor.clone()); + for (MotorInstance motor : this.motors.values()) { + clone.motors.put(motor.id, motor.clone()); } clone.modID = this.modID; return clone; } + @Override + public Iterator iterator() { + return this.motors.values().iterator(); + } + + } diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index 888882af9..de588e4f0 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -415,9 +415,9 @@ public class ThrustCurveMotor implements Motor, Comparable, Se //////// Motor instance implementation //////// - private class ThrustCurveMotorInstance implements MotorInstance { + private class ThrustCurveMotorInstance extends MotorInstance { - private int position; + private int timeIndex; // Previous time step value private double prevTime; @@ -434,13 +434,12 @@ public class ThrustCurveMotor implements Motor, Comparable, Se private final double unitRotationalInertia; private final double unitLongitudinalInertia; - private final Motor parentMotor; private int modID = 0; public ThrustCurveMotorInstance() { log.debug("ThrustCurveMotor: Creating motor instance of " + ThrustCurveMotor.this); - position = 0; + timeIndex = 0; prevTime = 0; instThrust = 0; stepThrust = 0; @@ -451,11 +450,6 @@ public class ThrustCurveMotor implements Motor, Comparable, Se parentMotor = ThrustCurveMotor.this; } - @Override - public Motor getParentMotor() { - return parentMotor; - } - @Override public double getTime() { return prevTime; @@ -500,7 +494,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se modID++; - if (position >= time.length - 1) { + if (timeIndex >= time.length - 1) { // Thrust has ended prevTime = nextTime; stepThrust = 0; @@ -511,33 +505,33 @@ public class ThrustCurveMotor implements Motor, Comparable, Se // Compute average & instantaneous thrust - if (nextTime < time[position + 1]) { + if (nextTime < time[timeIndex + 1]) { // Time step between time points - double nextF = MathUtil.map(nextTime, time[position], time[position + 1], - thrust[position], thrust[position + 1]); + double nextF = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1], + thrust[timeIndex], thrust[timeIndex + 1]); stepThrust = (instThrust + nextF) / 2; instThrust = nextF; } else { // Portion of previous step - stepThrust = (instThrust + thrust[position + 1]) / 2 * (time[position + 1] - prevTime); + stepThrust = (instThrust + thrust[timeIndex + 1]) / 2 * (time[timeIndex + 1] - prevTime); // Whole steps - position++; - while ((position < time.length - 1) && (nextTime >= time[position + 1])) { - stepThrust += (thrust[position] + thrust[position + 1]) / 2 * - (time[position + 1] - time[position]); - position++; + timeIndex++; + while ((timeIndex < time.length - 1) && (nextTime >= time[timeIndex + 1])) { + stepThrust += (thrust[timeIndex] + thrust[timeIndex + 1]) / 2 * + (time[timeIndex + 1] - time[timeIndex]); + timeIndex++; } // End step - if (position < time.length - 1) { - instThrust = MathUtil.map(nextTime, time[position], time[position + 1], - thrust[position], thrust[position + 1]); - stepThrust += (thrust[position] + instThrust) / 2 * - (nextTime - time[position]); + if (timeIndex < time.length - 1) { + instThrust = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1], + thrust[timeIndex], thrust[timeIndex + 1]); + stepThrust += (thrust[timeIndex] + instThrust) / 2 * + (nextTime - time[timeIndex]); } else { // Thrust ended during this step instThrust = 0; @@ -549,9 +543,9 @@ public class ThrustCurveMotor implements Motor, Comparable, Se // Compute average and instantaneous CG (simple average between points) Coordinate nextCG; - if (position < time.length - 1) { - nextCG = MathUtil.map(nextTime, time[position], time[position + 1], - cg[position], cg[position + 1]); + if (timeIndex < time.length - 1) { + nextCG = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1], + cg[timeIndex], cg[timeIndex + 1]); } else { nextCG = cg[cg.length - 1]; } @@ -564,11 +558,12 @@ public class ThrustCurveMotor implements Motor, Comparable, Se @Override public MotorInstance clone() { - try { - return (MotorInstance) super.clone(); - } catch (CloneNotSupportedException e) { - throw new BugException("CloneNotSupportedException", e); - } + throw new BugException("CloneNotSupportedException"); + //try { + // return (MotorInstance) super.clone(); + //} catch (CloneNotSupportedException e) { + // throw new BugException("CloneNotSupportedException", e); + //} } @Override diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java index 0043a8769..112e1cdd0 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java @@ -91,7 +91,7 @@ public class StabilityDomain implements SimulationDomain { absolute = cpx - cgx; double diameter = 0; - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (c instanceof SymmetricComponent) { double d1 = ((SymmetricComponent) c).getForeRadius() * 2; double d2 = ((SymmetricComponent) c).getAftRadius() * 2; diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java index 9075d60ad..4d7ffd02b 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java @@ -84,7 +84,7 @@ public class StabilityParameter implements OptimizableParameter { if (!absolute) { double diameter = 0; - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (c instanceof SymmetricComponent) { double d1 = ((SymmetricComponent) c).getForeRadius() * 2; double d2 = ((SymmetricComponent) c).getAftRadius() * 2; diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index 5e2cfa644..626c58b65 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -15,13 +15,11 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC private FlightConfigurationImpl separationConfigurations; protected int stageNumber; - private static int stageCount; public AxialStage() { this.separationConfigurations = new FlightConfigurationImpl(this, ComponentChangeEvent.EVENT_CHANGE, new StageSeparationConfiguration()); this.relativePosition = Position.AFTER; - stageNumber = AxialStage.stageCount; - AxialStage.stageCount++; + this.stageNumber = 0; } @Override @@ -35,10 +33,6 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC return trans.get("Stage.Stage"); } - public static int getStageCount() { - return AxialStage.stageCount; - } - public FlightConfiguration getStageSeparationConfiguration() { return separationConfigurations; } @@ -107,16 +101,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC if (null == this.parent) { return -1; } else if (this.isCenterline()) { - if (0 < this.stageNumber) { - return --this.stageNumber; - } + return --this.stageNumber; + } else { + return this.parent.getStageNumber(); } - - return -1; - } - - public static void resetStageCount() { - AxialStage.stageCount = 0; } @Override @@ -124,6 +112,10 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC return this.stageNumber; } + public void setStageNumber(final int newStageNumber) { + this.stageNumber = newStageNumber; + } + @Override public Coordinate[] shiftCoordinates(Coordinate[] c) { checkState(); diff --git a/core/src/net/sf/openrocket/rocketcomponent/BoosterSet.java b/core/src/net/sf/openrocket/rocketcomponent/BoosterSet.java index b9fd4f33d..883e391fd 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/BoosterSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/BoosterSet.java @@ -130,32 +130,6 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen return this.getAxialOffset(); } - /** - * Stages may be positioned relative to other stages. In that case, this will set the stage number - * against which this stage is positioned. - * - * @return the stage number which this stage is positioned relative to - */ - @Override - public int getRelativeToStage() { - if (null == this.parent) { - return -1; - } else if (this.parent instanceof BoosterSet) { - return this.parent.parent.getChildPosition(this.parent); - } else if (this.isCenterline()) { - if (0 < this.stageNumber) { - return --this.stageNumber; - } - } - - return -1; - } - - @Override - public int getStageNumber() { - return this.stageNumber; - } - @Override public Coordinate[] shiftCoordinates(Coordinate[] c) { checkState(); diff --git a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java index 52bbeff24..f17fedde7 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Configuration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Configuration.java @@ -1,13 +1,13 @@ package net.sf.openrocket.rocketcomponent; -import java.util.BitSet; import java.util.Collection; import java.util.EventListener; import java.util.EventObject; -import java.util.Iterator; +import java.util.HashMap; import java.util.List; -import java.util.NoSuchElementException; +import net.sf.openrocket.motor.MotorInstance; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.ChangeSource; @@ -16,26 +16,38 @@ import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Monitorable; import net.sf.openrocket.util.StateChangeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * A class defining a rocket configuration, including motors and which stages are active. + * A class defining a rocket configuration, including which stages are active. * - * TODO: HIGH: Remove motor ignition times from this class. * * @author Sampo Niskanen */ -public class Configuration implements Cloneable, ChangeSource, ComponentChangeListener, - Iterable, Monitorable { - - private Rocket rocket; - private BitSet stagesActive = new BitSet(); - - private String flightConfigurationId = null; +public class Configuration implements Cloneable, ChangeSource, ComponentChangeListener, Monitorable { + private static final Logger log = LoggerFactory.getLogger(Configuration.class); + protected Rocket rocket; + protected String flightConfigurationId = null; private List listenerList = new ArrayList(); + protected class StageFlags { + public boolean active = true; + public int prev = -1; + public AxialStage stage = null; + + public StageFlags(AxialStage _stage, int _prev, boolean _active) { + this.stage = _stage; + this.prev = _prev; + this.active = _active; + } + } /* Cached data */ + protected HashMap stageMap = new HashMap(); + private int boundsModID = -1; private ArrayList cachedBounds = new ArrayList(); private double cachedLength = -1; @@ -43,7 +55,6 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi private int refLengthModID = -1; private double cachedRefLength = -1; - private int modID = 0; @@ -55,98 +66,156 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi */ public Configuration(Rocket rocket) { this.rocket = rocket; - setAllStages(); + updateStageMap(); rocket.addComponentChangeListener(this); } - - public Rocket getRocket() { return rocket; } + public void clearAllStages() { + this.setAllStages(false); + } + public void setAllStages() { - stagesActive.clear(); - stagesActive.set(0, AxialStage.getStageCount()); + this.setAllStages(true); + } + + public void setAllStages(final boolean _value) { + for (StageFlags cur : stageMap.values()) { + cur.active = _value; + } fireChangeEvent(); } - - /** - * Set all stages up to and including the given stage number. For example, - * setToStage(0) will set only the first stage active. + /** + * This method flags a stage inactive. Other stages are unaffected. * - * @param stage the stage number. + * @param stageNumber stage number to inactivate */ - public void setToStage(int stage) { - stagesActive.clear(); - stagesActive.set(0, stage + 1, true); - // stages.set(stage+1, rocket.getStageCount(), false); - fireChangeEvent(); + public void clearOnlyStage(final int stageNumber) { + setStage(stageNumber, false); } - public void setOnlyStage(int stage) { - stagesActive.clear(); - stagesActive.set(stage, stage + 1, true); - fireChangeEvent(); + /** + * This method flags a stage active. Other stages are unaffected. + * + * @param stageNumber stage number to activate + */ + public void setOnlyStage(final int stageNumber) { + setStage(stageNumber, true); + } + + /** + * This method flags the specified stage as requested. Other stages are unaffected. + * + * @param stageNumber stage number to flag + * @param _active inactive (false) or active (true) + */ + public void setStage(final int stageNumber, final boolean _active) { + if ((0 <= stageNumber) && (stageMap.containsKey(stageNumber))) { + log.error("debug: setting stage " + stageNumber + " to " + _active); + stageMap.get(stageNumber).active = _active; + fireChangeEvent(); + return; + } + log.error("error: attempt to retrieve via a bad stage number: " + stageNumber); + } + + + public void toggleStage(final int stageNumber) { + if ((0 <= stageNumber) && (stageMap.containsKey(stageNumber))) { + + StageFlags flags = stageMap.get(stageNumber); + log.error("debug: toggling stage " + stageNumber + " to " + !flags.active); + flags.active = !flags.active; + fireChangeEvent(); + return; + } + log.error("error: attempt to retrieve via a bad stage number: " + stageNumber); } /** - * Check whether the up-most stage of the rocket is in this configuration. - * - * @return true if the first stage is active in this configuration. + * Check whether the stage is active. */ - public boolean isHead() { - return isStageActive(0); + public boolean isStageActive(final AxialStage stage) { + return this.isStageActive(stage.getStageNumber()); } - - /** * Check whether the stage specified by the index is active. */ - public boolean isStageActive(int stage) { - if (stage >= AxialStage.getStageCount()) + public boolean isStageActive(int stageNumber) { + if (stageNumber >= this.rocket.getStageCount()) { return false; - return stagesActive.get(stage); - } - - public int getStageCount() { - return AxialStage.getStageCount(); - } - - public int getActiveStageCount() { - int count = 0; - int s = AxialStage.getStageCount(); - - for (int i = 0; i < s; i++) { - if (stagesActive.get(i)) - count++; } - return count; + return stageMap.get(stageNumber).active; } - public int[] getActiveStages() { - // temporary hack fix - //int stageCount = Stage.getStageCount(); - - int stageCount = getRocket().getChildCount(); - List active = new ArrayList(); - int[] ret; - - for (int i = 0; i < stageCount; i++) { - if (stagesActive.get(i)) { - active.add(i); + public List getActiveComponents() { + ArrayList toReturn = new ArrayList(); + for (StageFlags curFlags : this.stageMap.values()) { + if (curFlags.active) { + toReturn.add(curFlags.stage); } } - ret = new int[active.size()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = active.get(i); + return toReturn; + } + + public List getActiveMotors(final MotorInstanceConfiguration mic) { + ArrayList toReturn = new ArrayList(); + for (MotorInstance inst : mic.getAllMotors()) { + MotorMount mount = inst.getMount(); + if (mount instanceof RocketComponent) { + RocketComponent comp = (RocketComponent) mount; + if (this.isStageActive(comp.getStage().getStageNumber())) { + toReturn.add(inst); + } + } } - return ret; + return toReturn; + } + + public List getActiveStages() { + List activeStages = new ArrayList(); + + for (StageFlags flags : this.stageMap.values()) { + activeStages.add(flags.stage); + } + + return activeStages; + } + + public int getActiveStageCount() { + int activeCount = 0; + for (StageFlags cur : this.stageMap.values()) { + if (cur.active) { + activeCount++; + } + } + return activeCount; + } + + /** + * Retrieve the bottom-most active stage. + * @return + */ + public AxialStage getBottomStage() { + AxialStage bottomStage = null; + for (StageFlags curFlags : this.stageMap.values()) { + if (curFlags.active) { + bottomStage = curFlags.stage; + } + } + return bottomStage; + } + + public int getStageCount() { + return stageMap.size(); } @@ -221,8 +290,38 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi ((StateChangeListener) l).stateChanged(e); } } + + updateStageMap(); } + private void updateStageMap() { + if (this.rocket.getStageCount() == this.stageMap.size()) { + // no changes needed + return; + } + + this.stageMap.clear(); + for (AxialStage curStage : this.rocket.getStageList()) { + int prevStageNum = curStage.getStageNumber() - 1; + if (curStage.getParent() instanceof AxialStage) { + prevStageNum = curStage.getParent().getStageNumber(); + } + StageFlags flagsToAdd = new StageFlags(curStage, prevStageNum, true); + this.stageMap.put(curStage.getStageNumber(), flagsToAdd); + } + } + + // DEBUG / DEVEL + public String toDebug() { + StringBuilder buf = new StringBuilder(); + buf.append(String.format("\nDumping stage config: \n")); + for (StageFlags flags : this.stageMap.values()) { + AxialStage curStage = flags.stage; + buf.append(String.format(" [%d]: %24s: %b\n", curStage.getStageNumber(), curStage.getName(), flags.active)); + } + buf.append("\n\n"); + return buf.toString(); + } @Override public void componentChanged(ComponentChangeEvent e) { @@ -232,26 +331,6 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi /////////////// Helper methods /////////////// - /** - * Return whether this configuration has any motors defined to it. - * - * @return true if this configuration has active motor mounts with motors defined to them. - */ - public boolean hasMotors() { - for (RocketComponent c : this) { - if (c instanceof MotorMount) { - MotorMount mount = (MotorMount) c; - if (!mount.isMotorMount()) - continue; - if (mount.getMotor(this.flightConfigurationId) != null) { - return true; - } - } - } - return false; - } - - /** * Return whether a component is in the currently active stages. */ @@ -272,7 +351,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi cachedBounds.clear(); double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY; - for (RocketComponent component : this) { + for (RocketComponent component : this.getActiveComponents()) { for (Coordinate coord : component.getComponentBounds()) { cachedBounds.add(coord); if (coord.x < minX) @@ -308,57 +387,17 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi - /** - * Return an iterator that iterates over the currently active components. - * The Rocket and Stage components are not returned, - * but instead all components that are within currently active stages. - */ - @Override - public Iterator iterator() { - List accumulator = new ArrayList(); - - accumulator = this.getActiveComponents(accumulator, rocket.getChildren()); - - return accumulator.iterator(); - } - - private List getActiveComponents(List accumulator, final List toScan) { - for (RocketComponent rc : toScan) { - if (rc instanceof AxialStage) { - if (!isStageActive(rc.getStageNumber())) { - continue; - } - } else { - accumulator.add(rc); - } - // recurse to children - getActiveComponents(accumulator, rc.getChildren()); - } - return accumulator; - } - - - /** - * Return an iterator that iterates over all MotorMounts within the - * current configuration that have an active motor. - * - * @return an iterator over active motor mounts. - */ - public Iterator motorIterator() { - return new MotorIterator(); - } - - /** * Perform a deep-clone. The object references are also cloned and no * listeners are listening on the cloned object. The rocket instance remains the same. */ + @SuppressWarnings("unchecked") @Override public Configuration clone() { try { Configuration config = (Configuration) super.clone(); config.listenerList = new ArrayList(); - config.stagesActive = (BitSet) this.stagesActive.clone(); + config.stageMap = (HashMap) this.stageMap.clone(); config.cachedBounds = new ArrayList(); config.boundsModID = -1; config.refLengthModID = -1; @@ -375,51 +414,5 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi return modID + rocket.getModID(); } - private class MotorIterator implements Iterator { - private final Iterator iterator; - private MotorMount next = null; - - public MotorIterator() { - this.iterator = iterator(); - } - - @Override - public boolean hasNext() { - getNext(); - return (next != null); - } - - @Override - public MotorMount next() { - getNext(); - if (next == null) { - throw new NoSuchElementException("iterator called for too long"); - } - - MotorMount ret = next; - next = null; - return ret; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove unsupported"); - } - - private void getNext() { - if (next != null) - return; - while (iterator.hasNext()) { - RocketComponent c = iterator.next(); - if (c instanceof MotorMount) { - MotorMount mount = (MotorMount) c; - if (mount.isMotorMount() && mount.getMotor(flightConfigurationId) != null) { - next = mount; - return; - } - } - } - } - } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/ReferenceType.java b/core/src/net/sf/openrocket/rocketcomponent/ReferenceType.java index 02264ebe5..1d6bf5b75 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ReferenceType.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ReferenceType.java @@ -8,9 +8,9 @@ public enum ReferenceType { NOSECONE { @Override public double getReferenceLength(Configuration config) { - for (RocketComponent c: config) { + for (RocketComponent c : config.getActiveComponents()) { if (c instanceof SymmetricComponent) { - SymmetricComponent s = (SymmetricComponent)c; + SymmetricComponent s = (SymmetricComponent) c; if (s.getForeRadius() >= 0.0005) return s.getForeRadius() * 2; if (s.getAftRadius() >= 0.0005) @@ -25,9 +25,9 @@ public enum ReferenceType { @Override public double getReferenceLength(Configuration config) { double r = 0; - for (RocketComponent c: config) { + for (RocketComponent c : config.getActiveComponents()) { if (c instanceof SymmetricComponent) { - SymmetricComponent s = (SymmetricComponent)c; + SymmetricComponent s = (SymmetricComponent) c; r = Math.max(r, s.getForeRadius()); r = Math.max(r, s.getAftRadius()); } @@ -37,8 +37,8 @@ public enum ReferenceType { r = Rocket.DEFAULT_REFERENCE_LENGTH; return r; } - }, - + }, + CUSTOM { @Override public double getReferenceLength(Configuration config) { diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index a78ca0145..271bf6d82 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -80,7 +80,7 @@ public class Rocket extends RocketComponent { // Does the rocket have a perfect finish (a notable amount of laminar flow) private boolean perfectFinish = false; - + private final HashMap stageMap = new HashMap(); ///////////// Constructor ///////////// @@ -93,7 +93,6 @@ public class Rocket extends RocketComponent { functionalModID = modID; defaultConfiguration = new Configuration(this); - AxialStage.resetStageCount(); } @@ -130,7 +129,7 @@ public class Rocket extends RocketComponent { */ public int getStageCount() { checkState(); - return AxialStage.getStageCount(); + return this.stageMap.size(); } /** @@ -195,29 +194,34 @@ public class Rocket extends RocketComponent { return functionalModID; } - public ArrayList getStageList() { - ArrayList toReturn = new ArrayList(); - - toReturn = Rocket.getStages(toReturn, this); - - return toReturn; + public Collection getStageList() { + return this.stageMap.values(); } - private static ArrayList getStages(ArrayList accumulator, final RocketComponent parent) { - if ((null == accumulator) || (null == parent)) { - return new ArrayList(); + private int getNewStageNumber() { + int guess = 0; + while (stageMap.containsKey(guess)) { + guess++; } - - for (RocketComponent curChild : parent.getChildren()) { - if (curChild instanceof AxialStage) { - AxialStage curStage = (AxialStage) curChild; - accumulator.add(curStage); - } - getStages(accumulator, curChild); - } - return accumulator; + return guess; } + public void trackStage(final AxialStage newStage) { + int stageNumber = newStage.getStageNumber(); + AxialStage value = stageMap.get(stageNumber); + + if (newStage.equals(value)) { + // stage is already added. skip. + } else { + stageNumber = getNewStageNumber(); + newStage.setStageNumber(stageNumber); + this.stageMap.put(stageNumber, newStage); + } + } + + public void forgetStage(final AxialStage oldStage) { + this.stageMap.remove(oldStage.getStageNumber()); + } public ReferenceType getReferenceType() { checkState(); diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index f10de19e8..5eb1e8b0c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable { + @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(RocketComponent.class); // Because of changes to Java 1.7.0-45's mechanism to construct DataFlavor objects (used in Drag and Drop) @@ -985,8 +986,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * it should override this with a public method that simply calls this * supermethod AND fire a suitable ComponentChangeEvent. * - * @deprecated name is ambiguous in three-dimensional space: value may refer to any of the three dimensions. Please use 'setPositionX' instead. - * + * @deprecated name is ambiguous in three-dimensional space: value may refer to any of the three dimensions. Please use 'setAxialOffset' instead. * @param value the position value of the component. */ public void setPositionValue(double value) { @@ -1346,6 +1346,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab children.add(index, component); component.parent = this; + if (component instanceof AxialStage) { + AxialStage nStage = (AxialStage) component; + this.getRocket().trackStage(nStage); + } + this.checkComponentStructure(); component.checkComponentStructure(); @@ -1363,6 +1368,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent component = children.remove(n); component.parent = null; + if (component instanceof AxialStage) { + AxialStage nStage = (AxialStage) component; + this.getRocket().forgetStage(nStage); + } + this.checkComponentStructure(); component.checkComponentStructure(); diff --git a/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java b/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java index 0aab853ac..635beb76c 100644 --- a/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java @@ -1,12 +1,12 @@ package net.sf.openrocket.simulation; +import java.util.List; + import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; -import net.sf.openrocket.motor.MotorId; import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.exception.SimulationException; import net.sf.openrocket.simulation.listeners.SimulationListenerHelper; import net.sf.openrocket.util.BugException; @@ -45,7 +45,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { } - + /** * Compute the wind to use, allowing listeners to override. * @@ -75,7 +75,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { } - + /** * Compute the gravity to use, allowing listeners to override. * @@ -104,7 +104,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { } - + /** * Compute the mass data to use, allowing listeners to override. * @@ -142,9 +142,9 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { } - - - + + + /** * Calculate the average thrust produced by the motors in the current configuration, allowing * listeners to override. The average is taken between status.time and @@ -171,19 +171,18 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { } Configuration configuration = status.getConfiguration(); + MotorInstanceConfiguration mic = status.getMotorConfiguration(); // Iterate over the motors and calculate combined thrust - MotorInstanceConfiguration mic = status.getMotorConfiguration(); if (!stepMotors) { mic = mic.clone(); } mic.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions); thrust = 0; - for (MotorId id : mic.getMotorIDs()) { - if (configuration.isComponentActive((RocketComponent) mic.getMotorMount(id))) { - MotorInstance motor = mic.getMotorInstance(id); - thrust += motor.getThrust(); - } + + List activeMotors = configuration.getActiveMotors(mic); + for (MotorInstance currentMotorInstance : activeMotors) { + thrust += currentMotorInstance.getThrust(); } // Post-listeners diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 67dd57093..68187ad05 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -1,6 +1,6 @@ package net.sf.openrocket.simulation; -import java.util.Iterator; +import java.util.List; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.l10n.Translator; @@ -8,14 +8,13 @@ import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorId; import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; +import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; -import net.sf.openrocket.rocketcomponent.MotorConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; import net.sf.openrocket.simulation.exception.MotorIgnitionException; import net.sf.openrocket.simulation.exception.SimulationException; @@ -68,7 +67,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Set up rocket configuration Configuration configuration = setupConfiguration(simulationConditions); flightConfigurationId = configuration.getFlightConfigurationID(); - MotorInstanceConfiguration motorConfiguration = setupMotorConfiguration(configuration); + MotorInstanceConfiguration motorConfiguration = new MotorInstanceConfiguration(configuration); if (motorConfiguration.getMotorIDs().isEmpty()) { throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noMotorsDefined")); } @@ -199,7 +198,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { MotorInstance motor = status.getMotorConfiguration().getMotorInstance(motorId); if (!motor.isActive() && status.addBurntOutMotor(motorId)) { addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, status.getSimulationTime(), - (RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId)); + (RocketComponent) status.getMotorConfiguration().getMotorInstance(motorId).getMount(), motorId)); } } @@ -221,7 +220,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { if (wantToTumble) { final boolean tooMuchThrust = t > THRUST_TUMBLE_CONDITION; - final boolean isSustainer = status.getConfiguration().isStageActive(0); + //final boolean isSustainer = status.getConfiguration().isStageActive(0); final boolean isApogee = status.isApogeeReached(); if (tooMuchThrust) { status.getWarnings().add(Warning.TUMBLE_UNDER_THRUST); @@ -261,37 +260,6 @@ public class BasicEventSimulationEngine implements SimulationEngine { - /** - * Create a new motor instance configuration for the rocket configuration. - * - * @param configuration the rocket configuration. - * @return a new motor instance configuration with all motors in place. - */ - private MotorInstanceConfiguration setupMotorConfiguration(Configuration configuration) { - MotorInstanceConfiguration motors = new MotorInstanceConfiguration(); - final String flightConfigId = configuration.getFlightConfigurationID(); - - Iterator iterator = configuration.motorIterator(); - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - RocketComponent component = (RocketComponent) mount; - MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId); - IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId); - Motor motor = motorConfig.getMotor(); - - if (motor != null) { - Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(flightConfigId)); - for (int i = 0; i < positions.length; i++) { - Coordinate position = positions[i]; - MotorId id = new MotorId(component.getID(), i + 1); - motors.addMotor(id, motor.getInstance(), motorConfig.getEjectionDelay(), mount, - ignitionConfig.getIgnitionEvent(), ignitionConfig.getIgnitionDelay(), position); - } - } - } - return motors; - } - /** * Handles events occurring during the flight from the event queue. * Each event that has occurred before or at the current simulation time is @@ -340,12 +308,13 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for motor ignition events, add ignition events to queue for (MotorId id : status.getMotorConfiguration().getMotorIDs()) { - IgnitionConfiguration.IgnitionEvent ignitionEvent = status.getMotorConfiguration().getMotorIgnitionEvent(id); - MotorMount mount = status.getMotorConfiguration().getMotorMount(id); + MotorInstance inst = status.getMotorConfiguration().getMotorInstance(id); + IgnitionConfiguration.IgnitionEvent ignitionEvent = inst.getIgnitionEvent(); + MotorMount mount = inst.getMount(); RocketComponent component = (RocketComponent) mount; if (ignitionEvent.isActivationEvent(event, component)) { - double ignitionDelay = status.getMotorConfiguration().getMotorIgnitionDelay(id); + double ignitionDelay = inst.getIgnitionDelay(); addEvent(new FlightEvent(FlightEvent.Type.IGNITION, status.getSimulationTime() + ignitionDelay, component, id)); @@ -354,11 +323,12 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for stage separation event - for (int stageNo : status.getConfiguration().getActiveStages()) { + + for (AxialStage stage : status.getConfiguration().getActiveStages()) { + int stageNo = stage.getStageNumber(); if (stageNo == 0) continue; - AxialStage stage = (AxialStage) status.getConfiguration().getRocket().getChild(stageNo); StageSeparationConfiguration separationConfig = stage.getStageSeparationConfiguration().get(flightConfigurationId); if (separationConfig.getSeparationEvent().isSeparationEvent(event, stage)) { addEvent(new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, @@ -368,9 +338,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for recovery device deployment, add events to queue - Iterator rci = status.getConfiguration().iterator(); - while (rci.hasNext()) { - RocketComponent c = rci.next(); + for (RocketComponent c : status.getConfiguration().getActiveComponents()) { if (!(c instanceof RecoveryDevice)) continue; DeploymentConfiguration deployConfig = ((RecoveryDevice) c).getDeploymentConfiguration().get(flightConfigurationId); @@ -393,8 +361,10 @@ public class BasicEventSimulationEngine implements SimulationEngine { case IGNITION: { // Ignite the motor MotorId motorId = (MotorId) event.getData(); - MotorInstanceConfiguration config = status.getMotorConfiguration(); - config.setMotorIgnitionTime(motorId, event.getTime()); + MotorInstanceConfiguration motorConfig = status.getMotorConfiguration(); + MotorInstance inst = motorConfig.getMotorInstance(motorId); + inst.setIgnitionTime(event.getTime()); + status.setMotorIgnited(true); status.getFlightData().addEvent(event); @@ -422,7 +392,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } // Add ejection charge event MotorId motorId = (MotorId) event.getData(); - double delay = status.getMotorConfiguration().getEjectionDelay(motorId); + double delay = status.getMotorConfiguration().getMotorInstance(motorId).getEjectionDelay(); if (delay != Motor.PLUGGED) { addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, status.getSimulationTime() + delay, event.getSource(), event.getData())); @@ -450,7 +420,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { stages.add(boosterStatus); // Mark the status as having dropped the booster - status.getConfiguration().setToStage(n - 1); + status.getConfiguration().clearOnlyStage(n); // Mark the booster status as only having the booster. boosterStatus.getConfiguration().setOnlyStage(n); @@ -476,12 +446,14 @@ public class BasicEventSimulationEngine implements SimulationEngine { // TODO: HIGH: Check stage activeness for other events as well? // Check whether any motor in the active stages is active anymore - for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) { - int stage = ((RocketComponent) status.getMotorConfiguration(). - getMotorMount(motorId)).getStageNumber(); + List activeMotors = status.getConfiguration().getActiveMotors(status.getMotorConfiguration()); + for (MotorInstance curInstance : activeMotors) { + MotorId curID = curInstance.getID(); + RocketComponent comp = ((RocketComponent) curInstance.getMount()); + int stage = comp.getStageNumber(); if (!status.getConfiguration().isStageActive(stage)) continue; - if (!status.getMotorConfiguration().getMotorInstance(motorId).isActive()) + if (!status.getMotorConfiguration().getMotorInstance(curID).isActive()) continue; status.getWarnings().add(Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING); } diff --git a/core/src/net/sf/openrocket/simulation/SimulationStatus.java b/core/src/net/sf/openrocket/simulation/SimulationStatus.java index bad65b1c9..db0030a52 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationStatus.java +++ b/core/src/net/sf/openrocket/simulation/SimulationStatus.java @@ -119,7 +119,7 @@ public class SimulationStatus implements Monitorable { */ double length = this.simulationConditions.getLaunchRodLength(); double lugPosition = Double.NaN; - for (RocketComponent c : this.configuration) { + for (RocketComponent c : this.configuration.getActiveComponents()) { if (c instanceof LaunchLug) { double pos = c.toAbsolute(new Coordinate(c.getLength()))[0].x; if (Double.isNaN(lugPosition) || pos > lugPosition) { diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java b/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java index 3e58beb5e..2de3d6bee 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java +++ b/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java @@ -6,8 +6,9 @@ import java.util.Map; import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; -import net.sf.openrocket.motor.MotorId; +import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; +import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; @@ -20,7 +21,9 @@ import net.sf.openrocket.util.Coordinate; public class DampingMoment extends AbstractSimulationListener { private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT); - private static final FlightDataType[] typeList = { type }; + + // unused + //private static final FlightDataType[] typeList = { type }; @Override public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException { @@ -66,11 +69,12 @@ public class DampingMoment extends AbstractSimulationListener { // find the maximum distance from nose to nozzle. double nozzleDistance = 0; - for (MotorId id : status.getMotorConfiguration().getMotorIDs()) { - MotorInstanceConfiguration config = status.getMotorConfiguration(); - Coordinate position = config.getMotorPosition(id); + Configuration config = status.getConfiguration(); + MotorInstanceConfiguration motorConfig = status.getMotorConfiguration(); + for (MotorInstance inst : config.getActiveMotors(motorConfig)) { + Coordinate position = inst.getPosition(); - double x = position.x + config.getMotorInstance(id).getParentMotor().getLength(); + double x = position.x + inst.getMotor().getLength(); if (x > nozzleDistance) { nozzleDistance = x; } diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java index 68063cb40..55a557d70 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java +++ b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java @@ -36,7 +36,7 @@ public class RollControlListener extends AbstractSimulationListener { // Maximum control fin angle (rad) private static final double MAX_ANGLE = 15 * Math.PI / 180; - + /* * PID parameters * @@ -47,9 +47,9 @@ public class RollControlListener extends AbstractSimulationListener { private static final double KP = 0.007; private static final double KI = 0.2; - - - + + + private double rollrate; private double prevTime = 0; @@ -58,7 +58,7 @@ public class RollControlListener extends AbstractSimulationListener { private double finPosition = 0; - + @Override public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { // Store the current roll rate for later use @@ -78,7 +78,7 @@ public class RollControlListener extends AbstractSimulationListener { // Find the fin set named CONTROL FinSet finset = null; - for (RocketComponent c : status.getConfiguration()) { + for (RocketComponent c : status.getConfiguration().getActiveComponents()) { if ((c instanceof FinSet) && (c.getName().equals(CONTROL_FIN_NAME))) { finset = (FinSet) c; break; @@ -88,12 +88,12 @@ public class RollControlListener extends AbstractSimulationListener { throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found"); } - + // Determine time step double deltaT = status.getSimulationTime() - prevTime; prevTime = status.getSimulationTime(); - + // PID controller double error = SETPOINT - rollrate; @@ -103,7 +103,7 @@ public class RollControlListener extends AbstractSimulationListener { double value = p + i; - + // Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE if (Math.abs(value) > MAX_ANGLE) { System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n", @@ -111,7 +111,7 @@ public class RollControlListener extends AbstractSimulationListener { value = MathUtil.clamp(value, -MAX_ANGLE, MAX_ANGLE); } - + // Limit the fin turn rate if (finPosition < value) { finPosition = Math.min(finPosition + TURNRATE * deltaT, value); diff --git a/core/src/net/sf/openrocket/unit/CaliberUnit.java b/core/src/net/sf/openrocket/unit/CaliberUnit.java index cc93cb511..9be45e649 100644 --- a/core/src/net/sf/openrocket/unit/CaliberUnit.java +++ b/core/src/net/sf/openrocket/unit/CaliberUnit.java @@ -23,8 +23,8 @@ public class CaliberUnit extends GeneralUnit { private double caliber = -1; - - + + public CaliberUnit(Configuration configuration) { super(1.0, "cal"); this.configuration = configuration; @@ -69,7 +69,7 @@ public class CaliberUnit extends GeneralUnit { } - + private void checkCaliber() { if (configuration != null && configuration.getModID() != configurationModId) { caliber = -1; @@ -98,7 +98,7 @@ public class CaliberUnit extends GeneralUnit { * @return the caliber of the rocket, or the default caliber. */ public static double calculateCaliber(Configuration config) { - return calculateCaliber(config.iterator()); + return calculateCaliber(config.getActiveComponents().iterator()); } /** @@ -112,7 +112,7 @@ public class CaliberUnit extends GeneralUnit { } - + private static double calculateCaliber(Iterator iterator) { double cal = 0; diff --git a/core/test/net/sf/openrocket/rocketcomponent/BoosterSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/BoosterSetTest.java index 84d3c0b15..fabc058bb 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/BoosterSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/BoosterSetTest.java @@ -647,7 +647,6 @@ public class BoosterSetTest extends BaseTestCase { assertEquals(" init order error: " + treeDump + " Booster B: resultant positions: ", expectedOffset, resultantOffsetB, EPSILON); } - @Test public void testStageNumbering() { Rocket rocket = createTestRocket(); @@ -679,9 +678,30 @@ public class BoosterSetTest extends BaseTestCase { actualStageNumber = boosterB.getStageNumber(); assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber); + //rocket.getDefaultConfiguration().dumpConfig(); + + core.removeChild(2); + + String treedump = rocket.toDebugTree(); + int expectedStageCount = 3; + int actualStageCount = rocket.getStageCount(); + + assertEquals(" Stage tracking error: removed booster A, but count not updated: " + treedump, expectedStageCount, actualStageCount); + actualStageCount = rocket.getDefaultConfiguration().getStageCount(); + assertEquals(" Stage tracking error: removed booster A, but configuration not updated: " + treedump, expectedStageCount, actualStageCount); + + BoosterSet boosterC = createBooster(); + boosterC.setName("Booster C Stage"); + core.addChild(boosterC); + boosterC.setAxialOffset(Position.BOTTOM, 0); + + expectedStageNumber = 2; + actualStageNumber = boosterC.getStageNumber(); + assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber); + + //rocket.getDefaultConfiguration().dumpConfig(); } - @Test public void testToAbsolute() { Rocket rocket = createTestRocket(); diff --git a/core/test/net/sf/openrocket/rocketcomponent/ConfigurationTest.java b/core/test/net/sf/openrocket/rocketcomponent/ConfigurationTest.java index 64284700a..f699f7d53 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/ConfigurationTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/ConfigurationTest.java @@ -1,11 +1,12 @@ package net.sf.openrocket.rocketcomponent; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.BitSet; import java.util.EventObject; -import java.util.Iterator; import net.sf.openrocket.rocketcomponent.RocketComponent.Position; import net.sf.openrocket.util.StateChangeListener; @@ -72,20 +73,9 @@ public class ConfigurationTest extends BaseTestCase { Configuration config = r1.getDefaultConfiguration(); /* Test */ - // Test rocket component iterator - // TODO: validate iterator iterates correctly - for (Iterator i = config.iterator(); i.hasNext();) { - i.next(); - } - - // Rocket component iterator remove method is unsupported, should throw exception - try { - Iterator configIterator = config.iterator(); - configIterator.remove(); - } catch (UnsupportedOperationException e) { - assertTrue(e.getMessage().equals("remove unsupported")); - } + // the component iterator is no longer a custom iterator.... + // use the standard iterator over the Collection<> returned by config.getActiveComponents() // Test motor iterator /* TODO: no motors in model Iterator motorIterator() @@ -95,14 +85,14 @@ public class ConfigurationTest extends BaseTestCase { } */ - // Motor iterator remove method is unsupported, should throw exception - try { - Iterator motorIterator = config.motorIterator(); - motorIterator.remove(); - } catch (UnsupportedOperationException e) { - assertTrue(e.getMessage().equals("remove unsupported")); - } - + // // Motor iterator remove method is unsupported, should throw exception + // try { + // Iterator motorIterator = config.motorIterator(); + // motorIterator.remove(); + // } catch (UnsupportedOperationException e) { + // assertTrue(e.getMessage().equals("remove unsupported")); + // } + // /* Cleanup */ config.release(); @@ -119,7 +109,7 @@ public class ConfigurationTest extends BaseTestCase { Configuration configClone = config.clone(); // TODO validate clone worked assertFalse(config.getRocket() == null); - assertFalse(config.hasMotors()); + // assertFalse(config.hasMotors()); config.release(); } @@ -211,31 +201,34 @@ public class ConfigurationTest extends BaseTestCase { Rocket r1 = makeSingleStageTestRocket(); Configuration config = r1.getDefaultConfiguration(); - BitSet activeStageFlags = new BitSet(); - activeStageFlags.set(0, false); // first stage - /* Test */ // test cloning of single stage rocket Configuration configClone = config.clone(); // TODO validate clone worked + configClone.release(); // test explicitly setting only first stage active + config.clearAllStages(); config.setOnlyStage(0); - activeStageFlags.clear(); - activeStageFlags.set(0, true); - validateStages(config, 1, activeStageFlags); + + + //config.dumpConfig(); + //System.err.println("treedump: \n" + treedump); + + // test that getStageCount() returns correct value + int expectedStageCount = 1; + int stageCount = config.getStageCount(); + assertTrue("stage count doesn't match", stageCount == expectedStageCount); + + expectedStageCount = 1; + stageCount = config.getActiveStageCount(); + assertThat("active stage count doesn't match", stageCount, equalTo(expectedStageCount)); // test explicitly setting all stages up to first stage active - config.setToStage(0); - activeStageFlags.clear(); - activeStageFlags.set(0, true); - validateStages(config, 1, activeStageFlags); + config.setOnlyStage(0); // test explicitly setting all stages active config.setAllStages(); - activeStageFlags.clear(); - activeStageFlags.set(0, true); - validateStages(config, 1, activeStageFlags); // Cleanup config.release(); @@ -252,43 +245,57 @@ public class ConfigurationTest extends BaseTestCase { Rocket r1 = makeTwoStageTestRocket(); Configuration config = r1.getDefaultConfiguration(); - BitSet activeStageFlags = new BitSet(); - activeStageFlags.set(0, false); // booster (first) stage - activeStageFlags.set(1, false); // sustainer (second) stage - - /* Test */ - // test cloning of two stage rocket Configuration configClone = config.clone(); // TODO validate clone worked + configClone.release(); + + int expectedStageCount; + int stageCount; + + expectedStageCount = 2; + stageCount = config.getStageCount(); + assertThat("stage count doesn't match", stageCount, equalTo(expectedStageCount)); + + config.clearAllStages(); + assertThat(" clear all stages: check #0: ", config.isStageActive(0), equalTo(false)); + assertThat(" clear all stages: check #1: ", config.isStageActive(1), equalTo(false)); // test explicitly setting only first stage active config.setOnlyStage(0); - activeStageFlags.clear(); - activeStageFlags.set(0, true); - validateStages(config, 2, activeStageFlags); - // test explicitly setting all stages up to first stage active - config.setToStage(0); - activeStageFlags.clear(); - activeStageFlags.set(0, true); - validateStages(config, 2, activeStageFlags); + expectedStageCount = 1; + stageCount = config.getActiveStageCount(); + assertThat("active stage count doesn't match", stageCount, equalTo(expectedStageCount)); + + assertThat(" setting single stage active: ", config.isStageActive(0), equalTo(true)); // test explicitly setting all stages up to second stage active - config.setToStage(1); - activeStageFlags.clear(); - activeStageFlags.set(0, 2, true); - validateStages(config, 2, activeStageFlags); + config.setOnlyStage(1); + assertThat(config.toDebug() + "Setting single stage active: ", config.isStageActive(1), equalTo(true)); + + config.clearOnlyStage(0); + assertThat(" deactivate stage #0: ", config.isStageActive(0), equalTo(false)); + assertThat(" deactive stage #0: ", config.isStageActive(1), equalTo(true)); // test explicitly setting all two stages active config.setAllStages(); - activeStageFlags.clear(); - activeStageFlags.set(0, 2, true); - validateStages(config, 2, activeStageFlags); + assertThat(" activate all stages: check #0: ", config.isStageActive(0), equalTo(true)); + assertThat(" activate all stages: check #1: ", config.isStageActive(1), equalTo(true)); + + // test toggling single stage + config.setAllStages(); + config.toggleStage(0); + assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false)); + + config.toggleStage(0); + assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(true)); + + config.toggleStage(0); + assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false)); // Cleanup config.release(); - } ///////////////////// Helper Methods //////////////////////////// @@ -307,43 +314,45 @@ public class ConfigurationTest extends BaseTestCase { } } assertTrue(config.getActiveStageCount() == expectedActiveStageCount); - int[] stages = config.getActiveStages(); - assertTrue(stages.length == expectedActiveStageCount); - // test if isHead() detects first stage being active or inactive - if (activeStageFlags.get(0)) { - assertTrue(config.isHead()); - } else { - assertFalse(config.isHead()); - } - - // test if isStageActive() detects stage x being active or inactive - for (int i = 0; i < expectedStageCount; i++) { - if (activeStageFlags.get(i)) { - assertTrue(config.isStageActive(i)); - } else { - assertFalse(config.isStageActive(i)); - } - } - - // test boundary conditions - - // stage -1 should not exist, and isStageActive() should throw exception - boolean IndexOutOfBoundsExceptionFlag = false; - try { - assertFalse(config.isStageActive(-1)); - } catch (IndexOutOfBoundsException e) { - IndexOutOfBoundsExceptionFlag = true; - } - assertTrue(IndexOutOfBoundsExceptionFlag); - - // n+1 stage should not exist, isStageActive() should return false - // TODO: isStageActive(stageCount + 1) really should throw IndexOutOfBoundsException - assertFalse(config.isStageActive(stageCount + 1)); + assertTrue("this test is not yet written.", false); + // int[] stages = config.getActiveStages(); + // + // assertTrue(stages.length == expectedActiveStageCount); + // + // // test if isHead() detects first stage being active or inactive + // if (activeStageFlags.get(0)) { + // assertTrue(config.isHead()); + // } else { + // assertFalse(config.isHead()); + // } + // + // // test if isStageActive() detects stage x being active or inactive + // for (int i = 0; i < expectedStageCount; i++) { + // if (activeStageFlags.get(i)) { + // assertTrue(config.isStageActive(i)); + // } else { + // assertFalse(config.isStageActive(i)); + // } + // } + // + // // test boundary conditions + // + // // stage -1 should not exist, and isStageActive() should throw exception + // boolean IndexOutOfBoundsExceptionFlag = false; + // try { + // assertFalse(config.isStageActive(-1)); + // } catch (IndexOutOfBoundsException e) { + // IndexOutOfBoundsExceptionFlag = true; + // } + // assertTrue(IndexOutOfBoundsExceptionFlag); + // + // // n+1 stage should not exist, isStageActive() should return false + // // TODO: isStageActive(stageCount + 1) really should throw IndexOutOfBoundsException + // assertFalse(config.isStageActive(stageCount + 1)); } - //////////////////// Test Rocket Creation Methods ///////////////////////// public static Rocket makeEmptyRocket() { diff --git a/swing/src/net/sf/openrocket/gui/components/StageSelector.java b/swing/src/net/sf/openrocket/gui/components/StageSelector.java index 947927912..d9a478c8f 100644 --- a/swing/src/net/sf/openrocket/gui/components/StageSelector.java +++ b/swing/src/net/sf/openrocket/gui/components/StageSelector.java @@ -11,12 +11,15 @@ import javax.swing.JToggleButton; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.StateChangeListener; public class StageSelector extends JPanel implements StateChangeListener { + private static final long serialVersionUID = -2898763402479628711L; + private static final Translator trans = Application.getTranslator(); private final Configuration configuration; @@ -40,13 +43,11 @@ public class StageSelector extends JPanel implements StateChangeListener { if (buttons.size() == stages) return; - while (buttons.size() > stages) { - JToggleButton button = buttons.remove(buttons.size() - 1); - this.remove(button); - } - - while (buttons.size() < stages) { - JToggleButton button = new JToggleButton(new StageAction(buttons.size())); + buttons.clear(); + this.removeAll(); + for(AxialStage stage : configuration.getRocket().getStageList()){ + int stageNum = stage.getStageNumber(); + JToggleButton button = new JToggleButton(new StageAction(stageNum)); this.add(button); buttons.add(button); } @@ -55,8 +56,6 @@ public class StageSelector extends JPanel implements StateChangeListener { } - - @Override public void stateChanged(EventObject e) { updateButtons(); @@ -64,10 +63,11 @@ public class StageSelector extends JPanel implements StateChangeListener { private class StageAction extends AbstractAction implements StateChangeListener { - private final int stage; + private static final long serialVersionUID = 7433006728984943763L; + private final int stageNumber; public StageAction(final int stage) { - this.stage = stage; + this.stageNumber = stage; configuration.addChangeListener(this); stateChanged(null); } @@ -75,37 +75,20 @@ public class StageSelector extends JPanel implements StateChangeListener { @Override public Object getValue(String key) { if (key.equals(NAME)) { - //// Stage - return trans.get("StageAction.Stage") + " " + (stage + 1); + // Stage + return trans.get("StageAction.Stage") + " " + (stageNumber ); } return super.getValue(key); } @Override public void actionPerformed(ActionEvent e) { - configuration.setToStage(stage); - - // boolean state = (Boolean)getValue(SELECTED_KEY); - // if (state == true) { - // // Was disabled, now enabled - // configuration.setToStage(stage); - // } else { - // // Was enabled, check what to do - // if (configuration.isStageActive(stage + 1)) { - // configuration.setToStage(stage); - // } else { - // if (stage == 0) - // configuration.setAllStages(); - // else - // configuration.setToStage(stage-1); - // } - // } - // stateChanged(null); + configuration.toggleStage(stageNumber); } @Override public void stateChanged(EventObject e) { - this.putValue(SELECTED_KEY, configuration.isStageActive(stage)); + this.putValue(SELECTED_KEY, configuration.isStageActive(stageNumber)); } } } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java index fee70b852..9c9883a13 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java @@ -513,7 +513,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe cgData.clear(); dragData.clear(); rollData.clear(); - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { forces = aeroData.get(c); Coordinate cg = massData.get(c); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index 413cf0961..ceefc69b2 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -2,7 +2,6 @@ package net.sf.openrocket.gui.figure3d; import java.awt.Point; import java.nio.ByteBuffer; -import java.util.Iterator; import java.util.Set; import java.util.Vector; @@ -62,7 +61,7 @@ public abstract class RocketRenderer { // Store a vector of pickable parts. final Vector pickParts = new Vector(); - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (ignore != null && ignore.contains(c)) continue; @@ -116,7 +115,7 @@ public abstract class RocketRenderer { gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0); gl.glLineWidth(5.0f); - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (selection.contains(c)) { // Draw as lines, set Z to nearest gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); @@ -141,7 +140,7 @@ public abstract class RocketRenderer { gl.glCullFace(GL.GL_BACK); // Draw all inner components - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (isDrawn(c)) { if (!isDrawnTransparent(c)) { renderComponent(gl, c, 1.0f); @@ -153,7 +152,7 @@ public abstract class RocketRenderer { // Draw T&T front faces blended, without depth test gl.glEnable(GL.GL_BLEND); - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (isDrawn(c)) { if (isDrawnTransparent(c)) { renderComponent(gl, c, 0.2f); @@ -166,20 +165,23 @@ public abstract class RocketRenderer { private void renderMotors(GL2 gl, Configuration configuration) { String motorID = configuration.getFlightConfigurationID(); - Iterator iterator = configuration.motorIterator(); - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - Motor motor = mount.getMotorConfiguration().get(motorID).getMotor(); - double length = motor.getLength(); + + for( RocketComponent comp : configuration.getActiveComponents()){ + if( comp instanceof MotorMount){ - Coordinate[] position = ((RocketComponent) mount).toAbsolute(new Coordinate(((RocketComponent) mount) - .getLength() + mount.getMotorOverhang() - length)); + MotorMount mount = (MotorMount) comp; + Motor motor = mount.getMotorConfiguration().get(motorID).getMotor(); + double length = motor.getLength(); - for (int i = 0; i < position.length; i++) { - gl.glPushMatrix(); - gl.glTranslated(position[i].x, position[i].y, position[i].z); - renderMotor(gl, motor); - gl.glPopMatrix(); + Coordinate[] position = ((RocketComponent) mount).toAbsolute(new Coordinate(((RocketComponent) mount) + .getLength() + mount.getMotorOverhang() - length)); + + for (int i = 0; i < position.length; i++) { + gl.glPushMatrix(); + gl.glTranslated(position[i].x, position[i].y, position[i].z); + renderMotor(gl, motor); + gl.glPopMatrix(); + } } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoPanel.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoPanel.java index eb49e69a3..fcaaf2133 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoPanel.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoPanel.java @@ -14,7 +14,6 @@ import java.util.Iterator; import java.util.List; import java.util.Vector; -import javax.media.opengl.DebugGL2; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GLAutoDrawable; @@ -44,7 +43,6 @@ import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.Color; @@ -416,37 +414,40 @@ public class PhotoPanel extends JPanel implements GLEventListener { rr.render(drawable, configuration, new HashSet()); //Figure out the lowest stage shown - final int currentStageNumber = configuration.getActiveStages()[configuration.getActiveStages().length-1]; - final AxialStage currentStage = (AxialStage)configuration.getRocket().getChild(currentStageNumber); + final int bottomStageNumber = configuration.getBottomStage().getStageNumber(); + //final int currentStageNumber = configuration.getActiveStages()[configuration.getActiveStages().length-1]; + //final AxialStage currentStage = (AxialStage)configuration.getRocket().getChild( bottomStageNumber); final String motorID = configuration.getFlightConfigurationID(); - final Iterator iterator = configuration.motorIterator(); - motor: while (iterator.hasNext()) { - final MotorMount mount = iterator.next(); + + + final Iterator iter = configuration.getActiveComponents().iterator(); + while( iter.hasNext()){ + RocketComponent comp = iter.next(); + if( comp instanceof MotorMount){ + + final MotorMount mount = (MotorMount) comp; + int curStageNumber = comp.getStageNumber(); - //If this mount is not in currentStage continue on to the next one. - RocketComponent parent = ((RocketComponent)mount); - while ( null != (parent = parent.getParent()) ){ - if ( parent instanceof AxialStage ){ - if ( parent != currentStage ) - continue motor; - break; + //If this mount is not in currentStage continue on to the next one. + if( curStageNumber != bottomStageNumber ){ + continue; + } + + final Motor motor = mount.getMotorConfiguration().get(motorID).getMotor(); + final double length = motor.getLength(); + + Coordinate[] position = ((RocketComponent) mount) + .toAbsolute(new Coordinate(((RocketComponent) mount) + .getLength() + mount.getMotorOverhang() - length)); + + for (int i = 0; i < position.length; i++) { + gl.glPushMatrix(); + gl.glTranslated(position[i].x + motor.getLength(), + position[i].y, position[i].z); + FlameRenderer.drawExhaust(gl, p, motor); + gl.glPopMatrix(); } - } - - final Motor motor = mount.getMotorConfiguration().get(motorID).getMotor(); - final double length = motor.getLength(); - - Coordinate[] position = ((RocketComponent) mount) - .toAbsolute(new Coordinate(((RocketComponent) mount) - .getLength() + mount.getMotorOverhang() - length)); - - for (int i = 0; i < position.length; i++) { - gl.glPushMatrix(); - gl.glTranslated(position[i].x + motor.getLength(), - position[i].y, position[i].z); - FlameRenderer.drawExhaust(gl, p, motor); - gl.glPopMatrix(); } } diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java index d4bcc4d01..52717e412 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java @@ -13,6 +13,7 @@ import java.awt.geom.Rectangle2D; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.startup.Application; @@ -47,7 +48,9 @@ public class RocketInfo implements FigureElement { private double cg = 0, cp = 0; private double length = 0, diameter = 0; private double mass = 0; - private double aoa = Double.NaN, theta = Double.NaN, mach = Application.getPreferences().getDefaultMach(); + private double aoa = Double.NaN; + private double theta = Double.NaN; + private double mach = Application.getPreferences().getDefaultMach(); private WarningSet warnings = null; @@ -151,7 +154,8 @@ public class RocketInfo implements FigureElement { UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter)); String massText; - if (configuration.hasMotors()) + MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration); + if (mic.hasMotors()) //// Mass with motors massText = trans.get("RocketInfo.massText1") +" "; else diff --git a/swing/src/net/sf/openrocket/gui/print/DesignReport.java b/swing/src/net/sf/openrocket/gui/print/DesignReport.java index 3235b06a3..4ba380511 100644 --- a/swing/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/swing/src/net/sf/openrocket/gui/print/DesignReport.java @@ -16,6 +16,7 @@ import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; @@ -192,7 +193,9 @@ public class DesignReport { canvas.showText("" + rocket.getStageCount()); - if (configuration.hasMotors()) { + + MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration); + if (mic.hasMotors()){ if (configuration.getStageCount() > 1) { canvas.newlineShowText(MASS_WITH_MOTORS); } else { @@ -341,7 +344,8 @@ public class DesignReport { for (RocketComponent c : rocket) { if (c instanceof AxialStage) { - config.setToStage(stage); + config.clearAllStages(); + config.setOnlyStage(stage); stage++; stageMass = massCalc.getCG(config, MassCalcType.LAUNCH_MASS).weight; // Calculate total thrust-to-weight from only lowest stage motors diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 3f7da1fa6..40de6add4 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -23,6 +23,8 @@ import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorInstance; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.MotorMount; @@ -339,13 +341,13 @@ public class RocketFigure extends AbstractScaleFigure { RenderingHints.VALUE_STROKE_NORMALIZE); // Draw motors - String motorID = configuration.getFlightConfigurationID(); Color fillColor = ((SwingPreferences)Application.getPreferences()).getMotorFillColor(); Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor(); - Iterator iterator = configuration.motorIterator(); - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - Motor motor = mount.getMotor(motorID); + + MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration); + for( MotorInstance curInstance : configuration.getActiveMotors(mic)){ + MotorMount mount = curInstance.getMount(); + Motor motor = curInstance.getMotor(); double motorLength = motor.getLength(); double motorRadius = motor.getDiameter() / 2; diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 729391b41..be670ed9a 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -56,6 +56,7 @@ import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.Configuration; @@ -640,7 +641,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change length = maxX - minX; } - for (RocketComponent c : configuration) { + for (RocketComponent c : configuration.getActiveComponents()) { if (c instanceof SymmetricComponent) { double d1 = ((SymmetricComponent) c).getForeRadius() * 2; double d2 = ((SymmetricComponent) c).getAftRadius() * 2; @@ -692,8 +693,10 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // Stop previous computation (if any) stopBackgroundSimulation(); + MotorInstanceConfiguration mic = new MotorInstanceConfiguration( configuration); + // Check that configuration has motors - if (!configuration.hasMotors()) { + if (!mic.hasMotors()){ extraText.setFlightData(FlightData.NaN_DATA); extraText.setCalculatingData(false); return; diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java index c770be0dc..bc0a7371c 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationRunDialog.java @@ -8,6 +8,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.Executors; @@ -30,6 +31,8 @@ import net.sf.openrocket.gui.dialogs.DetailDialog; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.MotorInstance; +import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; @@ -306,11 +309,13 @@ public class SimulationRunDialog extends JDialog { // Calculate estimate of motor burn time double launchBurn = 0; double otherBurn = 0; - Configuration config = simulation.getConfiguration(); String id = simulation.getOptions().getMotorConfigurationID(); - Iterator iterator = config.motorIterator(); - while (iterator.hasNext()) { - MotorMount m = iterator.next(); + + Configuration config = simulation.getConfiguration(); + MotorInstanceConfiguration mic = new MotorInstanceConfiguration(config); + Collection activeMotors = config.getActiveMotors(mic ); + for( MotorInstance curInstance : activeMotors ){ + MotorMount m = curInstance.getMount(); if (m.getIgnitionConfiguration().getDefault().getIgnitionEvent() == IgnitionConfiguration.IgnitionEvent.LAUNCH) launchBurn = MathUtil.max(launchBurn, m.getMotor(id).getBurnTimeEstimate()); else