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