1 package net.sf.openrocket.rocketcomponent;
3 import java.util.ArrayList;
4 import java.util.HashMap;
7 import net.sf.openrocket.motor.Motor;
8 import net.sf.openrocket.util.Coordinate;
9 import net.sf.openrocket.util.MathUtil;
13 * This class defines an inner tube that can be used as a motor mount. The component
14 * may also be clustered.
16 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
18 public class InnerTube extends ThicknessRingComponent
19 implements Clusterable, RadialParent, MotorMount {
21 private ClusterConfiguration cluster = ClusterConfiguration.SINGLE;
22 private double clusterScale = 1.0;
23 private double clusterRotation = 0.0;
26 private boolean motorMount = false;
27 private HashMap<String, Double> ejectionDelays = new HashMap<String, Double>();
28 private HashMap<String, Motor> motors = new HashMap<String, Motor>();
29 private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC;
30 private double ignitionDelay = 0;
31 private double overhang = 0;
39 this.setOuterRadius(0.019/2);
40 this.setInnerRadius(0.018/2);
41 this.setLength(0.070);
46 public double getInnerRadius(double x) {
47 return getInnerRadius();
52 public double getOuterRadius(double x) {
53 return getOuterRadius();
58 public String getComponentName() {
63 * Allow all InternalComponents to be added to this component.
66 public boolean isCompatible(Class<? extends RocketComponent> type) {
67 return InternalComponent.class.isAssignableFrom(type);
72 ///////////// Cluster methods //////////////
75 * Get the current cluster configuration.
76 * @return The current cluster configuration.
78 public ClusterConfiguration getClusterConfiguration() {
83 * Set the current cluster configuration.
84 * @param cluster The cluster configuration.
86 public void setClusterConfiguration(ClusterConfiguration cluster) {
87 this.cluster = cluster;
88 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
92 * Return the number of tubes in the cluster.
93 * @return Number of tubes in the current cluster.
96 public int getClusterCount() {
97 return cluster.getClusterCount();
101 * Get the cluster scaling. A value of 1.0 indicates that the tubes are packed
102 * touching each other, larger values separate the tubes and smaller values
103 * pack inside each other.
105 public double getClusterScale() {
110 * Set the cluster scaling.
111 * @see #getClusterScale()
113 public void setClusterScale(double scale) {
114 scale = Math.max(scale,0);
115 if (MathUtil.equals(clusterScale, scale))
117 clusterScale = scale;
118 fireComponentChangeEvent(new ComponentChangeEvent(this,ComponentChangeEvent.MASS_CHANGE));
124 * @return the clusterRotation
126 public double getClusterRotation() {
127 return clusterRotation;
132 * @param rotation the clusterRotation to set
134 public void setClusterRotation(double rotation) {
135 rotation = MathUtil.reduce180(rotation);
136 if (clusterRotation == rotation)
138 this.clusterRotation = rotation;
139 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
144 * Return the distance between the closest two cluster inner tube center points.
145 * This is equivalent to the cluster scale multiplied by the tube diameter.
148 public double getClusterSeparation() {
149 return 2*getOuterRadius()*clusterScale;
153 public List<Coordinate> getClusterPoints() {
154 List<Coordinate> list = new ArrayList<Coordinate>(getClusterCount());
155 List<Double> points = cluster.getPoints(clusterRotation - getRadialDirection());
156 double separation = getClusterSeparation();
157 for (int i=0; i < points.size()/2; i++) {
158 list.add(new Coordinate(0,points.get(2*i)*separation,points.get(2*i+1)*separation));
165 public Coordinate[] shiftCoordinates(Coordinate[] array) {
166 array = super.shiftCoordinates(array);
168 int count = getClusterCount();
172 List<Coordinate> points = getClusterPoints();
173 assert(points.size() == count);
174 Coordinate[] newArray = new Coordinate[array.length * count];
175 for (int i=0; i < array.length; i++) {
176 for (int j=0; j < count; j++) {
177 newArray[i*count + j] = array[i].add(points.get(j));
187 //////////////// Motor mount /////////////////
190 public boolean isMotorMount() {
195 public void setMotorMount(boolean mount) {
196 if (motorMount == mount)
199 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
203 public Motor getMotor(String id) {
207 // Check whether the id is valid for the current rocket
208 RocketComponent root = this.getRoot();
209 if (!(root instanceof Rocket))
211 if (!((Rocket) root).isMotorConfigurationID(id))
214 return motors.get(id);
218 public void setMotor(String id, Motor motor) {
221 throw new IllegalArgumentException("Cannot set non-null motor for id null");
224 Motor current = motors.get(id);
225 if ((motor == null && current == null) ||
226 (motor != null && motor.equals(current)))
228 motors.put(id, motor);
229 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
233 public double getMotorDelay(String id) {
234 Double delay = ejectionDelays.get(id);
236 return Motor.PLUGGED;
241 public void setMotorDelay(String id, double delay) {
242 ejectionDelays.put(id, delay);
243 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
248 public int getMotorCount() {
249 return getClusterCount();
253 public double getMotorMountDiameter() {
254 return getInnerRadius()*2;
258 public IgnitionEvent getIgnitionEvent() {
259 return ignitionEvent;
263 public void setIgnitionEvent(IgnitionEvent event) {
264 if (ignitionEvent == event)
266 ignitionEvent = event;
267 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
272 public double getIgnitionDelay() {
273 return ignitionDelay;
277 public void setIgnitionDelay(double delay) {
278 if (MathUtil.equals(delay, ignitionDelay))
280 ignitionDelay = delay;
281 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
286 public double getMotorOverhang() {
291 public void setMotorOverhang(double overhang) {
292 if (MathUtil.equals(this.overhang, overhang))
294 this.overhang = overhang;
295 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
302 * Copy the motor and ejection delay HashMaps.
304 * @see rocketcomponent.RocketComponent#copy()
306 @SuppressWarnings("unchecked")
308 public RocketComponent copy() {
309 RocketComponent c = super.copy();
310 ((InnerTube)c).motors = (HashMap<String,Motor>) motors.clone();
311 ((InnerTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();