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