1 package net.sf.openrocket.gui.print;
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;
15 import net.sf.openrocket.rocketcomponent.Transition;
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.
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.
25 public class PrintableTransition extends AbstractPrintableTransition {
30 private final static float dash1[] = { 4.0f };
32 * The dashed stroke for glue tab.
34 private final static BasicStroke dashed = new BasicStroke(1.0f,
36 BasicStroke.JOIN_MITER,
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.
48 private Path2D glueTab1;
51 * The alignment marks.
53 private Line2D tick1, tick2;
56 * The x coordinates for the two ticks drawn at theta degrees.
58 private int tick3X, tick4X;
61 * The angle, in degrees.
66 * The x,y coordinates for where the virtual circle center is located.
68 private int circleCenterX, circleCenterY;
73 * @param transition the transition to print
75 public PrintableTransition(Transition transition) {
76 super(false, transition);
80 protected void init(Transition component) {
82 double r1 = component.getAftRadius();
83 double r2 = component.getForeRadius();
85 //Regardless of orientation, we have the convention of R1 as the smaller radius. Flip if different.
88 r2 = component.getAftRadius();
90 double len = component.getLength();
92 double tmp = Math.sqrt(v * v + len * len);
93 double factor = tmp / v;
95 theta = (float) (360d * v / tmp);
97 int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
98 int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
102 int y = tabOffset + marginY;
104 Arc2D.Double outerArc = new Arc2D.Double();
105 Arc2D.Double innerArc = new Arc2D.Double();
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.
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
113 else if (theta >= 180) {
114 double thetaRads = Math.toRadians(theta - 180);
115 y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
119 circleCenterX = r2InPoints + x;
121 //Create the larger arc.
122 outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
124 //Create the smaller arc.
125 innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
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);
131 final int width = r2InPoints - r1InPoints;
132 line.lineTo(width + x, y);
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());
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);
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);
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;
160 tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
162 tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
164 tick3X = r2InPoints - fromEdge;
165 tick4X = r1InPoints + fromEdge;
167 setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
171 * Draw alignment marks on an angle.
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
179 private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
181 g2.rotate(Math.toRadians(-theta));
183 g2.rotate(Math.toRadians(theta));
184 g2.translate(-x, -y);
190 * @param g2 the graphics context
193 protected void draw(Graphics2D g2) {
198 drawAlignmentMarks(g2, circleCenterX,
200 new Line2D.Float(-tick3X, 0, -tick3X, -8),
202 drawAlignmentMarks(g2, circleCenterX,
204 new Line2D.Float(-tick4X, 0, -tick4X, -8),
207 g2.setStroke(dashed);