create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / print / PrintableCenteringRing.java
1 package net.sf.openrocket.gui.print;
2
3 import net.sf.openrocket.gui.print.visitor.CenteringRingStrategy;
4 import net.sf.openrocket.rocketcomponent.CenteringRing;
5 import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
6 import net.sf.openrocket.rocketcomponent.InnerTube;
7 import net.sf.openrocket.util.ArrayList;
8 import net.sf.openrocket.util.Coordinate;
9
10 import java.awt.Color;
11 import java.awt.Graphics;
12 import java.awt.Graphics2D;
13 import java.awt.Shape;
14 import java.awt.geom.Ellipse2D;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Set;
18
19 /**
20  * This class creates a renderable centering ring.  It depends only on AWT/Swing and can be called from other actors
21  * (like iText handlers) to render the centering ring on different graphics contexts.
22  */
23 public class PrintableCenteringRing extends AbstractPrintable<CenteringRing> {
24     /**
25      * If the component to be drawn is a centering ring, save a reference to it.
26      */
27     private CenteringRing target;
28
29     /**
30      * The line length of the cross hairs.
31      */
32     private final int lineLength = 10;
33
34     /**
35      * A set of the inner 'holes'.  At least one, but will have many if clustered.
36      */
37     private Set<CenteringRingStrategy.Dimension> innerCenterPoints = new HashSet<CenteringRingStrategy.Dimension>();
38
39     /**
40      * Construct a simple, non-clustered, printable centering ring, or if the motor mount represents a clustered
41      * configuration then get the cluster points to create the centering ring.
42      *
43      * @param theRing       the component to print
44      * @param theMotorMount the motor mount if clustered, else null
45      */
46     private PrintableCenteringRing(CenteringRing theRing, InnerTube theMotorMount) {
47         super(false, theRing);
48         if (theMotorMount == null || theMotorMount.getClusterConfiguration().equals(ClusterConfiguration.SINGLE)) {
49             //Single motor.
50             final float v = (float) PrintUnit.METERS.toPoints(target.getOuterRadius());
51             innerCenterPoints.add(
52                     new CenteringRingStrategy.Dimension(v, v,
53                             (float) PrintUnit.METERS.toPoints((target.getInnerRadius()))));
54         }
55         else {
56             List<Coordinate> coords = theMotorMount.getClusterPoints();
57             List<Coordinate> points = new ArrayList<Coordinate>();
58             for (Coordinate coordinate : coords) {
59                 points.add(coordinate.setX(theMotorMount.getOuterRadius()));
60             }
61             populateCenterPoints(points);
62         }
63     }
64
65     /**
66      * Constructor for a clustered centering ring.  This version is for a "split cluster", where each motor mount tube
67      * is a distinct entity.
68      *
69      * @param theRing        the centering ring component
70      * @param theMotorMounts a list of the motor mount tubes that are physically supported by the centering ring
71      */
72     private PrintableCenteringRing(CenteringRing theRing, List<InnerTube> theMotorMounts) {
73         super(false, theRing);
74         List<Coordinate> points = new ArrayList<Coordinate>();
75         //Transform the radial positions of the tubes.
76         for (InnerTube it : theMotorMounts) {
77             if (it.getClusterCount() > 1) {
78                 List<Coordinate> c = it.getClusterPoints();
79                 for (Coordinate coordinate : c) {
80                     points.add(coordinate.setX(it.getOuterRadius()));
81                 }
82             }
83             else {
84                 double y = it.getRadialShiftY();
85                 double z = it.getRadialShiftZ();
86                 Coordinate coordinate = new Coordinate(it.getOuterRadius(), y, z);
87                 points.add(coordinate);
88             }
89         }
90         populateCenterPoints(points);
91     }
92
93     /**
94      * Factory method to create a printable centering ring.
95      *
96      * @param theRing        the component to print
97      * @param theMotorMounts the motor mount if clustered, else null
98      */
99     public static PrintableCenteringRing create(CenteringRing theRing, List<InnerTube> theMotorMounts) {
100         if (theMotorMounts == null) {
101             return new PrintableCenteringRing(theRing, (InnerTube) null);
102         }
103         else if (theMotorMounts.size() <= 1) {
104             return new PrintableCenteringRing(theRing, theMotorMounts.isEmpty() ? null : theMotorMounts.get(0));
105         }
106         else {
107             return new PrintableCenteringRing(theRing, theMotorMounts);
108         }
109     }
110
111     /**
112      * Initialize the set of center points for each motor mount tube, based on the tube coordinates.
113      *
114      * @param theCoords the list of tube coordinates; each coordinate is in the OR units (meters) and must be
115      *                  transformed to the printing (points) coordinate system
116      */
117     private void populateCenterPoints(final List<Coordinate> theCoords) {
118         float radius = (float) PrintUnit.METERS.toPoints(target.getOuterRadius());
119         for (Coordinate coordinate : theCoords) {
120             innerCenterPoints.add(new CenteringRingStrategy.Dimension(
121                     (float) PrintUnit.METERS.toPoints(coordinate.y) + radius, //center point x
122                     (float) PrintUnit.METERS.toPoints(coordinate.z) + radius, //center point y
123                     (float) PrintUnit.METERS.toPoints(coordinate.x)));        //radius of motor mount
124         }
125     }
126
127     /**
128      * @param component the centering ring component
129      */
130     @Override
131     protected void init(final CenteringRing component) {
132
133         target = component;
134
135         double radius = target.getOuterRadius();
136         setSize((int) PrintUnit.METERS.toPoints(2 * radius),
137                 (int) PrintUnit.METERS.toPoints(2 * radius));
138     }
139
140     /**
141      * Draw a centering ring.
142      *
143      * @param g2 the graphics context
144      */
145     @Override
146     protected void draw(Graphics2D g2) {
147         double radius = PrintUnit.METERS.toPoints(target.getOuterRadius());
148
149         Color original = g2.getBackground();
150         Shape outerCircle = new Ellipse2D.Double(0, 0, radius * 2, radius * 2);
151         g2.setColor(Color.lightGray);
152         g2.fill(outerCircle);
153         g2.setColor(Color.black);
154         g2.draw(outerCircle);
155
156         for (CenteringRingStrategy.Dimension next : innerCenterPoints) {
157             drawInnerCircle(g2, next.getWidth(), next.getHeight(), next.getBreadth());
158         }
159         g2.setColor(original);
160     }
161
162     /**
163      * Draw one inner circle, representing the motor mount tube, with cross hairs in the center.
164      *
165      * @param g2         the graphics context
166      * @param theCenterX the center x in points
167      * @param theCenterY the center y in points
168      */
169     private void drawInnerCircle(final Graphics2D g2, final double theCenterX, final double theCenterY,
170                                  final double innerRadius) {
171         Shape innerCircle = new Ellipse2D.Double(theCenterX - innerRadius, theCenterY - innerRadius, innerRadius * 2, innerRadius * 2);
172         g2.setColor(Color.white);
173         g2.fill(innerCircle);
174         g2.setColor(Color.black);
175         g2.draw(innerCircle);
176
177         drawCross(g2, (int) theCenterX, (int) theCenterY, lineLength, lineLength);
178     }
179
180     /**
181      * Draw the center cross-hair.
182      *
183      * @param g      the graphics context
184      * @param x      the x coordinate of the center point
185      * @param y      the y coordinate of the center point
186      * @param width  the width in pixels of the horizontal hair
187      * @param height the width in pixels of the vertical hair
188      */
189     private void drawCross(Graphics g, int x, int y, int width, int height) {
190         g.setColor(Color.black);
191         ((Graphics2D) g).setStroke(thinStroke);
192         g.drawLine(x - width / 2, y, x + width / 2, y);
193         g.drawLine(x, y - height / 2, x, y + height / 2);
194     }
195
196 }