1 package net.sf.openrocket.rocketcomponent;
3 import java.util.ArrayList;
4 import java.util.HashMap;
7 import net.sf.openrocket.util.Coordinate;
8 import net.sf.openrocket.util.MathUtil;
12 * This class defines an inner tube that can be used as a motor mount. The component
13 * may also be clustered.
15 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
17 public class InnerTube extends ThicknessRingComponent
18 implements Clusterable, RadialParent, MotorMount {
20 private ClusterConfiguration cluster = ClusterConfiguration.SINGLE;
21 private double clusterScale = 1.0;
22 private double clusterRotation = 0.0;
25 private boolean motorMount = false;
26 private HashMap<String, Double> ejectionDelays = new HashMap<String, Double>();
27 private HashMap<String, Motor> motors = new HashMap<String, Motor>();
28 private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC;
29 private double ignitionDelay = 0;
30 private double overhang = 0;
38 this.setOuterRadius(0.019/2);
39 this.setInnerRadius(0.018/2);
40 this.setLength(0.070);
45 public double getInnerRadius(double x) {
46 return getInnerRadius();
51 public double getOuterRadius(double x) {
52 return getOuterRadius();
57 public String getComponentName() {
62 * Allow all InternalComponents to be added to this component.
65 public boolean isCompatible(Class<? extends RocketComponent> type) {
66 return InternalComponent.class.isAssignableFrom(type);
71 ///////////// Cluster methods //////////////
74 * Get the current cluster configuration.
75 * @return The current cluster configuration.
77 public ClusterConfiguration getClusterConfiguration() {
82 * Set the current cluster configuration.
83 * @param cluster The cluster configuration.
85 public void setClusterConfiguration(ClusterConfiguration cluster) {
86 this.cluster = cluster;
87 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
91 * Return the number of tubes in the cluster.
92 * @return Number of tubes in the current cluster.
95 public int getClusterCount() {
96 return cluster.getClusterCount();
100 * Get the cluster scaling. A value of 1.0 indicates that the tubes are packed
101 * touching each other, larger values separate the tubes and smaller values
102 * pack inside each other.
104 public double getClusterScale() {
109 * Set the cluster scaling.
110 * @see #getClusterScale()
112 public void setClusterScale(double scale) {
113 scale = Math.max(scale,0);
114 if (MathUtil.equals(clusterScale, scale))
116 clusterScale = scale;
117 fireComponentChangeEvent(new ComponentChangeEvent(this,ComponentChangeEvent.MASS_CHANGE));
123 * @return the clusterRotation
125 public double getClusterRotation() {
126 return clusterRotation;
131 * @param rotation the clusterRotation to set
133 public void setClusterRotation(double rotation) {
134 rotation = MathUtil.reduce180(rotation);
135 if (clusterRotation == rotation)
137 this.clusterRotation = rotation;
138 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
143 * Return the distance between the closest two cluster inner tube center points.
144 * This is equivalent to the cluster scale multiplied by the tube diameter.
147 public double getClusterSeparation() {
148 return 2*getOuterRadius()*clusterScale;
152 public List<Coordinate> getClusterPoints() {
153 List<Coordinate> list = new ArrayList<Coordinate>(getClusterCount());
154 List<Double> points = cluster.getPoints(clusterRotation - getRadialDirection());
155 double separation = getClusterSeparation();
156 for (int i=0; i < points.size()/2; i++) {
157 list.add(new Coordinate(0,points.get(2*i)*separation,points.get(2*i+1)*separation));
164 public Coordinate[] shiftCoordinates(Coordinate[] array) {
165 array = super.shiftCoordinates(array);
167 int count = getClusterCount();
171 List<Coordinate> points = getClusterPoints();
172 assert(points.size() == count);
173 Coordinate[] newArray = new Coordinate[array.length * count];
174 for (int i=0; i < array.length; i++) {
175 for (int j=0; j < count; j++) {
176 newArray[i*count + j] = array[i].add(points.get(j));
186 //////////////// Motor mount /////////////////
189 public boolean isMotorMount() {
194 public void setMotorMount(boolean mount) {
195 if (motorMount == mount)
198 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
202 public Motor getMotor(String id) {
203 return motors.get(id);
207 public void setMotor(String id, Motor motor) {
208 Motor current = motors.get(id);
209 if ((motor == null && current == null) ||
210 (motor != null && motor.equals(current)))
212 motors.put(id, motor);
213 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
217 public double getMotorDelay(String id) {
218 Double delay = ejectionDelays.get(id);
220 return Motor.PLUGGED;
225 public void setMotorDelay(String id, double delay) {
226 ejectionDelays.put(id, delay);
227 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
231 public int getMotorCount() {
232 return getClusterCount();
236 public double getMotorMountDiameter() {
237 return getInnerRadius()*2;
241 public IgnitionEvent getIgnitionEvent() {
242 return ignitionEvent;
246 public void setIgnitionEvent(IgnitionEvent event) {
247 if (ignitionEvent == event)
249 ignitionEvent = event;
250 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
255 public double getIgnitionDelay() {
256 return ignitionDelay;
260 public void setIgnitionDelay(double delay) {
261 if (MathUtil.equals(delay, ignitionDelay))
263 ignitionDelay = delay;
264 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
269 public double getMotorOverhang() {
274 public void setMotorOverhang(double overhang) {
275 if (MathUtil.equals(this.overhang, overhang))
277 this.overhang = overhang;
278 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
285 * Copy the motor and ejection delay HashMaps.
287 * @see rocketcomponent.RocketComponent#copy()
289 @SuppressWarnings("unchecked")
291 public RocketComponent copy() {
292 RocketComponent c = super.copy();
293 ((InnerTube)c).motors = (HashMap<String,Motor>) motors.clone();
294 ((InnerTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();