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