8573caa67f9b019ca8bbc4a5d6d41a2ef9b01fb8
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / RocketComponent.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.awt.Color;
4 import java.util.ArrayDeque;
5 import java.util.Collection;
6 import java.util.Deque;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.NoSuchElementException;
10
11 import javax.swing.event.ChangeListener;
12
13 import net.sf.openrocket.logging.LogHelper;
14 import net.sf.openrocket.startup.Application;
15 import net.sf.openrocket.util.ArrayList;
16 import net.sf.openrocket.util.BugException;
17 import net.sf.openrocket.util.ChangeSource;
18 import net.sf.openrocket.util.Coordinate;
19 import net.sf.openrocket.util.Invalidator;
20 import net.sf.openrocket.util.LineStyle;
21 import net.sf.openrocket.util.MathUtil;
22 import net.sf.openrocket.util.SafetyMutex;
23 import net.sf.openrocket.util.UniqueID;
24
25
26 public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent>,
27                 Visitable<ComponentVisitor, RocketComponent> {
28         private static final LogHelper log = Application.getLogger();
29         
30         /*
31          * Text is suitable to the form
32          *    Position relative to:  <title>
33          */
34         public enum Position {
35                 /** Position relative to the top of the parent component. */
36                 TOP("Top of the parent component"),
37                 /** Position relative to the middle of the parent component. */
38                 MIDDLE("Middle of the parent component"),
39                 /** Position relative to the bottom of the parent component. */
40                 BOTTOM("Bottom of the parent component"),
41                 /** Position after the parent component (for body components). */
42                 AFTER("After the parent component"),
43                 /** Specify an absolute X-coordinate position. */
44                 ABSOLUTE("Tip of the nose cone");
45                 
46                 private String title;
47                 
48                 Position(String title) {
49                         this.title = title;
50                 }
51                 
52                 @Override
53                 public String toString() {
54                         return title;
55                 }
56         }
57         
58         /**
59          * A safety mutex that can be used to prevent concurrent access to this component.
60          */
61         protected SafetyMutex mutex = SafetyMutex.newInstance();
62         
63         ////////  Parent/child trees
64         /**
65          * Parent component of the current component, or null if none exists.
66          */
67         private RocketComponent parent = null;
68         
69         /**
70          * List of child components of this component.
71          */
72         private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>();
73         
74
75         ////////  Parameters common to all components:
76         
77         /**
78          * Characteristic length of the component.  This is used in calculating the coordinate
79          * transformations and positions of other components in reference to this component.
80          * This may and should be used as the "true" length of the component, where applicable.
81          * By default it is zero, i.e. no translation.
82          */
83         protected double length = 0;
84         
85         /**
86          * Positioning of this component relative to the parent component.
87          */
88         protected Position relativePosition;
89         
90         /**
91          * Offset of the position of this component relative to the normal position given by
92          * relativePosition.  By default zero, i.e. no position change.
93          */
94         protected double position = 0;
95         
96
97         // Color of the component, null means to use the default color
98         private Color color = null;
99         private LineStyle lineStyle = null;
100         
101
102         // Override mass/CG
103         private double overrideMass = 0;
104         private boolean massOverriden = false;
105         private double overrideCGX = 0;
106         private boolean cgOverriden = false;
107         
108         private boolean overrideSubcomponents = false;
109         
110
111         // User-given name of the component
112         private String name = null;
113         
114         // User-specified comment
115         private String comment = "";
116         
117         // Unique ID of the component
118         private String id = null;
119         
120         /**
121          * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
122          */
123         private Invalidator invalidator = new Invalidator(this);
124         
125         
126         ////  NOTE !!!  All fields must be copied in the method copyFrom()!  ////
127         
128
129
130         /**
131          * Default constructor.  Sets the name of the component to the component's static name
132          * and the relative position of the component.
133          */
134         public RocketComponent(Position relativePosition) {
135                 // These must not fire any events, due to Rocket undo system initialization
136                 this.name = getComponentName();
137                 this.relativePosition = relativePosition;
138                 newID();
139         }
140         
141         ////////////  Methods that must be implemented  ////////////
142         
143
144         /**
145          * Static component name.  The name may not vary of the parameters, it must be static.
146          */
147         public abstract String getComponentName(); // Static component type name
148         
149         /**
150          * Return the component mass (regardless of mass overriding).
151          */
152         public abstract double getComponentMass(); // Mass of non-overridden component
153         
154         /**
155          * Return the component CG and mass (regardless of CG or mass overriding).
156          */
157         public abstract Coordinate getComponentCG(); // CG of non-overridden component
158         
159
160         /**
161          * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.  
162          * The unitary moment of inertia is the moment of inertia with the assumption that
163          * the mass of the component is one kilogram.  The inertia is measured in
164          * respect to the non-overridden CG.
165          * 
166          * @return   the longitudinal unitary moment of inertia of this component.
167          */
168         public abstract double getLongitudinalUnitInertia();
169         
170         
171         /**
172          * Return the rotational (around the x-axis) unitary moment of inertia.  
173          * The unitary moment of inertia is the moment of inertia with the assumption that
174          * the mass of the component is one kilogram.  The inertia is measured in
175          * respect to the non-overridden CG.
176          * 
177          * @return   the rotational unitary moment of inertia of this component.
178          */
179         public abstract double getRotationalUnitInertia();
180         
181         
182         /**
183          * Test whether this component allows any children components.  This method must
184          * return true if and only if {@link #isCompatible(Class)} returns true for any
185          * rocket component class.
186          * 
187          * @return      <code>true</code> if children can be attached to this component, <code>false</code> otherwise.
188          */
189         public abstract boolean allowsChildren();
190         
191         /**
192          * Test whether the given component type can be added to this component.  This type safety
193          * is enforced by the <code>addChild()</code> methods.  The return value of this method
194          * may change to reflect the current state of this component (e.g. two components of some
195          * type cannot be placed as children).
196          * 
197          * @param type  The RocketComponent class type to add.
198          * @return      Whether such a component can be added.
199          */
200         public abstract boolean isCompatible(Class<? extends RocketComponent> type);
201         
202         
203         /* Non-abstract helper method */
204         /**
205          * Test whether the given component can be added to this component.  This is equivalent
206          * to calling <code>isCompatible(c.getClass())</code>.
207          * 
208          * @param c  Component to test.
209          * @return   Whether the component can be added.
210          * @see #isCompatible(Class)
211          */
212         public final boolean isCompatible(RocketComponent c) {
213                 mutex.verify();
214                 return isCompatible(c.getClass());
215         }
216         
217         
218
219         /**
220          * Return a collection of bounding coordinates.  The coordinates must be such that
221          * the component is fully enclosed in their convex hull.
222          * 
223          * @return      a collection of coordinates that bound the component.
224          */
225         public abstract Collection<Coordinate> getComponentBounds();
226         
227         /**
228          * Return true if the component may have an aerodynamic effect on the rocket.
229          */
230         public abstract boolean isAerodynamic();
231         
232         /**
233          * Return true if the component may have an effect on the rocket's mass.
234          */
235         public abstract boolean isMassive();
236         
237         
238
239
240
241         ////////////  Methods that may be overridden  ////////////
242         
243
244         /**
245          * Shift the coordinates in the array corresponding to radial movement.  A component
246          * that has a radial position must shift the coordinates in this array suitably.
247          * If the component is clustered, then a new array must be returned with a
248          * coordinate for each cluster.
249          * <p>
250          * The default implementation simply returns the array, and thus produces no shift.
251          * 
252          * @param c   an array of coordinates to shift.
253          * @return    an array of shifted coordinates.  The method may modify the contents
254          *                        of the passed array and return the array itself.
255          */
256         public Coordinate[] shiftCoordinates(Coordinate[] c) {
257                 checkState();
258                 return c;
259         }
260         
261         
262         /**
263          * Called when any component in the tree fires a ComponentChangeEvent.  This is by 
264          * default a no-op, but subclasses may override this method to e.g. invalidate 
265          * cached data.  The overriding method *must* call 
266          * <code>super.componentChanged(e)</code> at some point.
267          * 
268          * @param e  The event fired
269          */
270         protected void componentChanged(ComponentChangeEvent e) {
271                 // No-op
272                 checkState();
273         }
274         
275         
276
277
278         /**
279          * Return the user-provided name of the component, or the component base
280          * name if the user-provided name is empty.  This can be used in the UI.
281          * 
282          * @return A string describing the component.
283          */
284         @Override
285         public final String toString() {
286                 mutex.verify();
287                 if (name.length() == 0)
288                         return getComponentName();
289                 else
290                         return name;
291         }
292         
293         
294         /**
295          * Create a string describing the basic component structure from this component downwards.
296          * @return      a string containing the rocket structure
297          */
298         public final String toDebugString() {
299                 mutex.lock("toDebugString");
300                 try {
301                         StringBuilder sb = new StringBuilder();
302                         toDebugString(sb);
303                         return sb.toString();
304                 } finally {
305                         mutex.unlock("toDebugString");
306                 }
307         }
308         
309         private void toDebugString(StringBuilder sb) {
310                 sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
311                 sb.append("[\"").append(this.getName()).append('"');
312                 for (RocketComponent c : this.children) {
313                         sb.append("; ");
314                         c.toDebugString(sb);
315                 }
316                 sb.append(']');
317         }
318         
319         
320         /**
321          * Make a deep copy of the rocket component tree structure from this component
322          * downwards for copying purposes.  Each component in the copy will be assigned
323          * a new component ID, making it a safe copy.  This method does not fire any events.
324          * 
325          * @return A deep copy of the structure.
326          */
327         public final RocketComponent copy() {
328                 RocketComponent clone = copyWithOriginalID();
329                 
330                 Iterator<RocketComponent> iterator = clone.iterator(true);
331                 while (iterator.hasNext()) {
332                         iterator.next().newID();
333                 }
334                 return clone;
335         }
336         
337         
338
339         /**
340          * Make a deep copy of the rocket component tree structure from this component
341          * downwards while maintaining the component ID's.  The purpose of this method is
342          * to allow copies to be created with the original ID's for the purpose of the
343          * undo/redo mechanism.  This method should not be used for other purposes,
344          * such as copy/paste.  This method does not fire any events.
345          * <p>
346          * This method must be overridden by any component that refers to mutable objects, 
347          * or if some fields should not be copied.  This should be performed by
348          * <code>RocketComponent c = super.copyWithOriginalID();</code> and then cloning/modifying
349          * the appropriate fields.
350          * <p>
351          * This is not performed as serializing/deserializing for performance reasons.
352          * 
353          * @return A deep copy of the structure.
354          */
355         protected RocketComponent copyWithOriginalID() {
356                 mutex.lock("copyWithOriginalID");
357                 try {
358                         checkState();
359                         RocketComponent clone;
360                         try {
361                                 clone = (RocketComponent) this.clone();
362                         } catch (CloneNotSupportedException e) {
363                                 throw new BugException("CloneNotSupportedException encountered, report a bug!", e);
364                         }
365                         
366                         // Reset the mutex
367                         clone.mutex = SafetyMutex.newInstance();
368                         
369                         // Reset all parent/child information
370                         clone.parent = null;
371                         clone.children = new ArrayList<RocketComponent>();
372                         
373                         // Add copied children to the structure without firing events.
374                         for (RocketComponent child : this.children) {
375                                 RocketComponent childCopy = child.copyWithOriginalID();
376                                 // Don't use add method since it fires events
377                                 clone.children.add(childCopy);
378                                 childCopy.parent = clone;
379                         }
380                         
381                         this.checkComponentStructure();
382                         clone.checkComponentStructure();
383                         
384                         return clone;
385                 } finally {
386                         mutex.unlock("copyWithOriginalID");
387                 }
388         }
389         
390         
391         /**
392          * Accept a visitor to this RocketComponent in the component hierarchy.
393          * 
394          * @param theVisitor  the visitor that will be called back with a reference to this RocketComponent
395          */
396         @Override
397         public void accept(final ComponentVisitor theVisitor) {
398                 theVisitor.visit(this);
399         }
400         
401         //////////////  Methods that may not be overridden  ////////////
402         
403
404
405         ////////// Common parameter setting/getting //////////
406         
407         /**
408          * Return the color of the object to use in 2D figures, or <code>null</code>
409          * to use the default color.
410          */
411         public final Color getColor() {
412                 mutex.verify();
413                 return color;
414         }
415         
416         /**
417          * Set the color of the object to use in 2D figures.  
418          */
419         public final void setColor(Color c) {
420                 if ((color == null && c == null) ||
421                                 (color != null && color.equals(c)))
422                         return;
423                 
424                 checkState();
425                 this.color = c;
426                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
427         }
428         
429         
430         public final LineStyle getLineStyle() {
431                 mutex.verify();
432                 return lineStyle;
433         }
434         
435         public final void setLineStyle(LineStyle style) {
436                 if (this.lineStyle == style)
437                         return;
438                 checkState();
439                 this.lineStyle = style;
440                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
441         }
442         
443         
444
445
446         /**
447          * Get the current override mass.  The mass is not necessarily in use
448          * at the moment.
449          * 
450          * @return  the override mass
451          */
452         public final double getOverrideMass() {
453                 mutex.verify();
454                 return overrideMass;
455         }
456         
457         /**
458          * Set the current override mass.  The mass is not set to use by this
459          * method.
460          * 
461          * @param m  the override mass
462          */
463         public final void setOverrideMass(double m) {
464                 if (MathUtil.equals(m, overrideMass))
465                         return;
466                 checkState();
467                 overrideMass = Math.max(m, 0);
468                 if (massOverriden)
469                         fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
470         }
471         
472         /**
473          * Return whether mass override is active for this component.  This does NOT
474          * take into account whether a parent component is overriding the mass.
475          * 
476          * @return  whether the mass is overridden
477          */
478         public final boolean isMassOverridden() {
479                 mutex.verify();
480                 return massOverriden;
481         }
482         
483         /**
484          * Set whether the mass is currently overridden.
485          * 
486          * @param o  whether the mass is overridden
487          */
488         public final void setMassOverridden(boolean o) {
489                 if (massOverriden == o) {
490                         return;
491                 }
492                 checkState();
493                 massOverriden = o;
494                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
495         }
496         
497         
498
499
500
501         /**
502          * Return the current override CG.  The CG is not necessarily overridden.
503          * 
504          * @return  the override CG
505          */
506         public final Coordinate getOverrideCG() {
507                 mutex.verify();
508                 return getComponentCG().setX(overrideCGX);
509         }
510         
511         /**
512          * Return the x-coordinate of the current override CG.
513          * 
514          * @return      the x-coordinate of the override CG.
515          */
516         public final double getOverrideCGX() {
517                 mutex.verify();
518                 return overrideCGX;
519         }
520         
521         /**
522          * Set the current override CG to (x,0,0).
523          * 
524          * @param x  the x-coordinate of the override CG to set.
525          */
526         public final void setOverrideCGX(double x) {
527                 if (MathUtil.equals(overrideCGX, x))
528                         return;
529                 checkState();
530                 this.overrideCGX = x;
531                 if (isCGOverridden())
532                         fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
533                 else
534                         fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
535         }
536         
537         /**
538          * Return whether the CG is currently overridden.
539          * 
540          * @return  whether the CG is overridden
541          */
542         public final boolean isCGOverridden() {
543                 mutex.verify();
544                 return cgOverriden;
545         }
546         
547         /**
548          * Set whether the CG is currently overridden.
549          * 
550          * @param o  whether the CG is overridden
551          */
552         public final void setCGOverridden(boolean o) {
553                 if (cgOverriden == o) {
554                         return;
555                 }
556                 checkState();
557                 cgOverriden = o;
558                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
559         }
560         
561         
562
563         /**
564          * Return whether the mass and/or CG override overrides all subcomponent values
565          * as well.  The default implementation is a normal getter/setter implementation,
566          * however, subclasses are allowed to override this behavior if some subclass
567          * always or never overrides subcomponents.  In this case the subclass should
568          * also override {@link #isOverrideSubcomponentsEnabled()} to return
569          * <code>false</code>.
570          * 
571          * @return      whether the current mass and/or CG override overrides subcomponents as well.
572          */
573         public boolean getOverrideSubcomponents() {
574                 mutex.verify();
575                 return overrideSubcomponents;
576         }
577         
578         
579         /**
580          * Set whether the mass and/or CG override overrides all subcomponent values
581          * as well.  See {@link #getOverrideSubcomponents()} for details.
582          * 
583          * @param override      whether the mass and/or CG override overrides all subcomponent.
584          */
585         public void setOverrideSubcomponents(boolean override) {
586                 if (overrideSubcomponents == override) {
587                         return;
588                 }
589                 checkState();
590                 overrideSubcomponents = override;
591                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
592         }
593         
594         /**
595          * Return whether the option to override all subcomponents is enabled or not.
596          * The default implementation returns <code>false</code> if neither mass nor
597          * CG is overridden, <code>true</code> otherwise.
598          * <p>
599          * This method may be overridden if the setting of overriding subcomponents
600          * cannot be set.
601          * 
602          * @return      whether the option to override subcomponents is currently enabled.
603          */
604         public boolean isOverrideSubcomponentsEnabled() {
605                 mutex.verify();
606                 return isCGOverridden() || isMassOverridden();
607         }
608         
609         
610
611
612         /**
613          * Get the user-defined name of the component.
614          */
615         public final String getName() {
616                 mutex.verify();
617                 return name;
618         }
619         
620         /**
621          * Set the user-defined name of the component.  If name==null, sets the name to
622          * the default name, currently the component name.
623          */
624         public final void setName(String name) {
625                 if (this.name.equals(name)) {
626                         return;
627                 }
628                 checkState();
629                 if (name == null || name.matches("^\\s*$"))
630                         this.name = getComponentName();
631                 else
632                         this.name = name;
633                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
634         }
635         
636         
637         /**
638          * Return the comment of the component.  The component may contain multiple lines
639          * using \n as a newline separator.
640          * 
641          * @return  the comment of the component.
642          */
643         public final String getComment() {
644                 mutex.verify();
645                 return comment;
646         }
647         
648         /**
649          * Set the comment of the component.
650          * 
651          * @param comment  the comment of the component.
652          */
653         public final void setComment(String comment) {
654                 if (this.comment.equals(comment))
655                         return;
656                 checkState();
657                 if (comment == null)
658                         this.comment = "";
659                 else
660                         this.comment = comment;
661                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
662         }
663         
664         
665
666         /**
667          * Returns the unique ID of the component.
668          * 
669          * @return      the ID of the component.
670          */
671         public final String getID() {
672                 return id;
673         }
674         
675         /**
676          * Generate a new ID for this component.
677          */
678         private final void newID() {
679                 mutex.verify();
680                 this.id = UniqueID.uuid();
681         }
682         
683         
684
685
686         /**
687          * Get the characteristic length of the component, for example the length of a body tube
688          * of the length of the root chord of a fin.  This is used in positioning the component
689          * relative to its parent.
690          * 
691          * If the length of a component is settable, the class must define the setter method
692          * itself.
693          */
694         public final double getLength() {
695                 mutex.verify();
696                 return length;
697         }
698         
699         /**
700          * Get the positioning of the component relative to its parent component.
701          * This is one of the enums of {@link Position}.  A setter method is not provided,
702          * but can be provided by a subclass.
703          */
704         public final Position getRelativePosition() {
705                 mutex.verify();
706                 return relativePosition;
707         }
708         
709         
710         /**
711          * Set the positioning of the component relative to its parent component.
712          * The actual position of the component is maintained to the best ability.
713          * <p>
714          * The default implementation is of protected visibility, since many components
715          * do not support setting the relative position.  A component that does support
716          * it should override this with a public method that simply calls this
717          * supermethod AND fire a suitable ComponentChangeEvent.
718          * 
719          * @param position      the relative positioning.
720          */
721         protected void setRelativePosition(RocketComponent.Position position) {
722                 if (this.relativePosition == position)
723                         return;
724                 checkState();
725                 
726                 // Update position so as not to move the component
727                 if (this.parent != null) {
728                         double thisPos = this.toRelative(Coordinate.NUL, this.parent)[0].x;
729                         
730                         switch (position) {
731                         case ABSOLUTE:
732                                 this.position = this.toAbsolute(Coordinate.NUL)[0].x;
733                                 break;
734                         
735                         case TOP:
736                                 this.position = thisPos;
737                                 break;
738                         
739                         case MIDDLE:
740                                 this.position = thisPos - (this.parent.length - this.length) / 2;
741                                 break;
742                         
743                         case BOTTOM:
744                                 this.position = thisPos - (this.parent.length - this.length);
745                                 break;
746                         
747                         default:
748                                 throw new BugException("Unknown position type: " + position);
749                         }
750                 }
751                 
752                 this.relativePosition = position;
753                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
754         }
755         
756         
757
758
759         /**
760          * Get the position value of the component.  The exact meaning of the value is
761          * dependent on the current relative positioning.
762          * 
763          * @return  the positional value.
764          */
765         public final double getPositionValue() {
766                 mutex.verify();
767                 return position;
768         }
769         
770         
771         /**
772          * Set the position value of the component.  The exact meaning of the value
773          * depends on the current relative positioning.
774          * <p>
775          * The default implementation is of protected visibility, since many components
776          * do not support setting the relative position.  A component that does support
777          * it should override this with a public method that simply calls this
778          * supermethod AND fire a suitable ComponentChangeEvent.
779          * 
780          * @param value         the position value of the component.
781          */
782         public void setPositionValue(double value) {
783                 if (MathUtil.equals(this.position, value))
784                         return;
785                 checkState();
786                 this.position = value;
787         }
788         
789         
790
791         ///////////  Coordinate changes  ///////////
792         
793         /**
794          * Returns coordinate c in absolute coordinates.  Equivalent to toComponent(c,null).
795          */
796         public Coordinate[] toAbsolute(Coordinate c) {
797                 checkState();
798                 return toRelative(c, null);
799         }
800         
801         
802         /**
803          * Return coordinate <code>c</code> described in the coordinate system of 
804          * <code>dest</code>.  If <code>dest</code> is <code>null</code> returns
805          * absolute coordinates.
806          * <p>
807          * This method returns an array of coordinates, each of which represents a
808          * position of the coordinate in clustered cases.  The array is guaranteed
809          * to contain at least one element.  
810          * <p>
811          * The current implementation does not support rotating components.
812          * 
813          * @param c    Coordinate in the component's coordinate system.
814          * @param dest Destination component coordinate system.
815          * @return     an array of coordinates describing <code>c</code> in coordinates
816          *                         relative to <code>dest</code>.
817          */
818         public final Coordinate[] toRelative(Coordinate c, RocketComponent dest) {
819                 checkState();
820                 mutex.lock("toRelative");
821                 try {
822                         double absoluteX = Double.NaN;
823                         RocketComponent search = dest;
824                         Coordinate[] array = new Coordinate[1];
825                         array[0] = c;
826                         
827                         RocketComponent component = this;
828                         while ((component != search) && (component.parent != null)) {
829                                 
830                                 array = component.shiftCoordinates(array);
831                                 
832                                 switch (component.relativePosition) {
833                                 case TOP:
834                                         for (int i = 0; i < array.length; i++) {
835                                                 array[i] = array[i].add(component.position, 0, 0);
836                                         }
837                                         break;
838                                 
839                                 case MIDDLE:
840                                         for (int i = 0; i < array.length; i++) {
841                                                 array[i] = array[i].add(component.position +
842                                                                 (component.parent.length - component.length) / 2, 0, 0);
843                                         }
844                                         break;
845                                 
846                                 case BOTTOM:
847                                         for (int i = 0; i < array.length; i++) {
848                                                 array[i] = array[i].add(component.position +
849                                                                 (component.parent.length - component.length), 0, 0);
850                                         }
851                                         break;
852                                 
853                                 case AFTER:
854                                         // Add length of all previous brother-components with POSITION_RELATIVE_AFTER
855                                         int index = component.parent.children.indexOf(component);
856                                         assert (index >= 0);
857                                         for (index--; index >= 0; index--) {
858                                                 RocketComponent comp = component.parent.children.get(index);
859                                                 double componentLength = comp.getTotalLength();
860                                                 for (int i = 0; i < array.length; i++) {
861                                                         array[i] = array[i].add(componentLength, 0, 0);
862                                                 }
863                                         }
864                                         for (int i = 0; i < array.length; i++) {
865                                                 array[i] = array[i].add(component.position + component.parent.length, 0, 0);
866                                         }
867                                         break;
868                                 
869                                 case ABSOLUTE:
870                                         search = null; // Requires back-search if dest!=null
871                                         if (Double.isNaN(absoluteX)) {
872                                                 absoluteX = component.position;
873                                         }
874                                         break;
875                                 
876                                 default:
877                                         throw new BugException("Unknown relative positioning type of component" +
878                                                         component + ": " + component.relativePosition);
879                                 }
880                                 
881                                 component = component.parent; // parent != null
882                         }
883                         
884                         if (!Double.isNaN(absoluteX)) {
885                                 for (int i = 0; i < array.length; i++) {
886                                         array[i] = array[i].setX(absoluteX + c.x);
887                                 }
888                         }
889                         
890                         // Check whether destination has been found or whether to backtrack
891                         // TODO: LOW: Backtracking into clustered components uses only one component 
892                         if ((dest != null) && (component != dest)) {
893                                 Coordinate[] origin = dest.toAbsolute(Coordinate.NUL);
894                                 for (int i = 0; i < array.length; i++) {
895                                         array[i] = array[i].sub(origin[0]);
896                                 }
897                         }
898                         
899                         return array;
900                 } finally {
901                         mutex.unlock("toRelative");
902                 }
903         }
904         
905         
906         /**
907          * Recursively sum the lengths of all subcomponents that have position 
908          * Position.AFTER.
909          * 
910          * @return  Sum of the lengths.
911          */
912         private final double getTotalLength() {
913                 checkState();
914                 this.checkComponentStructure();
915                 mutex.lock("getTotalLength");
916                 try {
917                         double l = 0;
918                         if (relativePosition == Position.AFTER)
919                                 l = length;
920                         for (int i = 0; i < children.size(); i++)
921                                 l += children.get(i).getTotalLength();
922                         return l;
923                 } finally {
924                         mutex.unlock("getTotalLength");
925                 }
926         }
927         
928         
929
930         /////////// Total mass and CG calculation ////////////
931         
932         /**
933          * Return the (possibly overridden) mass of component.
934          * 
935          * @return The mass of the component or the given override mass.
936          */
937         public final double getMass() {
938                 mutex.verify();
939                 if (massOverriden)
940                         return overrideMass;
941                 return getComponentMass();
942         }
943         
944         /**
945          * Return the (possibly overridden) center of gravity and mass.
946          * 
947          * Returns the CG with the weight of the coordinate set to the weight of the component.
948          * Both CG and mass may be separately overridden.
949          * 
950          * @return The CG of the component or the given override CG.
951          */
952         public final Coordinate getCG() {
953                 checkState();
954                 if (cgOverriden)
955                         return getOverrideCG().setWeight(getMass());
956                 
957                 if (massOverriden)
958                         return getComponentCG().setWeight(getMass());
959                 
960                 return getComponentCG();
961         }
962         
963         
964         /**
965          * Return the longitudinal (around the y- or z-axis) moment of inertia of this component.
966          * The moment of inertia is scaled in reference to the (possibly overridden) mass
967          * and is relative to the non-overridden CG.
968          * 
969          * @return    the longitudinal moment of inertia of this component.
970          */
971         public final double getLongitudinalInertia() {
972                 checkState();
973                 return getLongitudinalUnitInertia() * getMass();
974         }
975         
976         /**
977          * Return the rotational (around the y- or z-axis) moment of inertia of this component.
978          * The moment of inertia is scaled in reference to the (possibly overridden) mass
979          * and is relative to the non-overridden CG.
980          * 
981          * @return    the rotational moment of inertia of this component.
982          */
983         public final double getRotationalInertia() {
984                 checkState();
985                 return getRotationalUnitInertia() * getMass();
986         }
987         
988         
989
990         ///////////  Children handling  ///////////
991         
992
993         /**
994          * Adds a child to the rocket component tree.  The component is added to the end
995          * of the component's child list.  This is a helper method that calls 
996          * {@link #addChild(RocketComponent,int)}.
997          * 
998          * @param component  The component to add.
999          * @throws IllegalArgumentException  if the component is already part of some 
1000          *                                                                       component tree.
1001          * @see #addChild(RocketComponent,int)
1002          */
1003         public final void addChild(RocketComponent component) {
1004                 checkState();
1005                 addChild(component, children.size());
1006         }
1007         
1008         
1009         /**
1010          * Adds a child to the rocket component tree.  The component is added to 
1011          * the given position of the component's child list.
1012          * <p>
1013          * This method may be overridden to enforce more strict component addition rules.  
1014          * The tests should be performed first and then this method called.
1015          * 
1016          * @param component     The component to add.
1017          * @param index         Position to add component to.
1018          * @throws IllegalArgumentException  If the component is already part of 
1019          *                                                                       some component tree.
1020          */
1021         public void addChild(RocketComponent component, int index) {
1022                 checkState();
1023                 if (component.parent != null) {
1024                         throw new IllegalArgumentException("component " + component.getComponentName() +
1025                                         " is already in a tree");
1026                 }
1027                 if (!isCompatible(component)) {
1028                         throw new IllegalStateException("Component " + component.getComponentName() +
1029                                         " not currently compatible with component " + getComponentName());
1030                 }
1031                 
1032                 children.add(index, component);
1033                 component.parent = this;
1034                 
1035                 this.checkComponentStructure();
1036                 component.checkComponentStructure();
1037                 
1038                 fireAddRemoveEvent(component);
1039         }
1040         
1041         
1042         /**
1043          * Removes a child from the rocket component tree.
1044          * 
1045          * @param n  remove the n'th child.
1046          * @throws IndexOutOfBoundsException  if n is out of bounds
1047          */
1048         public final void removeChild(int n) {
1049                 checkState();
1050                 RocketComponent component = children.remove(n);
1051                 component.parent = null;
1052                 
1053                 this.checkComponentStructure();
1054                 component.checkComponentStructure();
1055                 
1056                 fireAddRemoveEvent(component);
1057         }
1058         
1059         /**
1060          * Removes a child from the rocket component tree.  Does nothing if the component
1061          * is not present as a child.
1062          * 
1063          * @param component             the component to remove
1064          * @return                              whether the component was a child
1065          */
1066         public final boolean removeChild(RocketComponent component) {
1067                 checkState();
1068                 
1069                 component.checkComponentStructure();
1070                 
1071                 if (children.remove(component)) {
1072                         component.parent = null;
1073                         
1074                         this.checkComponentStructure();
1075                         component.checkComponentStructure();
1076                         
1077                         fireAddRemoveEvent(component);
1078                         return true;
1079                 }
1080                 return false;
1081         }
1082         
1083         
1084
1085
1086         /**
1087          * Move a child to another position.
1088          * 
1089          * @param component     the component to move
1090          * @param index the component's new position
1091          * @throws IllegalArgumentException If an illegal placement was attempted.
1092          */
1093         public final void moveChild(RocketComponent component, int index) {
1094                 checkState();
1095                 if (children.remove(component)) {
1096                         children.add(index, component);
1097                         
1098                         this.checkComponentStructure();
1099                         component.checkComponentStructure();
1100                         
1101                         fireAddRemoveEvent(component);
1102                 }
1103         }
1104         
1105         
1106         /**
1107          * Fires an AERODYNAMIC_CHANGE, MASS_CHANGE or OTHER_CHANGE event depending on the
1108          * type of component removed.
1109          */
1110         private void fireAddRemoveEvent(RocketComponent component) {
1111                 Iterator<RocketComponent> iter = component.iterator(true);
1112                 int type = ComponentChangeEvent.TREE_CHANGE;
1113                 while (iter.hasNext()) {
1114                         RocketComponent c = iter.next();
1115                         if (c.isAerodynamic())
1116                                 type |= ComponentChangeEvent.AERODYNAMIC_CHANGE;
1117                         if (c.isMassive())
1118                                 type |= ComponentChangeEvent.MASS_CHANGE;
1119                 }
1120                 
1121                 fireComponentChangeEvent(type);
1122         }
1123         
1124         
1125         public final int getChildCount() {
1126                 checkState();
1127                 this.checkComponentStructure();
1128                 return children.size();
1129         }
1130         
1131         public final RocketComponent getChild(int n) {
1132                 checkState();
1133                 this.checkComponentStructure();
1134                 return children.get(n);
1135         }
1136         
1137         public final List<RocketComponent> getChildren() {
1138                 checkState();
1139                 this.checkComponentStructure();
1140                 return children.clone();
1141         }
1142         
1143         
1144         /**
1145          * Returns the position of the child in this components child list, or -1 if the
1146          * component is not a child of this component.
1147          * 
1148          * @param child  The child to search for.
1149          * @return  Position in the list or -1 if not found.
1150          */
1151         public final int getChildPosition(RocketComponent child) {
1152                 checkState();
1153                 this.checkComponentStructure();
1154                 return children.indexOf(child);
1155         }
1156         
1157         /**
1158          * Get the parent component of this component.  Returns <code>null</code> if the component
1159          * has no parent.
1160          * 
1161          * @return  The parent of this component or <code>null</code>.
1162          */
1163         public final RocketComponent getParent() {
1164                 checkState();
1165                 return parent;
1166         }
1167         
1168         /**
1169          * Get the root component of the component tree.
1170          * 
1171          * @return  The root component of the component tree.
1172          */
1173         public final RocketComponent getRoot() {
1174                 checkState();
1175                 RocketComponent gp = this;
1176                 while (gp.parent != null)
1177                         gp = gp.parent;
1178                 return gp;
1179         }
1180         
1181         /**
1182          * Returns the root Rocket component of this component tree.  Throws an 
1183          * IllegalStateException if the root component is not a Rocket.
1184          * 
1185          * @return  The root Rocket component of the component tree.
1186          * @throws  IllegalStateException  If the root component is not a Rocket.
1187          */
1188         public final Rocket getRocket() {
1189                 checkState();
1190                 RocketComponent r = getRoot();
1191                 if (r instanceof Rocket)
1192                         return (Rocket) r;
1193                 throw new IllegalStateException("getRocket() called with root component "
1194                                 + r.getComponentName());
1195         }
1196         
1197         
1198         /**
1199          * Return the Stage component that this component belongs to.  Throws an
1200          * IllegalStateException if a Stage is not in the parentage of this component.
1201          * 
1202          * @return      The Stage component this component belongs to.
1203          * @throws      IllegalStateException   if a Stage component is not in the parentage.
1204          */
1205         public final Stage getStage() {
1206                 checkState();
1207                 RocketComponent c = this;
1208                 while (c != null) {
1209                         if (c instanceof Stage)
1210                                 return (Stage) c;
1211                         c = c.getParent();
1212                 }
1213                 throw new IllegalStateException("getStage() called without Stage as a parent.");
1214         }
1215         
1216         /**
1217          * Return the stage number of the stage this component belongs to.  The stages
1218          * are numbered from zero upwards.
1219          * 
1220          * @return   the stage number this component belongs to.
1221          */
1222         public final int getStageNumber() {
1223                 checkState();
1224                 if (parent == null) {
1225                         throw new IllegalArgumentException("getStageNumber() called for root component");
1226                 }
1227                 
1228                 RocketComponent stage = this;
1229                 while (!(stage instanceof Stage)) {
1230                         stage = stage.parent;
1231                         if (stage == null || stage.parent == null) {
1232                                 throw new IllegalStateException("getStageNumber() could not find parent " +
1233                                                 "stage.");
1234                         }
1235                 }
1236                 return stage.parent.getChildPosition(stage);
1237         }
1238         
1239         
1240         /**
1241          * Find a component with the given ID.  The component tree is searched from this component
1242          * down (including this component) for the ID and the corresponding component is returned,
1243          * or null if not found.
1244          * 
1245          * @param idToFind  ID to search for.
1246          * @return    The component with the ID, or null if not found.
1247          */
1248         public final RocketComponent findComponent(String idToFind) {
1249                 checkState();
1250                 Iterator<RocketComponent> iter = this.iterator(true);
1251                 while (iter.hasNext()) {
1252                         RocketComponent c = iter.next();
1253                         if (c.getID().equals(idToFind))
1254                                 return c;
1255                 }
1256                 return null;
1257         }
1258         
1259         
1260         // TODO: Move these methods elsewhere (used only in SymmetricComponent)
1261         public final RocketComponent getPreviousComponent() {
1262                 checkState();
1263                 this.checkComponentStructure();
1264                 if (parent == null)
1265                         return null;
1266                 int pos = parent.getChildPosition(this);
1267                 if (pos < 0) {
1268                         StringBuffer sb = new StringBuffer();
1269                         sb.append("Inconsistent internal state: ");
1270                         sb.append("this=").append(this).append('[')
1271                                         .append(System.identityHashCode(this)).append(']');
1272                         sb.append(" parent.children=[");
1273                         for (int i = 0; i < parent.children.size(); i++) {
1274                                 RocketComponent c = parent.children.get(i);
1275                                 sb.append(c).append('[').append(System.identityHashCode(c)).append(']');
1276                                 if (i < parent.children.size() - 1)
1277                                         sb.append(", ");
1278                         }
1279                         sb.append(']');
1280                         throw new IllegalStateException(sb.toString());
1281                 }
1282                 assert (pos >= 0);
1283                 if (pos == 0)
1284                         return parent;
1285                 RocketComponent c = parent.getChild(pos - 1);
1286                 while (c.getChildCount() > 0)
1287                         c = c.getChild(c.getChildCount() - 1);
1288                 return c;
1289         }
1290         
1291         // TODO: Move these methods elsewhere (used only in SymmetricComponent)
1292         public final RocketComponent getNextComponent() {
1293                 checkState();
1294                 if (getChildCount() > 0)
1295                         return getChild(0);
1296                 
1297                 RocketComponent current = this;
1298                 RocketComponent nextParent = this.parent;
1299                 
1300                 while (nextParent != null) {
1301                         int pos = nextParent.getChildPosition(current);
1302                         if (pos < nextParent.getChildCount() - 1)
1303                                 return nextParent.getChild(pos + 1);
1304                         
1305                         current = nextParent;
1306                         nextParent = current.parent;
1307                 }
1308                 return null;
1309         }
1310         
1311         
1312         ///////////  Event handling  //////////
1313         //
1314         // Listener lists are provided by the root Rocket component,
1315         // a single listener list for the whole rocket.
1316         //
1317         
1318         /**
1319          * Adds a ComponentChangeListener to the rocket tree.  The listener is added to the root
1320          * component, which must be of type Rocket (which overrides this method).  Events of all
1321          * subcomponents are sent to all listeners.
1322          * 
1323          * @throws IllegalStateException - if the root component is not a Rocket
1324          */
1325         public void addComponentChangeListener(ComponentChangeListener l) {
1326                 checkState();
1327                 getRocket().addComponentChangeListener(l);
1328         }
1329         
1330         /**
1331          * Removes a ComponentChangeListener from the rocket tree.  The listener is removed from
1332          * the root component, which must be of type Rocket (which overrides this method).
1333          * Does nothing if the root component is not a Rocket.  (The asymmetry is so
1334          * that listeners can always be removed just in case.)
1335          * 
1336          * @param l  Listener to remove
1337          */
1338         public void removeComponentChangeListener(ComponentChangeListener l) {
1339                 if (parent != null) {
1340                         getRoot().removeComponentChangeListener(l);
1341                 }
1342         }
1343         
1344         
1345         /**
1346          * Adds a <code>ChangeListener</code> to the rocket tree.  This is identical to 
1347          * <code>addComponentChangeListener()</code> except that it uses a 
1348          * <code>ChangeListener</code>.  The same events are dispatched to the
1349          * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass 
1350          * of <code>ChangeEvent</code>.
1351          * 
1352          * @throws IllegalStateException - if the root component is not a <code>Rocket</code>
1353          */
1354         @Override
1355         public void addChangeListener(ChangeListener l) {
1356                 checkState();
1357                 getRocket().addChangeListener(l);
1358         }
1359         
1360         /**
1361          * Removes a ChangeListener from the rocket tree.  This is identical to
1362          * removeComponentChangeListener() except it uses a ChangeListener.
1363          * Does nothing if the root component is not a Rocket.  (The asymmetry is so
1364          * that listeners can always be removed just in case.)
1365          * 
1366          * @param l  Listener to remove
1367          */
1368         @Override
1369         public void removeChangeListener(ChangeListener l) {
1370                 if (this.parent != null) {
1371                         getRoot().removeChangeListener(l);
1372                 }
1373         }
1374         
1375         
1376         /**
1377          * Fires a ComponentChangeEvent on the rocket structure.  The call is passed to the 
1378          * root component, which must be of type Rocket (which overrides this method).
1379          * Events of all subcomponents are sent to all listeners.
1380          * 
1381          * If the component tree root is not a Rocket, the event is ignored.  This is the 
1382          * case when constructing components not in any Rocket tree.  In this case it 
1383          * would be impossible for the component to have listeners in any case.
1384          *  
1385          * @param e  Event to send
1386          */
1387         protected void fireComponentChangeEvent(ComponentChangeEvent e) {
1388                 checkState();
1389                 if (parent == null) {
1390                         /* Ignore if root invalid. */
1391                         log.debug("Attempted firing event " + e + " with root " + this.getComponentName() + ", ignoring event");
1392                         return;
1393                 }
1394                 getRoot().fireComponentChangeEvent(e);
1395         }
1396         
1397         
1398         /**
1399          * Fires a ComponentChangeEvent of the given type.  The source of the event is set to
1400          * this component.
1401          * 
1402          * @param type  Type of event
1403          * @see #fireComponentChangeEvent(ComponentChangeEvent)
1404          */
1405         protected void fireComponentChangeEvent(int type) {
1406                 fireComponentChangeEvent(new ComponentChangeEvent(this, type));
1407         }
1408         
1409         
1410         /**
1411          * Checks whether this component has been invalidated and should no longer be used.
1412          * This is a safety check that in-place replaced components are no longer used.
1413          * All non-trivial methods (with the exception of methods simply getting a property)
1414          * should call this method before changing or computing anything.
1415          * 
1416          * @throws      BugException    if this component has been invalidated by {@link #copyFrom(RocketComponent)}.
1417          */
1418         protected void checkState() {
1419                 invalidator.check(true);
1420                 mutex.verify();
1421         }
1422         
1423         
1424         /**
1425          * Check that the local component structure is correct.  This can be called after changing
1426          * the component structure in order to verify the integrity.
1427          * <p>
1428          * TODO: Remove this after the "inconsistent internal state" bug has been corrected
1429          */
1430         public void checkComponentStructure() {
1431                 if (this.parent != null) {
1432                         // Test that this component is found in parent's children with == operator
1433                         if (!containsExact(this.parent.children, this)) {
1434                                 throw new BugException("Inconsistent component structure detected, parent does not contain this " +
1435                                                 "component as a child, parent=" + parent.toDebugString() + " this=" + this.toDebugString());
1436                         }
1437                 }
1438                 for (RocketComponent child : this.children) {
1439                         if (child.parent != this) {
1440                                 throw new BugException("Inconsistent component structure detected, child does not have this component " +
1441                                                 "as the parent, this=" + this.toDebugString() + " child=" + child.toDebugString() +
1442                                                 " child.parent=" + (child.parent == null ? "null" : child.parent.toDebugString()));
1443                         }
1444                 }
1445         }
1446         
1447         // Check whether the list contains exactly the searched-for component (with == operator)
1448         private boolean containsExact(List<RocketComponent> haystack, RocketComponent needle) {
1449                 for (RocketComponent c : haystack) {
1450                         if (needle == c) {
1451                                 return true;
1452                         }
1453                 }
1454                 return false;
1455         }
1456         
1457         
1458         ///////////  Iterators  //////////
1459         
1460         /**
1461          * Returns an iterator that iterates over all children and sub-children.
1462          * <p>
1463          * The iterator iterates through all children below this object, including itself if
1464          * <code>returnSelf</code> is true.  The order of the iteration is not specified
1465          * (it may be specified in the future).
1466          * <p>
1467          * If an iterator iterating over only the direct children of the component is required,
1468          * use <code>component.getChildren().iterator()</code>.
1469          * 
1470          * TODO: HIGH: Remove this after merges have been done
1471          * 
1472          * @param returnSelf boolean value specifying whether the component itself should be 
1473          *                                       returned
1474          * @return An iterator for the children and sub-children.
1475          * @deprecated Use {@link #iterator(boolean)} instead
1476          */
1477         @Deprecated
1478         public final Iterator<RocketComponent> deepIterator(boolean returnSelf) {
1479                 return iterator(returnSelf);
1480         }
1481         
1482         
1483         /**
1484          * Returns an iterator that iterates over all children and sub-children, including itself.
1485          * <p>
1486          * This method is equivalent to <code>deepIterator(true)</code>.
1487          * 
1488          * TODO: HIGH: Remove this after merges have been done
1489          * 
1490          * @return An iterator for this component, its children and sub-children.
1491          * @deprecated Use {@link #iterator()} instead
1492          */
1493         @Deprecated
1494         public final Iterator<RocketComponent> deepIterator() {
1495                 return iterator();
1496         }
1497         
1498         
1499
1500         /**
1501          * Returns an iterator that iterates over all children and sub-children.
1502          * <p>
1503          * The iterator iterates through all children below this object, including itself if
1504          * <code>returnSelf</code> is true.  The order of the iteration is not specified
1505          * (it may be specified in the future).
1506          * <p>
1507          * If an iterator iterating over only the direct children of the component is required,
1508          * use <code>component.getChildren().iterator()</code>.
1509          * 
1510          * @param returnSelf boolean value specifying whether the component itself should be 
1511          *                                       returned
1512          * @return An iterator for the children and sub-children.
1513          */
1514         public final Iterator<RocketComponent> iterator(boolean returnSelf) {
1515                 checkState();
1516                 return new RocketComponentIterator(this, returnSelf);
1517         }
1518         
1519         
1520         /**
1521          * Returns an iterator that iterates over this components, its children and sub-children.
1522          * <p>
1523          * This method is equivalent to <code>iterator(true)</code>.
1524          * 
1525          * @return An iterator for this component, its children and sub-children.
1526          */
1527         @Override
1528         public final Iterator<RocketComponent> iterator() {
1529                 return iterator(true);
1530         }
1531         
1532         
1533
1534
1535
1536         /**
1537          * Compare component equality based on the ID of this component.  Only the
1538          * ID and class type is used for a basis of comparison.
1539          */
1540         @Override
1541         public boolean equals(Object obj) {
1542                 if (this == obj)
1543                         return true;
1544                 if (obj == null)
1545                         return false;
1546                 if (this.getClass() != obj.getClass())
1547                         return false;
1548                 RocketComponent other = (RocketComponent) obj;
1549                 return this.id.equals(other.id);
1550         }
1551         
1552         
1553
1554         @Override
1555         public int hashCode() {
1556                 return id.hashCode();
1557         }
1558         
1559         
1560
1561         ////////////  Helper methods for subclasses
1562         
1563
1564
1565
1566         /**
1567          * Helper method to add rotationally symmetric bounds at the specified coordinates.
1568          * The X-axis value is <code>x</code> and the radius at the specified position is
1569          * <code>r</code>. 
1570          */
1571         protected static final void addBound(Collection<Coordinate> bounds, double x, double r) {
1572                 bounds.add(new Coordinate(x, -r, -r));
1573                 bounds.add(new Coordinate(x, r, -r));
1574                 bounds.add(new Coordinate(x, r, r));
1575                 bounds.add(new Coordinate(x, -r, r));
1576         }
1577         
1578         
1579         protected static final Coordinate ringCG(double outerRadius, double innerRadius,
1580                         double x1, double x2, double density) {
1581                 return new Coordinate((x1 + x2) / 2, 0, 0,
1582                                 ringMass(outerRadius, innerRadius, x2 - x1, density));
1583         }
1584         
1585         protected static final double ringMass(double outerRadius, double innerRadius,
1586                         double length, double density) {
1587                 return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) *
1588                                         length * density;
1589         }
1590         
1591         protected static final double ringLongitudinalUnitInertia(double outerRadius,
1592                         double innerRadius, double length) {
1593                 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
1594                 return (3 * (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) + MathUtil.pow2(length)) / 12;
1595         }
1596         
1597         protected static final double ringRotationalUnitInertia(double outerRadius,
1598                         double innerRadius) {
1599                 // 1/2 * (r1^2 + r2^2)
1600                 return (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) / 2;
1601         }
1602         
1603         
1604
1605         ////////////  OTHER
1606         
1607
1608         /**
1609          * Loads the RocketComponent fields from the given component.  This method is meant
1610          * for in-place replacement of a component.  It is used with the undo/redo
1611          * mechanism and when converting a finset into a freeform fin set.
1612          * This component must not have a parent, otherwise this method will fail.
1613          * <p>
1614          * The child components in the source tree are copied into the current tree, however,
1615          * the original components should not be used since they represent old copies of the
1616          * components.  It is recommended to invalidate them by calling {@link #invalidate()}.
1617          * <p>
1618          * This method returns a list of components that should be invalidated after references
1619          * to them have been removed (for example by firing appropriate events).  The list contains
1620          * all children and sub-children of the current component and the entire component
1621          * tree of <code>src</code>.
1622          * 
1623          * @return      a list of components that should not be used after this call.
1624          */
1625         protected List<RocketComponent> copyFrom(RocketComponent src) {
1626                 checkState();
1627                 List<RocketComponent> toInvalidate = new ArrayList<RocketComponent>();
1628                 
1629                 if (this.parent != null) {
1630                         throw new UnsupportedOperationException("copyFrom called for non-root component, parent=" +
1631                                         this.parent.toDebugString() + ", this=" + this.toDebugString());
1632                 }
1633                 
1634                 // Add current structure to be invalidated
1635                 Iterator<RocketComponent> iterator = this.iterator(false);
1636                 while (iterator.hasNext()) {
1637                         toInvalidate.add(iterator.next());
1638                 }
1639                 
1640                 // Remove previous components
1641                 for (RocketComponent child : this.children) {
1642                         child.parent = null;
1643                 }
1644                 this.children.clear();
1645                 
1646                 // Copy new children to this component
1647                 for (RocketComponent c : src.children) {
1648                         RocketComponent copy = c.copyWithOriginalID();
1649                         this.children.add(copy);
1650                         copy.parent = this;
1651                 }
1652                 
1653                 this.checkComponentStructure();
1654                 src.checkComponentStructure();
1655                 
1656                 // Set all parameters
1657                 this.length = src.length;
1658                 this.relativePosition = src.relativePosition;
1659                 this.position = src.position;
1660                 this.color = src.color;
1661                 this.lineStyle = src.lineStyle;
1662                 this.overrideMass = src.overrideMass;
1663                 this.massOverriden = src.massOverriden;
1664                 this.overrideCGX = src.overrideCGX;
1665                 this.cgOverriden = src.cgOverriden;
1666                 this.overrideSubcomponents = src.overrideSubcomponents;
1667                 this.name = src.name;
1668                 this.comment = src.comment;
1669                 this.id = src.id;
1670                 
1671                 // Add source components to invalidation tree
1672                 for (RocketComponent c : src) {
1673                         toInvalidate.add(c);
1674                 }
1675                 
1676                 return toInvalidate;
1677         }
1678         
1679         protected void invalidate() {
1680                 invalidator.invalidate();
1681         }
1682         
1683         
1684         //////////  Iterator implementation  ///////////
1685         
1686         /**
1687          * Private inner class to implement the Iterator.
1688          * 
1689          * This iterator is fail-fast if the root of the structure is a Rocket.
1690          */
1691         private static class RocketComponentIterator implements Iterator<RocketComponent> {
1692                 // Stack holds iterators which still have some components left.
1693                 private final Deque<Iterator<RocketComponent>> iteratorStack = new ArrayDeque<Iterator<RocketComponent>>();
1694                 
1695                 private final Rocket root;
1696                 private final int treeModID;
1697                 
1698                 private final RocketComponent original;
1699                 private boolean returnSelf = false;
1700                 
1701                 // Construct iterator with component's child's iterator, if it has elements
1702                 public RocketComponentIterator(RocketComponent c, boolean returnSelf) {
1703                         
1704                         RocketComponent gp = c.getRoot();
1705                         if (gp instanceof Rocket) {
1706                                 root = (Rocket) gp;
1707                                 treeModID = root.getTreeModID();
1708                         } else {
1709                                 root = null;
1710                                 treeModID = -1;
1711                         }
1712                         
1713                         Iterator<RocketComponent> i = c.children.iterator();
1714                         if (i.hasNext())
1715                                 iteratorStack.push(i);
1716                         
1717                         this.original = c;
1718                         this.returnSelf = returnSelf;
1719                 }
1720                 
1721                 @Override
1722                 public boolean hasNext() {
1723                         checkID();
1724                         if (returnSelf)
1725                                 return true;
1726                         return !iteratorStack.isEmpty(); // Elements remain if stack is not empty
1727                 }
1728                 
1729                 @Override
1730                 public RocketComponent next() {
1731                         Iterator<RocketComponent> i;
1732                         
1733                         checkID();
1734                         
1735                         // Return original component first
1736                         if (returnSelf) {
1737                                 returnSelf = false;
1738                                 return original;
1739                         }
1740                         
1741                         // Peek first iterator from stack, throw exception if empty
1742                         i = iteratorStack.peek();
1743                         if (i == null) {
1744                                 throw new NoSuchElementException("No further elements in RocketComponent iterator");
1745                         }
1746                         
1747                         // Retrieve next component of the iterator, remove iterator from stack if empty
1748                         RocketComponent c = i.next();
1749                         if (!i.hasNext())
1750                                 iteratorStack.pop();
1751                         
1752                         // Add iterator of component children to stack if it has children
1753                         i = c.children.iterator();
1754                         if (i.hasNext())
1755                                 iteratorStack.push(i);
1756                         
1757                         return c;
1758                 }
1759                 
1760                 private void checkID() {
1761                         if (root != null) {
1762                                 if (root.getTreeModID() != treeModID) {
1763                                         throw new IllegalStateException("Rocket modified while being iterated");
1764                                 }
1765                         }
1766                 }
1767                 
1768                 @Override
1769                 public void remove() {
1770                         throw new UnsupportedOperationException("remove() not supported by " +
1771                                         "RocketComponent iterator");
1772                 }
1773         }
1774         
1775 }