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