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