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