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