Printable Centering Ring templates.
[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         @Override
76         protected void init(Transition component) {
77
78                 double r1 = component.getAftRadius();
79                 double r2 = component.getForeRadius();
80
81                 //Regardless of orientation, we have the convention of R1 as the smaller radius.  Flip if different.
82                 if (r1 > r2) {
83                         r1 = r2;
84                         r2 = component.getAftRadius();
85                 }
86                 double len = component.getLength();
87                 double v = r2 - r1;
88                 double tmp = Math.sqrt(v * v + len * len);
89                 double factor = tmp / v;
90
91                 theta = (float) (360d * v / tmp);
92
93                 int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
94                 int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
95
96                 int x = marginX;
97                 int tabOffset = 35;
98                 int y = tabOffset + marginY;
99
100                 Arc2D.Double outerArc = new Arc2D.Double();
101                 Arc2D.Double innerArc = new Arc2D.Double();
102
103                 //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.
104                 if (theta >= 270) {
105                         y += r2InPoints;
106                 }
107                 //If the arc is between 1/2 and 3/4 of a circle, then compute the actual height based upon the angle and radius
108                 //of the bigger arc.
109                 else if (theta >= 180) {
110                         double thetaRads = Math.toRadians(theta - 180);
111                         y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
112                 }
113
114                 circleCenterY = y;
115                 circleCenterX = r2InPoints + x;
116
117                 //Create the larger arc.
118                 outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
119
120                 //Create the smaller arc.
121                 innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
122
123                 //Create the line between the start of the larger arc and the start of the smaller arc.
124                 Path2D.Double line = new Path2D.Double();
125                 line.setWindingRule(Path2D.WIND_NON_ZERO);
126                 line.moveTo(x, y);
127                 final int width = r2InPoints - r1InPoints;
128                 line.lineTo(width + x, y);
129
130                 //Create the line between the endpoint of the larger arc and the endpoint of the smaller arc.
131                 Path2D.Double closingLine = new Path2D.Double();
132                 closingLine.setWindingRule(Path2D.WIND_NON_ZERO);
133                 Point2D innerArcEndPoint = innerArc.getEndPoint();
134                 closingLine.moveTo(innerArcEndPoint.getX(), innerArcEndPoint.getY());
135                 Point2D outerArcEndPoint = outerArc.getEndPoint();
136                 closingLine.lineTo(outerArcEndPoint.getX(), outerArcEndPoint.getY());
137
138                 //Add all shapes to the polygon path.
139                 gp = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
140                 gp.append(line, false);
141                 gp.append(outerArc, false);
142                 gp.append(closingLine, false);
143                 gp.append(innerArc, false);
144
145                 //Create the glue tab.
146                 glueTab1 = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
147                 glueTab1.moveTo(x, y);
148                 glueTab1.lineTo(x + tabOffset, y - tabOffset);
149                 glueTab1.lineTo(width + x - tabOffset, y - tabOffset);
150                 glueTab1.lineTo(width + x, y);
151
152                 //Create tick marks for alignment, 1/4 of the width in from either edge
153                 int fromEdge = width / 4;
154                 final int tickLength = 8;
155                 //Upper left
156                 tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
157                 //Upper right
158                 tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
159
160                 tick3X = r2InPoints - fromEdge;
161                 tick4X = r1InPoints + fromEdge;
162
163                 setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
164         }
165
166         /**
167          * Draw alignment marks on an angle.
168          *
169          * @param g2    the graphics context
170          * @param x     the center of the circle's x coordinate
171          * @param y     the center of the circle's y
172          * @param line  the line to draw
173          * @param theta the angle
174          */
175         private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
176                 g2.translate(x, y);
177                 g2.rotate(Math.toRadians(-theta));
178                 g2.draw(line);
179                 g2.rotate(Math.toRadians(theta));
180                 g2.translate(-x, -y);
181         }
182
183         /**
184          * Draw a transition.
185          *
186          * @param g2 the graphics context
187          */
188         @Override
189         protected void draw(Graphics2D g2) {
190                 //Render it.
191                 g2.draw(gp);
192                 g2.draw(tick1);
193                 g2.draw(tick2);
194                 drawAlignmentMarks(g2, circleCenterX,
195                                 circleCenterY,
196                                 new Line2D.Float(-tick3X, 0, -tick3X, -8),
197                                 theta);
198                 drawAlignmentMarks(g2, circleCenterX,
199                                 circleCenterY,
200                                 new Line2D.Float(-tick4X, 0, -tick4X, -8),
201                                 theta);
202
203                 g2.setStroke(dashed);
204                 g2.draw(glueTab1);
205         }
206
207 }