1 package net.sf.openrocket.gui.print;
3 import net.sf.openrocket.rocketcomponent.Transition;
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;
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.
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.
21 public class PrintableTransition extends AbstractPrintable<Transition> {
26 private final static float dash1[] = { 4.0f };
28 * The dashed stroke for glue tab.
30 private final static BasicStroke dashed = new BasicStroke(1.0f,
32 BasicStroke.JOIN_MITER,
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.
44 private Path2D glueTab1;
47 * The alignment marks.
49 private Line2D tick1, tick2;
52 * The x coordinates for the two ticks drawn at theta degrees.
54 private int tick3X, tick4X;
57 * The angle, in degrees.
62 * The x,y coordinates for where the virtual circle center is located.
64 private int circleCenterX, circleCenterY;
69 * @param transition the transition to print
71 public PrintableTransition(Transition transition) {
72 super(false, transition);
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>
80 * @param component the component
83 protected void init(Transition component) {
85 double r1 = component.getAftRadius();
86 double r2 = component.getForeRadius();
88 //Regardless of orientation, we have the convention of R1 as the smaller radius. Flip if different.
91 r2 = component.getAftRadius();
93 double len = component.getLength();
95 double tmp = Math.sqrt(v * v + len * len);
96 double factor = tmp / v;
98 theta = (float) (360d * v / tmp);
100 int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
101 int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
107 Arc2D.Double outerArc = new Arc2D.Double();
108 Arc2D.Double innerArc = new Arc2D.Double();
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.
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
116 else if (theta >= 180) {
117 double thetaRads = Math.toRadians(theta - 180);
118 y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
122 circleCenterX = r2InPoints + x;
124 //Create the larger arc.
125 outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
127 //Create the smaller arc.
128 innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
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);
134 final int width = r2InPoints - r1InPoints;
135 line.lineTo(width + x, y);
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());
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);
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);
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;
163 tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
165 tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
167 tick3X = r2InPoints - fromEdge;
168 tick4X = r1InPoints + fromEdge;
170 setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
174 * Draw alignment marks on an angle.
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
182 private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
184 g2.rotate(Math.toRadians(-theta));
186 g2.rotate(Math.toRadians(theta));
187 g2.translate(-x, -y);
193 * @param g2 the graphics context
196 protected void draw(Graphics2D g2) {
201 drawAlignmentMarks(g2, circleCenterX,
203 new Line2D.Float(-tick3X, 0, -tick3X, -8),
205 drawAlignmentMarks(g2, circleCenterX,
207 new Line2D.Float(-tick4X, 0, -tick4X, -8),
210 g2.setStroke(dashed);