create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / rocketcomponent / InnerTube.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6
7 import net.sf.openrocket.l10n.Translator;
8 import net.sf.openrocket.motor.Motor;
9 import net.sf.openrocket.preset.ComponentPreset;
10 import net.sf.openrocket.startup.Application;
11 import net.sf.openrocket.util.BugException;
12 import net.sf.openrocket.util.Coordinate;
13 import net.sf.openrocket.util.MathUtil;
14
15
16 /**
17  * This class defines an inner tube that can be used as a motor mount.  The component
18  * may also be clustered.
19  *
20  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
21  */
22 public class InnerTube extends ThicknessRingComponent
23                 implements Clusterable, RadialParent, MotorMount {
24         private static final Translator trans = Application.getTranslator();
25         
26         private ClusterConfiguration cluster = ClusterConfiguration.SINGLE;
27         private double clusterScale = 1.0;
28         private double clusterRotation = 0.0;
29         
30
31         private boolean motorMount = false;
32         private HashMap<String, Double> ejectionDelays = new HashMap<String, Double>();
33         private HashMap<String, Motor> motors = new HashMap<String, Motor>();
34         private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC;
35         private double ignitionDelay = 0;
36         private double overhang = 0;
37         
38         
39         /**
40          * Main constructor.
41          */
42         public InnerTube() {
43                 // A-C motor size:
44                 this.setOuterRadius(0.019 / 2);
45                 this.setInnerRadius(0.018 / 2);
46                 this.setLength(0.070);
47         }
48         
49         
50         @Override
51         public double getInnerRadius(double x) {
52                 return getInnerRadius();
53         }
54         
55         
56         @Override
57         public double getOuterRadius(double x) {
58                 return getOuterRadius();
59         }
60         
61         
62         @Override
63         public String getComponentName() {
64                 //// Inner Tube
65                 return trans.get("InnerTube.InnerTube");
66         }
67         
68         @Override
69         public boolean allowsChildren() {
70                 return true;
71         }
72         
73         /**
74          * Allow all InternalComponents to be added to this component.
75          */
76         @Override
77         public boolean isCompatible(Class<? extends RocketComponent> type) {
78                 return InternalComponent.class.isAssignableFrom(type);
79         }
80         
81         @Override
82         public ComponentPreset.Type getPresetType() {
83                 return ComponentPreset.Type.BODY_TUBE;
84         }
85
86         @Override
87         protected void loadFromPreset(ComponentPreset preset) {
88                 if ( preset.has(ComponentPreset.OUTER_DIAMETER) )  {
89                         double outerDiameter = preset.get(ComponentPreset.OUTER_DIAMETER);
90                         this.outerRadius = outerDiameter/2.0;
91                         if ( preset.has(ComponentPreset.INNER_DIAMETER) ) {
92                                 double innerDiameter = preset.get(ComponentPreset.INNER_DIAMETER);
93                                 this.thickness = (outerDiameter-innerDiameter) / 2.0;
94                         }
95                 }
96
97                 super.loadFromPreset(preset);
98
99                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
100         }
101
102
103         /////////////  Cluster methods  //////////////
104         
105         /**
106          * Get the current cluster configuration.
107          * @return  The current cluster configuration.
108          */
109         @Override
110         public ClusterConfiguration getClusterConfiguration() {
111                 return cluster;
112         }
113         
114         /**
115          * Set the current cluster configuration.
116          * @param cluster  The cluster configuration.
117          */
118         @Override
119         public void setClusterConfiguration(ClusterConfiguration cluster) {
120                 this.cluster = cluster;
121                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
122         }
123         
124         /**
125          * Return the number of tubes in the cluster.
126          * @return Number of tubes in the current cluster.
127          */
128         @Override
129         public int getClusterCount() {
130                 return cluster.getClusterCount();
131         }
132         
133         /**
134          * Get the cluster scaling.  A value of 1.0 indicates that the tubes are packed
135          * touching each other, larger values separate the tubes and smaller values
136          * pack inside each other.
137          */
138         public double getClusterScale() {
139                 return clusterScale;
140         }
141         
142         /**
143          * Set the cluster scaling.
144          * @see #getClusterScale()
145          */
146         public void setClusterScale(double scale) {
147                 scale = Math.max(scale, 0);
148                 if (MathUtil.equals(clusterScale, scale))
149                         return;
150                 clusterScale = scale;
151                 fireComponentChangeEvent(new ComponentChangeEvent(this, ComponentChangeEvent.MASS_CHANGE));
152         }
153         
154         
155
156         /**
157          * @return the clusterRotation
158          */
159         public double getClusterRotation() {
160                 return clusterRotation;
161         }
162         
163         
164         /**
165          * @param rotation the clusterRotation to set
166          */
167         public void setClusterRotation(double rotation) {
168                 rotation = MathUtil.reduce180(rotation);
169                 if (clusterRotation == rotation)
170                         return;
171                 this.clusterRotation = rotation;
172                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
173         }
174         
175         
176         /**
177          * Return the distance between the closest two cluster inner tube center points.
178          * This is equivalent to the cluster scale multiplied by the tube diameter.
179          */
180         @Override
181         public double getClusterSeparation() {
182                 return 2 * getOuterRadius() * clusterScale;
183         }
184         
185         
186         public List<Coordinate> getClusterPoints() {
187                 List<Coordinate> list = new ArrayList<Coordinate>(getClusterCount());
188                 List<Double> points = cluster.getPoints(clusterRotation - getRadialDirection());
189                 double separation = getClusterSeparation();
190                 for (int i = 0; i < points.size() / 2; i++) {
191                         list.add(new Coordinate(0, points.get(2 * i) * separation, points.get(2 * i + 1) * separation));
192                 }
193                 return list;
194         }
195         
196         
197         @Override
198         public Coordinate[] shiftCoordinates(Coordinate[] array) {
199                 array = super.shiftCoordinates(array);
200                 
201                 int count = getClusterCount();
202                 if (count == 1)
203                         return array;
204                 
205                 List<Coordinate> points = getClusterPoints();
206                 if (points.size() != count) {
207                         throw new BugException("Inconsistent cluster configuration, cluster count=" + count +
208                                         " point count=" + points.size());
209                 }
210                 Coordinate[] newArray = new Coordinate[array.length * count];
211                 for (int i = 0; i < array.length; i++) {
212                         for (int j = 0; j < count; j++) {
213                                 newArray[i * count + j] = array[i].add(points.get(j));
214                         }
215                 }
216                 
217                 return newArray;
218         }
219         
220         
221
222
223         ////////////////  Motor mount  /////////////////
224         
225         @Override
226         public boolean isMotorMount() {
227                 return motorMount;
228         }
229         
230         @Override
231         public void setMotorMount(boolean mount) {
232                 if (motorMount == mount)
233                         return;
234                 motorMount = mount;
235                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
236         }
237         
238         @Override
239         public Motor getMotor(String id) {
240                 if (id == null)
241                         return null;
242                 
243                 // Check whether the id is valid for the current rocket
244                 RocketComponent root = this.getRoot();
245                 if (!(root instanceof Rocket))
246                         return null;
247                 if (!((Rocket) root).isMotorConfigurationID(id))
248                         return null;
249                 
250                 return motors.get(id);
251         }
252         
253         @Override
254         public void setMotor(String id, Motor motor) {
255                 if (id == null) {
256                         if (motor != null) {
257                                 throw new IllegalArgumentException("Cannot set non-null motor for id null");
258                         }
259                 }
260                 Motor current = motors.get(id);
261                 if ((motor == null && current == null) ||
262                                 (motor != null && motor.equals(current)))
263                         return;
264                 motors.put(id, motor);
265                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
266         }
267         
268         @Override
269         public double getMotorDelay(String id) {
270                 Double delay = ejectionDelays.get(id);
271                 if (delay == null)
272                         return Motor.PLUGGED;
273                 return delay;
274         }
275         
276         @Override
277         public void setMotorDelay(String id, double delay) {
278                 ejectionDelays.put(id, delay);
279                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
280         }
281         
282         @Deprecated
283         @Override
284         public int getMotorCount() {
285                 return getClusterCount();
286         }
287         
288         @Override
289         public double getMotorMountDiameter() {
290                 return getInnerRadius() * 2;
291         }
292         
293         @Override
294         public IgnitionEvent getIgnitionEvent() {
295                 return ignitionEvent;
296         }
297         
298         @Override
299         public void setIgnitionEvent(IgnitionEvent event) {
300                 if (ignitionEvent == event)
301                         return;
302                 ignitionEvent = event;
303                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
304         }
305         
306         
307         @Override
308         public double getIgnitionDelay() {
309                 return ignitionDelay;
310         }
311         
312         @Override
313         public void setIgnitionDelay(double delay) {
314                 if (MathUtil.equals(delay, ignitionDelay))
315                         return;
316                 ignitionDelay = delay;
317                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
318         }
319         
320         
321         @Override
322         public double getMotorOverhang() {
323                 return overhang;
324         }
325         
326         @Override
327         public void setMotorOverhang(double overhang) {
328                 if (MathUtil.equals(this.overhang, overhang))
329                         return;
330                 this.overhang = overhang;
331                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
332         }
333         
334         
335         @Override
336         public Coordinate getMotorPosition(String id) {
337                 Motor motor = motors.get(id);
338                 if (motor == null) {
339                         throw new IllegalArgumentException("No motor with id " + id + " defined.");
340                 }
341                 
342                 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
343         }
344         
345         /*
346          * (non-Javadoc)
347          * Copy the motor and ejection delay HashMaps.
348          *
349          * @see rocketcomponent.RocketComponent#copy()
350          */
351         @SuppressWarnings("unchecked")
352         @Override
353         protected RocketComponent copyWithOriginalID() {
354                 RocketComponent c = super.copyWithOriginalID();
355                 ((InnerTube) c).motors = (HashMap<String, Motor>) motors.clone();
356                 ((InnerTube) c).ejectionDelays = (HashMap<String, Double>) ejectionDelays.clone();
357                 return c;
358         }
359         
360 }