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