DGP - merged printing support from branch
[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      * Accept a visitor to this BodyTube in the component hierarchy.
207      * 
208      * @param theVisitor  the visitor that will be called back with a reference to this BodyTube
209      */
210     @Override 
211     public void accept (final ComponentVisitor theVisitor) {
212         theVisitor.visit(this);
213     }
214
215
216         /************ Component calculations ***********/
217         
218         // From SymmetricComponent
219         /**
220          * Returns the outer radius at the position x.  This returns the same value as getOuterRadius().
221          */
222         @Override
223         public double getRadius(double x) {
224                 return getOuterRadius();
225         }
226         
227         /**
228          * Returns the inner radius at the position x.  If the tube is filled, returns always zero.
229          */
230         @Override
231         public double getInnerRadius(double x) {
232                 if (filled)
233                         return 0.0;
234                 else
235                         return Math.max(getOuterRadius()-thickness,0);
236         }
237         
238         
239         /**
240          * Returns the body tube's center of gravity.
241          */
242         @Override
243         public Coordinate getComponentCG() {
244                 return new Coordinate(length / 2, 0, 0, getComponentMass());
245         }
246         
247         /**
248          * Returns the body tube's volume.
249          */
250         @Override
251         public double getComponentVolume() {
252                 double r = getOuterRadius();
253                 if (filled)
254                         return getFilledVolume(r, length);
255                 else
256                         return getFilledVolume(r, length) - getFilledVolume(getInnerRadius(0), length);
257         }
258         
259         
260         @Override
261         public double getLongitudalUnitInertia() {
262                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
263                 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
264                                 MathUtil.pow2(getLength())) / 12;
265         }
266         
267         @Override
268         public double getRotationalUnitInertia() {
269                 // 1/2 * (r1^2 + r2^2)
270                 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
271         }
272         
273         
274
275
276         /**
277          * Helper function for cylinder volume.
278          */
279         private static double getFilledVolume(double r, double l) {
280                 return Math.PI * r * r * l;
281         }
282         
283         
284         /**
285          * Adds bounding coordinates to the given set.  The body tube will fit within the
286          * convex hull of the points.
287          * 
288          * Currently the points are simply a rectangular box around the body tube.
289          */
290         @Override
291         public Collection<Coordinate> getComponentBounds() {
292                 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
293                 double r = getOuterRadius();
294                 addBound(bounds, 0, r);
295                 addBound(bounds, length, r);
296                 return bounds;
297         }
298         
299         
300
301         /**
302          * Check whether the given type can be added to this component.  BodyTubes allow any
303          * InternalComponents or ExternalComponents, excluding BodyComponents, to be added.
304          * 
305          * @param type  The RocketComponent class type to add.
306          * @return      Whether such a component can be added.
307          */
308         @Override
309         public boolean isCompatible(Class<? extends RocketComponent> type) {
310                 if (InternalComponent.class.isAssignableFrom(type))
311                         return true;
312                 if (ExternalComponent.class.isAssignableFrom(type) &&
313                                 !BodyComponent.class.isAssignableFrom(type))
314                         return true;
315                 return false;
316         }
317         
318         ////////////////  Motor mount  /////////////////
319         
320         @Override
321         public boolean isMotorMount() {
322                 return motorMount;
323         }
324         
325         @Override
326         public void setMotorMount(boolean mount) {
327                 if (motorMount == mount)
328                         return;
329                 motorMount = mount;
330                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
331         }
332         
333         @Override
334         public Motor getMotor(String id) {
335                 if (id == null)
336                         return null;
337                 
338                 // Check whether the id is valid for the current rocket
339                 RocketComponent root = this.getRoot();
340                 if (!(root instanceof Rocket))
341                         return null;
342                 if (!((Rocket) root).isMotorConfigurationID(id))
343                         return null;
344                 
345                 return motors.get(id);
346         }
347         
348         @Override
349         public void setMotor(String id, Motor motor) {
350                 if (id == null) {
351                         if (motor != null) {
352                                 throw new IllegalArgumentException("Cannot set non-null motor for id null");
353                         }
354                 }
355                 Motor current = motors.get(id);
356                 if ((motor == null && current == null) ||
357                                 (motor != null && motor.equals(current)))
358                         return;
359                 motors.put(id, motor);
360                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
361         }
362         
363         @Override
364         public double getMotorDelay(String id) {
365                 Double delay = ejectionDelays.get(id);
366                 if (delay == null)
367                         return Motor.PLUGGED;
368                 return delay;
369         }
370         
371         @Override
372         public void setMotorDelay(String id, double delay) {
373                 ejectionDelays.put(id, delay);
374                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
375         }
376         
377         @Override
378         public int getMotorCount() {
379                 return 1;
380         }
381         
382         @Override
383         public double getMotorMountDiameter() {
384                 return getInnerRadius() * 2;
385         }
386         
387         @Override
388         public IgnitionEvent getIgnitionEvent() {
389                 return ignitionEvent;
390         }
391         
392         @Override
393         public void setIgnitionEvent(IgnitionEvent event) {
394                 if (ignitionEvent == event)
395                         return;
396                 ignitionEvent = event;
397                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
398         }
399         
400         
401         @Override
402         public double getIgnitionDelay() {
403                 return ignitionDelay;
404         }
405         
406         @Override
407         public void setIgnitionDelay(double delay) {
408                 if (MathUtil.equals(delay, ignitionDelay))
409                         return;
410                 ignitionDelay = delay;
411                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
412         }
413         
414         
415         @Override
416         public double getMotorOverhang() {
417                 return overhang;
418         }
419         
420         @Override
421         public void setMotorOverhang(double overhang) {
422                 if (MathUtil.equals(this.overhang, overhang))
423                         return;
424                 this.overhang = overhang;
425                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
426         }
427         
428         
429         @Override
430         public Coordinate getMotorPosition(String id) {
431                 Motor motor = motors.get(id);
432                 if (motor == null) {
433                         throw new IllegalArgumentException("No motor with id " + id + " defined.");
434                 }
435                 
436                 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
437         }
438         
439         
440
441
442         /*
443          * (non-Javadoc)
444          * Copy the motor and ejection delay HashMaps.
445          * 
446          * @see rocketcomponent.RocketComponent#copy()
447          */
448         @SuppressWarnings("unchecked")
449         @Override
450         protected RocketComponent copyWithOriginalID() {
451                 RocketComponent c = super.copyWithOriginalID();
452                 ((BodyTube) c).motors = (HashMap<String, Motor>) motors.clone();
453                 ((BodyTube) c).ejectionDelays = (HashMap<String, Double>) ejectionDelays.clone();
454                 return c;
455         }
456 }