Undo/redo system enhancements, DnD for component tree, bug fixes
[debian/openrocket] / 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.motor.Motor;
8 import net.sf.openrocket.util.Coordinate;
9 import net.sf.openrocket.util.MathUtil;
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 {
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         public double getRadius() {
66                 if (autoRadius) {
67                         // Return auto radius from front or rear
68                         double r = -1;
69                         SymmetricComponent c = this.getPreviousSymmetricComponent();
70                         if (c != null) {
71                                 r = c.getFrontAutoRadius();
72                         }
73                         if (r < 0) {
74                                 c = this.getNextSymmetricComponent();
75                                 if (c != null) {
76                                         r = c.getRearAutoRadius();
77                                 }
78                         }
79                         if (r < 0)
80                                 r = DEFAULT_RADIUS;
81                         return r;
82                 }
83                 return radius;
84         }
85         
86         
87         /**
88          * Set the outer radius of the body tube.  If the radius is less than the wall thickness,
89          * the wall thickness is decreased accordingly of the value of the radius.
90          * This method sets the automatic radius off.
91          */
92         public void setRadius(double radius) {
93                 if ((this.radius == radius) && (autoRadius == false))
94                         return;
95                 
96                 this.autoRadius = false;
97                 this.radius = Math.max(radius, 0);
98                 
99                 if (this.thickness > this.radius)
100                         this.thickness = this.radius;
101                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
102         }
103         
104         
105         /**
106          * Returns whether the radius is selected automatically or not.
107          * Returns false also in case automatic radius selection is not possible.
108          */
109         public boolean isRadiusAutomatic() {
110                 return autoRadius;
111         }
112         
113         /**
114          * Sets whether the radius is selected automatically or not.  
115          */
116         public void setRadiusAutomatic(boolean auto) {
117                 if (autoRadius == auto)
118                         return;
119                 
120                 autoRadius = auto;
121                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
122         }
123         
124         
125         @Override
126         public double getAftRadius() {
127                 return getRadius();
128         }
129         
130         @Override
131         public double getForeRadius() {
132                 return getRadius();
133         }
134         
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 getRadius();
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 getRadius();
173         }
174         
175         
176
177
178
179         public double getInnerRadius() {
180                 if (filled)
181                         return 0;
182                 return Math.max(getRadius() - thickness, 0);
183         }
184         
185         public void setInnerRadius(double r) {
186                 setThickness(getRadius() - r);
187         }
188         
189         
190
191
192         /**
193          * Return the component name.
194          */
195         @Override
196         public String getComponentName() {
197                 return "Body tube";
198         }
199         
200         
201         /************ Component calculations ***********/
202         
203         // From SymmetricComponent
204         /**
205          * Returns the outer radius at the position x.  This returns the same value as getRadius().
206          */
207         @Override
208         public double getRadius(double x) {
209                 return getRadius();
210         }
211         
212         /**
213          * Returns the inner radius at the position x.  If the tube is filled, returns always zero.
214          */
215         @Override
216         public double getInnerRadius(double x) {
217                 if (filled)
218                         return 0.0;
219                 else
220                         return Math.max(getRadius() - thickness, 0);
221         }
222         
223         
224         /**
225          * Returns the body tube's center of gravity.
226          */
227         @Override
228         public Coordinate getComponentCG() {
229                 return new Coordinate(length / 2, 0, 0, getComponentMass());
230         }
231         
232         /**
233          * Returns the body tube's volume.
234          */
235         @Override
236         public double getComponentVolume() {
237                 double r = getRadius();
238                 if (filled)
239                         return getFilledVolume(r, length);
240                 else
241                         return getFilledVolume(r, length) - getFilledVolume(getInnerRadius(0), length);
242         }
243         
244         
245         @Override
246         public double getLongitudalUnitInertia() {
247                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
248                 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) + MathUtil.pow2(getLength())) / 12;
249         }
250         
251         @Override
252         public double getRotationalUnitInertia() {
253                 // 1/2 * (r1^2 + r2^2)
254                 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius())) / 2;
255         }
256         
257         
258
259
260         /**
261          * Helper function for cylinder volume.
262          */
263         private static double getFilledVolume(double r, double l) {
264                 return Math.PI * r * r * l;
265         }
266         
267         
268         /**
269          * Adds bounding coordinates to the given set.  The body tube will fit within the
270          * convex hull of the points.
271          * 
272          * Currently the points are simply a rectangular box around the body tube.
273          */
274         @Override
275         public Collection<Coordinate> getComponentBounds() {
276                 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
277                 double r = getRadius();
278                 addBound(bounds, 0, r);
279                 addBound(bounds, length, r);
280                 return bounds;
281         }
282         
283         
284         ////////////////  Motor mount  /////////////////
285         
286         @Override
287         public boolean isMotorMount() {
288                 return motorMount;
289         }
290         
291         @Override
292         public void setMotorMount(boolean mount) {
293                 if (motorMount == mount)
294                         return;
295                 motorMount = mount;
296                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
297         }
298         
299         @Override
300         public Motor getMotor(String id) {
301                 if (id == null)
302                         return null;
303                 
304                 // Check whether the id is valid for the current rocket
305                 RocketComponent root = this.getRoot();
306                 if (!(root instanceof Rocket))
307                         return null;
308                 if (!((Rocket) root).isMotorConfigurationID(id))
309                         return null;
310                 
311                 return motors.get(id);
312         }
313         
314         @Override
315         public void setMotor(String id, Motor motor) {
316                 if (id == null) {
317                         if (motor != null) {
318                                 throw new IllegalArgumentException("Cannot set non-null motor for id null");
319                         }
320                 }
321                 Motor current = motors.get(id);
322                 if ((motor == null && current == null) ||
323                                 (motor != null && motor.equals(current)))
324                         return;
325                 motors.put(id, motor);
326                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
327         }
328         
329         @Override
330         public double getMotorDelay(String id) {
331                 Double delay = ejectionDelays.get(id);
332                 if (delay == null)
333                         return Motor.PLUGGED;
334                 return delay;
335         }
336         
337         @Override
338         public void setMotorDelay(String id, double delay) {
339                 ejectionDelays.put(id, delay);
340                 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
341         }
342         
343         @Override
344         public int getMotorCount() {
345                 return 1;
346         }
347         
348         @Override
349         public double getMotorMountDiameter() {
350                 return getInnerRadius() * 2;
351         }
352         
353         @Override
354         public IgnitionEvent getIgnitionEvent() {
355                 return ignitionEvent;
356         }
357         
358         @Override
359         public void setIgnitionEvent(IgnitionEvent event) {
360                 if (ignitionEvent == event)
361                         return;
362                 ignitionEvent = event;
363                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
364         }
365         
366         
367         @Override
368         public double getIgnitionDelay() {
369                 return ignitionDelay;
370         }
371         
372         @Override
373         public void setIgnitionDelay(double delay) {
374                 if (MathUtil.equals(delay, ignitionDelay))
375                         return;
376                 ignitionDelay = delay;
377                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
378         }
379         
380         
381         @Override
382         public double getMotorOverhang() {
383                 return overhang;
384         }
385         
386         @Override
387         public void setMotorOverhang(double overhang) {
388                 if (MathUtil.equals(this.overhang, overhang))
389                         return;
390                 this.overhang = overhang;
391                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
392         }
393         
394         
395         @Override
396         public Coordinate getMotorPosition(String id) {
397                 Motor motor = motors.get(id);
398                 if (motor == null) {
399                         throw new IllegalArgumentException("No motor with id " + id + " defined.");
400                 }
401                 
402                 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
403         }
404         
405         
406
407
408         /*
409          * (non-Javadoc)
410          * Copy the motor and ejection delay HashMaps.
411          * 
412          * @see rocketcomponent.RocketComponent#copy()
413          */
414         @SuppressWarnings("unchecked")
415         @Override
416         protected RocketComponent copyWithOriginalID() {
417                 RocketComponent c = super.copyWithOriginalID();
418                 ((BodyTube) c).motors = (HashMap<String, Motor>) motors.clone();
419                 ((BodyTube) c).ejectionDelays = (HashMap<String, Double>) ejectionDelays.clone();
420                 return c;
421         }
422 }