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