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