updates for 0.9.3
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / BodyTube.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6
7 import net.sf.openrocket.motor.Motor;
8 import net.sf.openrocket.util.Coordinate;
9 import net.sf.openrocket.util.MathUtil;
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 {
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         public double getRadius() {
66                 if (autoRadius) {
67                         // Return auto radius from front or rear
68                         double r = -1;
69                         SymmetricComponent c = this.getPreviousSymmetricComponent();
70                         if (c != null) {
71                                 r = c.getFrontAutoRadius();
72                         }
73                         if (r < 0) {
74                                 c = this.getNextSymmetricComponent();
75                                 if (c != null) {
76                                         r = c.getRearAutoRadius();
77                                 }
78                         }
79                         if (r < 0)
80                                 r = DEFAULT_RADIUS;
81                         return r;
82                 }
83                 return radius;
84         }
85         
86         
87         /**
88          * Set the outer radius of the body tube.  If the radius is less than the wall thickness,
89          * the wall thickness is decreased accordingly of the value of the radius.
90          * This method sets the automatic radius off.
91          */
92         public void setRadius(double radius) {
93                 if ((this.radius == radius) && (autoRadius==false))
94                         return;
95                 
96                 this.autoRadius = false;
97                 this.radius = Math.max(radius,0);
98
99                 if (this.thickness > this.radius)
100                         this.thickness = this.radius;
101                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
102         }
103
104
105         /**
106          * Returns whether the radius is selected automatically or not.
107          * Returns false also in case automatic radius selection is not possible.
108          */
109         public boolean isRadiusAutomatic() {
110                 return autoRadius;
111         }
112
113         /**
114          * Sets whether the radius is selected automatically or not.  
115          */
116         public void setRadiusAutomatic(boolean auto) {
117                 if (autoRadius == auto)
118                         return;
119                 
120                 autoRadius = auto;
121                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
122         }
123         
124         
125         @Override
126         public double getAftRadius() {  return getRadius(); }
127         @Override
128         public double getForeRadius() { return getRadius(); }
129         @Override
130         public boolean isAftRadiusAutomatic() { return isRadiusAutomatic();     }
131         @Override
132         public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
133         
134         
135         
136         @Override
137         protected double getFrontAutoRadius() {
138                 if (isRadiusAutomatic()) {
139                         // Search for previous SymmetricComponent
140                         SymmetricComponent c = this.getPreviousSymmetricComponent();
141                         if (c != null) {
142                                 return c.getFrontAutoRadius();
143                         } else {
144                                 return -1;
145                         }
146                 }
147                 return getRadius();
148         }
149
150         @Override
151         protected double getRearAutoRadius() {
152                 if (isRadiusAutomatic()) {
153                         // Search for next SymmetricComponent
154                         SymmetricComponent c = this.getNextSymmetricComponent();
155                         if (c != null) {
156                                 return c.getRearAutoRadius();
157                         } else {
158                                 return -1;
159                         }
160                 }
161                 return getRadius();
162         }
163
164
165         
166         
167         
168         
169         
170         public double getInnerRadius() {
171                 if (filled)
172                         return 0;
173                 return Math.max(getRadius()-thickness, 0);
174         }
175         
176         public void setInnerRadius(double r) {
177                 setThickness(getRadius()-r);
178         }
179         
180         
181         
182         
183         /**
184          * Return the component name.
185          */
186         @Override
187         public String getComponentName() {
188                 return "Body tube";
189         }
190
191
192         /************ Component calculations ***********/
193         
194         // From SymmetricComponent
195         /**
196          * Returns the outer radius at the position x.  This returns the same value as getRadius().
197          */
198         @Override
199         public double getRadius(double x) {
200                 return getRadius();
201         }
202
203         /**
204          * Returns the inner radius at the position x.  If the tube is filled, returns always zero.
205          */
206         @Override
207         public double getInnerRadius(double x) {
208                 if (filled)
209                         return 0.0;
210                 else
211                         return Math.max(getRadius()-thickness,0);
212         }
213         
214
215         /**
216          * Returns the body tube's center of gravity.
217          */
218         @Override
219         public Coordinate getComponentCG() {
220                 return new Coordinate(length/2,0,0,getComponentMass());
221         }
222         
223         /**
224          * Returns the body tube's volume.
225          */
226         @Override
227         public double getComponentVolume() {
228                 double r = getRadius();
229                 if (filled)
230                         return getFilledVolume(r,length);
231                 else
232                         return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
233         }
234         
235         
236         @Override
237         public double getLongitudalUnitInertia() {
238                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
239                 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) +
240                                 MathUtil.pow2(getLength())) / 12;
241         }
242
243         @Override
244         public double getRotationalUnitInertia() {
245                 // 1/2 * (r1^2 + r2^2)
246                 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius()))/2;
247         }
248
249
250         
251
252         /**
253          * Helper function for cylinder volume.
254          */
255         private static double getFilledVolume(double r, double l) {
256                 return Math.PI * r*r * l;
257         }
258
259         
260         /**
261          * Adds bounding coordinates to the given set.  The body tube will fit within the
262          * convex hull of the points.
263          * 
264          * Currently the points are simply a rectangular box around the body tube.
265          */
266         @Override
267         public Collection<Coordinate> getComponentBounds() {
268                 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
269                 double r = getRadius();
270                 addBound(bounds,0,r);
271                 addBound(bounds,length,r);
272                 return bounds;
273         }
274
275
276         ////////////////  Motor mount  /////////////////
277         
278         @Override
279         public boolean isMotorMount() {
280                 return motorMount;
281         }
282
283         @Override
284         public void setMotorMount(boolean mount) {
285                 if (motorMount == mount)
286                         return;
287                 motorMount = mount;
288                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
289         }
290         
291         @Override
292         public Motor getMotor(String id) {
293                 return motors.get(id);
294         }
295
296         @Override
297         public void setMotor(String id, Motor motor) {
298                 Motor current = motors.get(id);
299                 if ((motor == null && current == null) ||
300                                 (motor != null && motor.equals(current)))
301                         return;
302                 motors.put(id, motor);
303                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
304         }
305
306         @Override
307         public double getMotorDelay(String id) {
308                 Double delay = ejectionDelays.get(id);
309                 if (delay == null)
310                         return Motor.PLUGGED;
311                 return delay;
312         }
313
314         @Override
315         public void setMotorDelay(String id, double delay) {
316                 ejectionDelays.put(id, delay);
317                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
318         }
319         
320         @Override
321         public int getMotorCount() {
322                 return 1;
323         }
324         
325         @Override
326         public double getMotorMountDiameter() {
327                 return getInnerRadius()*2;
328         }
329
330         @Override
331         public IgnitionEvent getIgnitionEvent() {
332                 return ignitionEvent;
333         }
334
335         @Override
336         public void setIgnitionEvent(IgnitionEvent event) {
337                 if (ignitionEvent == event)
338                         return;
339                 ignitionEvent = event;
340                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
341         }
342
343         
344         @Override
345         public double getIgnitionDelay() {
346                 return ignitionDelay;
347         }
348
349         @Override
350         public void setIgnitionDelay(double delay) {
351                 if (MathUtil.equals(delay, ignitionDelay))
352                         return;
353                 ignitionDelay = delay;
354                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
355         }
356
357         
358         @Override
359         public double getMotorOverhang() {
360                 return overhang;
361         }
362         
363         @Override
364         public void setMotorOverhang(double overhang) {
365                 if (MathUtil.equals(this.overhang, overhang))
366                         return;
367                 this.overhang = overhang;
368                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
369         }
370
371         
372
373         
374         /*
375          * (non-Javadoc)
376          * Copy the motor and ejection delay HashMaps.
377          * 
378          * @see rocketcomponent.RocketComponent#copy()
379          */
380         @SuppressWarnings("unchecked")
381         @Override
382         public RocketComponent copy() {
383                 RocketComponent c = super.copy();
384                 ((BodyTube)c).motors = (HashMap<String,Motor>) motors.clone();
385                 ((BodyTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();
386                 return c;
387         }
388
389 }