create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / print / PrintableTransition.java
1 package net.sf.openrocket.gui.print;
2
3 import net.sf.openrocket.rocketcomponent.Transition;
4
5 import java.awt.BasicStroke;
6 import java.awt.Graphics2D;
7 import java.awt.geom.Arc2D;
8 import java.awt.geom.GeneralPath;
9 import java.awt.geom.Line2D;
10 import java.awt.geom.Path2D;
11 import java.awt.geom.Point2D;
12
13 /**
14  * This class allows for a Transition to be printable.  It does so by decorating an existing transition (which will not be
15  * modified) and rendering it within a JPanel.  The JPanel is not actually visualized on a display, but instead renders
16  * it to a print device.
17  * <p/>
18  * Note: Currently nose cones are only supported by drawing the 2D projection of the profile.  A more useful approach
19  * may be to draw a myriahedral projection that can be cut out and bent to form the shape.
20  */
21 public class PrintableTransition extends AbstractPrintable<Transition> {
22
23         /**
24          * Dashed array value.
25          */
26         private final static float dash1[] = { 4.0f };
27         /**
28          * The dashed stroke for glue tab.
29          */
30         private final static BasicStroke dashed = new BasicStroke(1.0f,
31                         BasicStroke.CAP_BUTT,
32                         BasicStroke.JOIN_MITER,
33                         10.0f, dash1, 0.0f);
34
35         /**
36          * The layout is an outer arc, an inner arc, and two lines one either endpoints that connect the arcs.
37          * Most of the math involves transposing geometric cartesian coordinates to the Java AWT coordinate system.
38          */
39         private Path2D gp;
40
41         /**
42          * The glue tab.
43          */
44         private Path2D glueTab1;
45
46         /**
47          * The alignment marks.
48          */
49         private Line2D tick1, tick2;
50
51         /**
52          * The x coordinates for the two ticks drawn at theta degrees.
53          */
54         private int tick3X, tick4X;
55
56         /**
57          * The angle, in degrees.
58          */
59         private float theta;
60
61         /**
62          * The x,y coordinates for where the virtual circle center is located.
63          */
64         private int circleCenterX, circleCenterY;
65
66         /**
67          * Constructor.
68          *
69          * @param transition the transition to print
70          */
71         public PrintableTransition(Transition transition) {
72                 super(false, transition);
73         }
74
75     /**
76      * Compute the basic values of each arc of the transition/shroud.  This is adapted from
77      * <a href="http://www.rocketshoppe.com/info/Transitions.pdf">The Properties of
78      * Model Rocket Body Tube Transitions, by J.R. Brohm</a>
79      *
80      * @param component the component
81      */
82         @Override
83         protected void init(Transition component) {
84
85                 double r1 = component.getAftRadius();
86                 double r2 = component.getForeRadius();
87
88                 //Regardless of orientation, we have the convention of R1 as the smaller radius.  Flip if different.
89                 if (r1 > r2) {
90                         r1 = r2;
91                         r2 = component.getAftRadius();
92                 }
93                 double len = component.getLength();
94                 double v = r2 - r1;
95                 double tmp = Math.sqrt(v * v + len * len);
96                 double factor = tmp / v;
97
98                 theta = (float) (360d * v / tmp);
99
100                 int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
101                 int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
102
103                 int x = 0;
104                 int tabOffset = 35;
105                 int y = tabOffset;
106
107                 Arc2D.Double outerArc = new Arc2D.Double();
108                 Arc2D.Double innerArc = new Arc2D.Double();
109
110                 //If the arcs are more than 3/4 of a circle, then assume the height (y) is the same as the radius of the bigger arc.
111                 if (theta >= 270) {
112                         y += r2InPoints;
113                 }
114                 //If the arc is between 1/2 and 3/4 of a circle, then compute the actual height based upon the angle and radius
115                 //of the bigger arc.
116                 else if (theta >= 180) {
117                         double thetaRads = Math.toRadians(theta - 180);
118                         y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
119                 }
120
121                 circleCenterY = y;
122                 circleCenterX = r2InPoints + x;
123
124                 //Create the larger arc.
125                 outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
126
127                 //Create the smaller arc.
128                 innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
129
130                 //Create the line between the start of the larger arc and the start of the smaller arc.
131                 Path2D.Double line = new Path2D.Double();
132                 line.setWindingRule(Path2D.WIND_NON_ZERO);
133                 line.moveTo(x, y);
134                 final int width = r2InPoints - r1InPoints;
135                 line.lineTo(width + x, y);
136
137                 //Create the line between the endpoint of the larger arc and the endpoint of the smaller arc.
138                 Path2D.Double closingLine = new Path2D.Double();
139                 closingLine.setWindingRule(Path2D.WIND_NON_ZERO);
140                 Point2D innerArcEndPoint = innerArc.getEndPoint();
141                 closingLine.moveTo(innerArcEndPoint.getX(), innerArcEndPoint.getY());
142                 Point2D outerArcEndPoint = outerArc.getEndPoint();
143                 closingLine.lineTo(outerArcEndPoint.getX(), outerArcEndPoint.getY());
144
145                 //Add all shapes to the polygon path.
146                 gp = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
147                 gp.append(line, false);
148                 gp.append(outerArc, false);
149                 gp.append(closingLine, false);
150                 gp.append(innerArc, false);
151
152                 //Create the glue tab.
153                 glueTab1 = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
154                 glueTab1.moveTo(x, y);
155                 glueTab1.lineTo(x + tabOffset, y - tabOffset);
156                 glueTab1.lineTo(width + x - tabOffset, y - tabOffset);
157                 glueTab1.lineTo(width + x, y);
158
159                 //Create tick marks for alignment, 1/4 of the width in from either edge
160                 int fromEdge = width / 4;
161                 final int tickLength = 8;
162                 //Upper left
163                 tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
164                 //Upper right
165                 tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
166
167                 tick3X = r2InPoints - fromEdge;
168                 tick4X = r1InPoints + fromEdge;
169
170                 setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
171         }
172
173         /**
174          * Draw alignment marks on an angle.
175          *
176          * @param g2    the graphics context
177          * @param x     the center of the circle's x coordinate
178          * @param y     the center of the circle's y
179          * @param line  the line to draw
180          * @param theta the angle
181          */
182         private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
183                 g2.translate(x, y);
184                 g2.rotate(Math.toRadians(-theta));
185                 g2.draw(line);
186                 g2.rotate(Math.toRadians(theta));
187                 g2.translate(-x, -y);
188         }
189
190         /**
191          * Draw a transition.
192          *
193          * @param g2 the graphics context
194          */
195         @Override
196         protected void draw(Graphics2D g2) {
197                 //Render it.
198                 g2.draw(gp);
199                 g2.draw(tick1);
200                 g2.draw(tick2);
201                 drawAlignmentMarks(g2, circleCenterX,
202                                 circleCenterY,
203                                 new Line2D.Float(-tick3X, 0, -tick3X, -8),
204                                 theta);
205                 drawAlignmentMarks(g2, circleCenterX,
206                                 circleCenterY,
207                                 new Line2D.Float(-tick4X, 0, -tick4X, -8),
208                                 theta);
209
210                 g2.setStroke(dashed);
211                 g2.draw(glueTab1);
212         }
213
214 }