DGP - 1st printing
[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() { return isRadiusAutomatic();     }
137         @Override
138         public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
139         
140         
141         
142         @Override
143         protected double getFrontAutoRadius() {
144                 if (isRadiusAutomatic()) {
145                         // Search for previous SymmetricComponent
146                         SymmetricComponent c = this.getPreviousSymmetricComponent();
147                         if (c != null) {
148                                 return c.getFrontAutoRadius();
149                         } else {
150                                 return -1;
151                         }
152                 }
153                 return getOuterRadius();
154         }
155
156         @Override
157         protected double getRearAutoRadius() {
158                 if (isRadiusAutomatic()) {
159                         // Search for next SymmetricComponent
160                         SymmetricComponent c = this.getNextSymmetricComponent();
161                         if (c != null) {
162                                 return c.getRearAutoRadius();
163                         } else {
164                                 return -1;
165                         }
166                 }
167                 return getOuterRadius();
168         }
169
170
171         
172         
173         
174         
175     @Override   
176         public double getInnerRadius() {
177                 if (filled)
178                         return 0;
179                 return Math.max(getOuterRadius()-thickness, 0);
180         }
181         
182     @Override
183         public void setInnerRadius(double r) {
184                 setThickness(getOuterRadius()-r);
185         }
186         
187         
188         
189         
190         /**
191          * Return the component name.
192          */
193         @Override
194         public String getComponentName() {
195                 return "Body tube";
196         }
197
198     /**
199      * Accept a visitor to this BodyTube in the component hierarchy.
200      * 
201      * @param theVisitor  the visitor that will be called back with a reference to this BodyTube
202      */
203     @Override 
204     public void accept (final ComponentVisitor theVisitor) {
205         theVisitor.visit(this);
206     }
207
208
209         /************ Component calculations ***********/
210         
211         // From SymmetricComponent
212         /**
213          * Returns the outer radius at the position x.  This returns the same value as getOuterRadius().
214          */
215         @Override
216         public double getRadius(double x) {
217                 return getOuterRadius();
218         }
219
220         /**
221          * Returns the inner radius at the position x.  If the tube is filled, returns always zero.
222          */
223         @Override
224         public double getInnerRadius(double x) {
225                 if (filled)
226                         return 0.0;
227                 else
228                         return Math.max(getOuterRadius()-thickness,0);
229         }
230         
231
232         /**
233          * Returns the body tube's center of gravity.
234          */
235         @Override
236         public Coordinate getComponentCG() {
237                 return new Coordinate(length/2,0,0,getComponentMass());
238         }
239         
240         /**
241          * Returns the body tube's volume.
242          */
243         @Override
244         public double getComponentVolume() {
245                 double r = getOuterRadius();
246                 if (filled)
247                         return getFilledVolume(r,length);
248                 else
249                         return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
250         }
251         
252         
253         @Override
254         public double getLongitudalUnitInertia() {
255                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
256                 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
257                                 MathUtil.pow2(getLength())) / 12;
258         }
259
260         @Override
261         public double getRotationalUnitInertia() {
262                 // 1/2 * (r1^2 + r2^2)
263                 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
264         }
265
266
267         
268
269         /**
270          * Helper function for cylinder volume.
271          */
272         private static double getFilledVolume(double r, double l) {
273                 return Math.PI * r*r * l;
274         }
275
276         
277         /**
278          * Adds bounding coordinates to the given set.  The body tube will fit within the
279          * convex hull of the points.
280          * 
281          * Currently the points are simply a rectangular box around the body tube.
282          */
283         @Override
284         public Collection<Coordinate> getComponentBounds() {
285                 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
286                 double r = getOuterRadius();
287                 addBound(bounds,0,r);
288                 addBound(bounds,length,r);
289                 return bounds;
290         }
291
292
293         ////////////////  Motor mount  /////////////////
294         
295         @Override
296         public boolean isMotorMount() {
297                 return motorMount;
298         }
299
300         @Override
301         public void setMotorMount(boolean mount) {
302                 if (motorMount == mount)
303                         return;
304                 motorMount = mount;
305                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
306         }
307         
308         @Override
309         public Motor getMotor(String id) {
310                 if (id == null)
311                         return null;
312                 
313                 // Check whether the id is valid for the current rocket
314                 RocketComponent root = this.getRoot();
315                 if (!(root instanceof Rocket))
316                         return null;
317                 if (!((Rocket) root).isMotorConfigurationID(id))
318                         return null;
319                 
320                 return motors.get(id);
321         }
322
323         @Override
324         public void setMotor(String id, Motor motor) {
325                 if (id == null) {
326                         if (motor != null) {
327                                 throw new IllegalArgumentException("Cannot set non-null motor for id null");
328                         }
329                 }
330                 Motor current = motors.get(id);
331                 if ((motor == null && current == null) ||
332                                 (motor != null && motor.equals(current)))
333                         return;
334                 motors.put(id, motor);
335                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
336         }
337
338         @Override
339         public double getMotorDelay(String id) {
340                 Double delay = ejectionDelays.get(id);
341                 if (delay == null)
342                         return Motor.PLUGGED;
343                 return delay;
344         }
345
346         @Override
347         public void setMotorDelay(String id, double delay) {
348                 ejectionDelays.put(id, delay);
349                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
350         }
351         
352         @Override
353         public int getMotorCount() {
354                 return 1;
355         }
356         
357         @Override
358         public double getMotorMountDiameter() {
359                 return getInnerRadius()*2;
360         }
361
362         @Override
363         public IgnitionEvent getIgnitionEvent() {
364                 return ignitionEvent;
365         }
366
367         @Override
368         public void setIgnitionEvent(IgnitionEvent event) {
369                 if (ignitionEvent == event)
370                         return;
371                 ignitionEvent = event;
372                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
373         }
374
375         
376         @Override
377         public double getIgnitionDelay() {
378                 return ignitionDelay;
379         }
380
381         @Override
382         public void setIgnitionDelay(double delay) {
383                 if (MathUtil.equals(delay, ignitionDelay))
384                         return;
385                 ignitionDelay = delay;
386                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
387         }
388
389         
390         @Override
391         public double getMotorOverhang() {
392                 return overhang;
393         }
394         
395         @Override
396         public void setMotorOverhang(double overhang) {
397                 if (MathUtil.equals(this.overhang, overhang))
398                         return;
399                 this.overhang = overhang;
400                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
401         }
402
403
404         @Override
405         public Coordinate getMotorPosition(String id) {
406                 Motor motor = motors.get(id);
407                 if (motor == null) {
408                         throw new IllegalArgumentException("No motor with id " + id + " defined.");
409                 }
410                 
411                 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
412         }
413
414         
415
416         
417         /*
418          * (non-Javadoc)
419          * Copy the motor and ejection delay HashMaps.
420          * 
421          * @see rocketcomponent.RocketComponent#copy()
422          */
423         @SuppressWarnings("unchecked")
424         @Override
425         public RocketComponent copy() {
426                 RocketComponent c = super.copy();
427                 ((BodyTube)c).motors = (HashMap<String,Motor>) motors.clone();
428                 ((BodyTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();
429                 return c;
430         }
431 }