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