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