added svn:ignores
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / BodyTube.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import net.sf.openrocket.motor.Motor;
4 import net.sf.openrocket.util.Coordinate;
5 import net.sf.openrocket.util.MathUtil;
6
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.HashMap;
10
11
12 /**
13  * Rocket body tube component.  Has only two parameters, a radius and length.
14  *
15  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
16  */
17
18 public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial {
19
20         private double radius = 0;
21         private boolean autoRadius = false; // Radius chosen automatically based on parent component
22
23         // When changing the inner radius, thickness is modified
24
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;
31
32
33
34         public BodyTube() {
35                 super();
36                 this.length = 8 * DEFAULT_RADIUS;
37                 this.radius = DEFAULT_RADIUS;
38                 this.autoRadius = true;
39         }
40
41         public BodyTube(double length, double radius) {
42                 super();
43                 this.radius = Math.max(radius, 0);
44                 this.length = Math.max(length, 0);
45         }
46
47
48         public BodyTube(double length, double radius, boolean filled) {
49                 this(length, radius);
50                 this.filled = filled;
51         }
52
53         public BodyTube(double length, double radius, double thickness) {
54                 this(length, radius);
55                 this.filled = false;
56                 this.thickness = thickness;
57         }
58
59
60         /************  Get/set component parameter methods ************/
61
62         /**
63          * Return the outer radius of the body tube.
64      *
65      * @return  the outside radius of the tube
66          */
67     @Override
68         public double getOuterRadius () {
69                 if (autoRadius) {
70                         // Return auto radius from front or rear
71                         double r = -1;
72                         SymmetricComponent c = this.getPreviousSymmetricComponent();
73                         if (c != null) {
74                                 r = c.getFrontAutoRadius();
75                         }
76                         if (r < 0) {
77                                 c = this.getNextSymmetricComponent();
78                                 if (c != null) {
79                                         r = c.getRearAutoRadius();
80                                 }
81                         }
82                         if (r < 0)
83                                 r = DEFAULT_RADIUS;
84                         return r;
85                 }
86                 return radius;
87         }
88
89
90         /**
91          * Set the outer radius of the body tube.  If the radius is less than the wall thickness,
92          * the wall thickness is decreased accordingly of the value of the radius.
93          * This method sets the automatic radius off.
94      *
95      * @param radius  the outside radius in standard units
96          */
97     @Override
98         public void setOuterRadius (double radius) {
99                 if ((this.radius == radius) && (autoRadius == false))
100                         return;
101
102                 this.autoRadius = false;
103                 this.radius = Math.max(radius, 0);
104
105                 if (this.thickness > this.radius)
106                         this.thickness = this.radius;
107                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
108         }
109
110
111         /**
112          * Returns whether the radius is selected automatically or not.
113          * Returns false also in case automatic radius selection is not possible.
114          */
115         public boolean isRadiusAutomatic() {
116                 return autoRadius;
117         }
118
119         /**
120          * Sets whether the radius is selected automatically or not.
121          */
122         public void setRadiusAutomatic(boolean auto) {
123                 if (autoRadius == auto)
124                         return;
125
126                 autoRadius = auto;
127                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
128         }
129
130
131         @Override
132         public double getAftRadius() {  return getOuterRadius(); }
133         @Override
134         public double getForeRadius() { return getOuterRadius(); }
135         @Override
136         public boolean isAftRadiusAutomatic() {
137                 return isRadiusAutomatic();
138         }
139
140         @Override
141         public boolean isForeRadiusAutomatic() {
142                 return isRadiusAutomatic();
143         }
144
145
146
147         @Override
148         protected double getFrontAutoRadius() {
149                 if (isRadiusAutomatic()) {
150                         // Search for previous SymmetricComponent
151                         SymmetricComponent c = this.getPreviousSymmetricComponent();
152                         if (c != null) {
153                                 return c.getFrontAutoRadius();
154                         } else {
155                                 return -1;
156                         }
157                 }
158                 return getOuterRadius();
159         }
160
161         @Override
162         protected double getRearAutoRadius() {
163                 if (isRadiusAutomatic()) {
164                         // Search for next SymmetricComponent
165                         SymmetricComponent c = this.getNextSymmetricComponent();
166                         if (c != null) {
167                                 return c.getRearAutoRadius();
168                         } else {
169                                 return -1;
170                         }
171                 }
172                 return getOuterRadius();
173         }
174
175
176
177
178
179
180
181
182     @Override
183         public double getInnerRadius() {
184                 if (filled)
185                         return 0;
186                 return Math.max(getOuterRadius()-thickness, 0);
187         }
188
189     @Override
190         public void setInnerRadius(double r) {
191                 setThickness(getOuterRadius()-r);
192         }
193
194
195
196
197         /**
198          * Return the component name.
199          */
200         @Override
201         public String getComponentName() {
202                 return "Body tube";
203         }
204
205
206         /************ Component calculations ***********/
207
208         // From SymmetricComponent
209         /**
210          * Returns the outer radius at the position x.  This returns the same value as getOuterRadius().
211          */
212         @Override
213         public double getRadius(double x) {
214                 return getOuterRadius();
215         }
216
217         /**
218          * Returns the inner radius at the position x.  If the tube is filled, returns always zero.
219          */
220         @Override
221         public double getInnerRadius(double x) {
222                 if (filled)
223                         return 0.0;
224                 else
225                         return Math.max(getOuterRadius()-thickness,0);
226         }
227
228
229         /**
230          * Returns the body tube's center of gravity.
231          */
232         @Override
233         public Coordinate getComponentCG() {
234                 return new Coordinate(length / 2, 0, 0, getComponentMass());
235         }
236
237         /**
238          * Returns the body tube's volume.
239          */
240         @Override
241         public double getComponentVolume() {
242                 double r = getOuterRadius();
243                 if (filled)
244                         return getFilledVolume(r, length);
245                 else
246                         return getFilledVolume(r, length) - getFilledVolume(getInnerRadius(0), length);
247         }
248
249
250         @Override
251         public double getLongitudinalUnitInertia() {
252                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
253                 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
254                                 MathUtil.pow2(getLength())) / 12;
255         }
256
257         @Override
258         public double getRotationalUnitInertia() {
259                 // 1/2 * (r1^2 + r2^2)
260                 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
261         }
262
263
264
265
266         /**
267          * Helper function for cylinder volume.
268          */
269         private static double getFilledVolume(double r, double l) {
270                 return Math.PI * r * r * l;
271         }
272
273
274         /**
275          * Adds bounding coordinates to the given set.  The body tube will fit within the
276          * convex hull of the points.
277          *
278          * Currently the points are simply a rectangular box around the body tube.
279          */
280         @Override
281         public Collection<Coordinate> getComponentBounds() {
282                 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
283                 double r = getOuterRadius();
284                 addBound(bounds, 0, r);
285                 addBound(bounds, length, r);
286                 return bounds;
287         }
288
289
290
291         /**
292          * Check whether the given type can be added to this component.  BodyTubes allow any
293          * InternalComponents or ExternalComponents, excluding BodyComponents, to be added.
294          *
295          * @param type  The RocketComponent class type to add.
296          * @return      Whether such a component can be added.
297          */
298         @Override
299         public boolean isCompatible(Class<? extends RocketComponent> type) {
300                 if (InternalComponent.class.isAssignableFrom(type))
301                         return true;
302                 if (ExternalComponent.class.isAssignableFrom(type) &&
303                                 !BodyComponent.class.isAssignableFrom(type))
304                         return true;
305                 return false;
306         }
307
308         ////////////////  Motor mount  /////////////////
309
310         @Override
311         public boolean isMotorMount() {
312                 return motorMount;
313         }
314
315         @Override
316         public void setMotorMount(boolean mount) {
317                 if (motorMount == mount)
318                         return;
319                 motorMount = mount;
320                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
321         }
322
323         @Override
324         public Motor getMotor(String id) {
325                 if (id == null)
326                         return null;
327
328                 // Check whether the id is valid for the current rocket
329                 RocketComponent root = this.getRoot();
330                 if (!(root instanceof Rocket))
331                         return null;
332                 if (!((Rocket) root).isMotorConfigurationID(id))
333                         return null;
334
335                 return motors.get(id);
336         }
337
338         @Override
339         public void setMotor(String id, Motor motor) {
340                 if (id == null) {
341                         if (motor != null) {
342                                 throw new IllegalArgumentException("Cannot set non-null motor for id null");
343                         }
344                 }
345                 Motor current = motors.get(id);
346                 if ((motor == null && current == null) ||
347                                 (motor != null && motor.equals(current)))
348                         return;
349                 motors.put(id, motor);
350                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
351         }
352
353         @Override
354         public double getMotorDelay(String id) {
355                 Double delay = ejectionDelays.get(id);
356                 if (delay == null)
357                         return Motor.PLUGGED;
358                 return delay;
359         }
360
361         @Override
362         public void setMotorDelay(String id, double delay) {
363                 ejectionDelays.put(id, delay);
364                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
365         }
366
367         @Override
368         public int getMotorCount() {
369                 return 1;
370         }
371
372         @Override
373         public double getMotorMountDiameter() {
374                 return getInnerRadius() * 2;
375         }
376
377         @Override
378         public IgnitionEvent getIgnitionEvent() {
379                 return ignitionEvent;
380         }
381
382         @Override
383         public void setIgnitionEvent(IgnitionEvent event) {
384                 if (ignitionEvent == event)
385                         return;
386                 ignitionEvent = event;
387                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
388         }
389
390
391         @Override
392         public double getIgnitionDelay() {
393                 return ignitionDelay;
394         }
395
396         @Override
397         public void setIgnitionDelay(double delay) {
398                 if (MathUtil.equals(delay, ignitionDelay))
399                         return;
400                 ignitionDelay = delay;
401                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
402         }
403
404
405         @Override
406         public double getMotorOverhang() {
407                 return overhang;
408         }
409
410         @Override
411         public void setMotorOverhang(double overhang) {
412                 if (MathUtil.equals(this.overhang, overhang))
413                         return;
414                 this.overhang = overhang;
415                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
416         }
417
418
419         @Override
420         public Coordinate getMotorPosition(String id) {
421                 Motor motor = motors.get(id);
422                 if (motor == null) {
423                         throw new IllegalArgumentException("No motor with id " + id + " defined.");
424                 }
425
426                 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
427         }
428
429
430
431
432         /*
433          * (non-Javadoc)
434          * Copy the motor and ejection delay HashMaps.
435          *
436          * @see rocketcomponent.RocketComponent#copy()
437          */
438         @SuppressWarnings("unchecked")
439         @Override
440         protected RocketComponent copyWithOriginalID() {
441                 RocketComponent c = super.copyWithOriginalID();
442                 ((BodyTube) c).motors = (HashMap<String, Motor>) motors.clone();
443                 ((BodyTube) c).ejectionDelays = (HashMap<String, Double>) ejectionDelays.clone();
444                 return c;
445         }
446 }