Fix bug with fin marking guide. If a rocket has some split fins

asymetrically placed around the rocket, the fin marking lines might not
be drawn in the wrap.  I simplified the algorithm used so first we
determine what angle the split in the wrap is, then we position each
line relative to it.
This commit is contained in:
kruland2607 2013-01-26 22:29:11 -06:00
parent 4141b478f7
commit 775b9d1d13

View File

@ -1,15 +1,5 @@
package net.sf.openrocket.gui.print; package net.sf.openrocket.gui.print;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import javax.swing.JPanel;
import java.awt.BasicStroke; import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
@ -21,12 +11,22 @@ import java.awt.geom.Path2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.swing.JPanel;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
/** /**
* This is the core Swing representation of a fin marking guide. It can handle multiple fin sets on the same or * This is the core Swing representation of a fin marking guide. It can handle multiple fin sets on the same or
* different body tubes. One marking guide will be created for any body tube that has a finset. If a tube has multiple * different body tubes. One marking guide will be created for any body tube that has a finset. If a tube has multiple
@ -36,448 +36,446 @@ import java.util.Map;
* <p/> * <p/>
*/ */
public class FinMarkingGuide extends JPanel { public class FinMarkingGuide extends JPanel {
/** /**
* The stroke of normal lines. * The stroke of normal lines.
*/ */
private final static BasicStroke thinStroke = new BasicStroke(1.0f); private final static BasicStroke thinStroke = new BasicStroke(1.0f);
/** /**
* The size of the arrow in points. * The size of the arrow in points.
*/ */
private static final int ARROW_SIZE = 10; private static final int ARROW_SIZE = 10;
/** /**
* Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the * Typical thickness of a piece of printer paper (~20-24 lb paper). Wrapping paper around a tube results in the
* radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a * radius being increased by the thickness of the paper. The smaller the tube, the more pronounced this becomes as a
* percentage of circumference. Using 1/10mm as an approximation here. * percentage of circumference. Using 1/10mm as an approximation here.
*/ */
private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d); private static final double PAPER_THICKNESS_IN_METERS = PrintUnit.MILLIMETERS.toMeters(0.1d);
/** /**
* The default guide width in inches. * The default guide width in inches.
*/ */
public final static double DEFAULT_GUIDE_WIDTH = 3d; public final static double DEFAULT_GUIDE_WIDTH = 3d;
/** /**
* 2 PI radians (represents a circle). * 2 PI radians (represents a circle).
*/ */
public final static double TWO_PI = 2 * Math.PI; public final static double TWO_PI = 2 * Math.PI;
public final static double PI = Math.PI;
/**
* The I18N translator. /**
*/ * The I18N translator.
private static final Translator trans = Application.getTranslator(); */
private static final Translator trans = Application.getTranslator();
/**
* The margin. /**
*/ * The margin.
private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f); */
private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f);
/**
* The height (circumference) of the biggest body tube with a finset. /**
*/ * The height (circumference) of the biggest body tube with a finset.
private int maxHeight = 0; */
private int maxHeight = 0;
/**
* A map of body tubes, to a list of components that contains finsets and launch lugs. /**
*/ * A map of body tubes, to a list of components that contains finsets and launch lugs.
private Map<BodyTube, java.util.List<ExternalComponent>> markingGuideItems; */
private Map<BodyTube, java.util.List<ExternalComponent>> markingGuideItems;
/**
* Constructor. /**
* * Constructor.
* @param rocket the rocket instance *
*/ * @param rocket the rocket instance
public FinMarkingGuide(Rocket rocket) { */
super(false); public FinMarkingGuide(Rocket rocket) {
setBackground(Color.white); super(false);
markingGuideItems = init(rocket); setBackground(Color.white);
//Max of 2 drawing guides horizontally per page. markingGuideItems = init(rocket);
setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight); //Max of 2 drawing guides horizontally per page.
} setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight);
}
/**
* Initialize the marking guide class by iterating over a rocket and finding all finsets. /**
* * Initialize the marking guide class by iterating over a rocket and finding all finsets.
* @param component the root rocket component - this is iterated to find all finset and launch lugs *
* * @param component the root rocket component - this is iterated to find all finset and launch lugs
* @return a map of body tubes to lists of finsets and launch lugs. *
*/ * @return a map of body tubes to lists of finsets and launch lugs.
private Map<BodyTube, java.util.List<ExternalComponent>> init(Rocket component) { */
Iterator<RocketComponent> iter = component.iterator(false); private Map<BodyTube, java.util.List<ExternalComponent>> init(Rocket component) {
Map<BodyTube, java.util.List<ExternalComponent>> results = new LinkedHashMap<BodyTube, List<ExternalComponent>>(); Iterator<RocketComponent> iter = component.iterator(false);
BodyTube current = null; Map<BodyTube, java.util.List<ExternalComponent>> results = new LinkedHashMap<BodyTube, List<ExternalComponent>>();
int totalHeight = 0; BodyTube current = null;
int iterationHeight = 0; int totalHeight = 0;
int count = 0; int iterationHeight = 0;
int count = 0;
while (iter.hasNext()) {
RocketComponent next = iter.next(); while (iter.hasNext()) {
if (next instanceof BodyTube) { RocketComponent next = iter.next();
current = (BodyTube) next; if (next instanceof BodyTube) {
} current = (BodyTube) next;
else if (next instanceof FinSet || next instanceof LaunchLug) { }
java.util.List<ExternalComponent> list = results.get(current); else if (next instanceof FinSet || next instanceof LaunchLug) {
if (list == null && current != null) { java.util.List<ExternalComponent> list = results.get(current);
list = new ArrayList<ExternalComponent>(); if (list == null && current != null) {
results.put(current, list); list = new ArrayList<ExternalComponent>();
results.put(current, list);
double radius = current.getOuterRadius();
int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI); double radius = current.getOuterRadius();
int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI);
// Find the biggest body tube circumference.
if (iterationHeight < (circumferenceInPoints + MARGIN)) { // Find the biggest body tube circumference.
iterationHeight = circumferenceInPoints + MARGIN; if (iterationHeight < (circumferenceInPoints + MARGIN)) {
} iterationHeight = circumferenceInPoints + MARGIN;
//At most, two marking guides horizontally. After that, move down and back to the left margin. }
count++; //At most, two marking guides horizontally. After that, move down and back to the left margin.
if (count % 2 == 0) { count++;
totalHeight += iterationHeight; if (count % 2 == 0) {
iterationHeight = 0; totalHeight += iterationHeight;
} iterationHeight = 0;
} }
if (list != null) { }
list.add((ExternalComponent) next); if (list != null) {
} list.add((ExternalComponent) next);
} }
} }
maxHeight = totalHeight + iterationHeight; }
return results; maxHeight = totalHeight + iterationHeight;
} return results;
}
/**
* Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or /**
* converted to another image/picture format and used accordingly. * Returns a generated image of the fin marking guide. May then be used wherever AWT images can be used, or
* * converted to another image/picture format and used accordingly.
* @return an awt image of the fin marking guide *
*/ * @return an awt image of the fin marking guide
public Image createImage() { */
int width = getWidth() + 25; public Image createImage() {
int height = getHeight() + 25; int width = getWidth() + 25;
// Create a buffered image in which to draw int height = getHeight() + 25;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // Create a buffered image in which to draw
// Create a graphics context on the buffered image BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bufferedImage.createGraphics(); // Create a graphics context on the buffered image
// Draw graphics Graphics2D g2d = bufferedImage.createGraphics();
g2d.setBackground(Color.white); // Draw graphics
g2d.clearRect(0, 0, width, height); g2d.setBackground(Color.white);
paintComponent(g2d); g2d.clearRect(0, 0, width, height);
// Graphics context no longer needed so dispose it paintComponent(g2d);
g2d.dispose(); // Graphics context no longer needed so dispose it
return bufferedImage; g2d.dispose();
} return bufferedImage;
}
/**
* <pre> /**
* ---------------------- Page Edge -------------------------------------------------------- * <pre>
* | ^ * ---------------------- Page Edge --------------------------------------------------------
* | | * | ^
* | * | |
* | y * |
* | * | y
* | | * |
* P v * | |
* a --- +----------------------------+ ------------ * P v
* g<------^-- x ------------>+ + ^ * a --- +----------------------------+ <- radialOrigin (in radians)
* e | + + | * g<------^-- x ------------>+ +
* | + + baseYOffset * e | + +
* E | + + v * | + +
* d | +<----------Fin------------->+ ------------- * E | + +
* g | + + * d | +<----------Fin------------->+ <- y+ (finRadialPosition - radialOrigin) / TWO_PI * circumferenceInPoints
* e | + + * g | + +
* | | + + * e | + +
* | | + + * | | + +
* | | + + baseSpacing * | | + +
* | | + + * | | + +
* | | + + * | | + +
* | | + + * | | + +
* | | + + * | | + +
* | | +<----------Fin------------->+ -------------- * | | + +
* | | + + * | | +<----------Fin------------->+
* | circumferenceInPoints + + * | | + +
* | | + + * | circumferenceInPoints + +
* | | + + * | | + +
* | | + + baseSpacing * | | + +
* | | +<------Launch Lug --------->+ ----- * | | + +
* | | + + \ * | | +<------Launch Lug --------->+
* | | + + + yLLOffset * | | + +
* | | + + / * | | + +
* | | +<----------Fin------------->+ -------------- * | | + +
* | | + + ^ * | | +<----------Fin------------->+
* | | + + | * | | + +
* | | + + baseYOffset * | | + +
* | v + + v * | | + +
* | --- +----------------------------+ -------------- * | v + +
* * | --- +----------------------------+ <- radialOrigin + TWO_PI
* |<-------- width ----------->| *
* * |<-------- width ----------->|
* yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the *
* lug. * yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the
* * lug.
* Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines *
* correctly. * Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines
* </pre> * correctly.
* * </pre>
* @param g the Graphics context *
*/ * @param g the Graphics context
@Override */
public void paintComponent(Graphics g) { @Override
super.paintComponent(g); public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g; super.paintComponent(g);
paintFinMarkingGuide(g2); Graphics2D g2 = (Graphics2D) g;
} paintFinMarkingGuide(g2);
}
private void paintFinMarkingGuide(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, private void paintFinMarkingGuide(Graphics2D g2) {
RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK);
g2.setStroke(thinStroke); g2.setColor(Color.BLACK);
int x = MARGIN; g2.setStroke(thinStroke);
int y = MARGIN; int x = MARGIN;
int y = MARGIN;
int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH);
int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH);
int column = 0;
int column = 0;
for (BodyTube next : markingGuideItems.keySet()) {
double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) * for (BodyTube next : markingGuideItems.keySet()) {
TWO_PI); double circumferenceInPoints = PrintUnit.METERS.toPoints((next.getOuterRadius() + PAPER_THICKNESS_IN_METERS) *
List<ExternalComponent> componentList = markingGuideItems.get(next); TWO_PI);
//Don't draw the lug if there are no fins. List<ExternalComponent> componentList = markingGuideItems.get(next);
if (hasFins(componentList)) { //Don't draw the lug if there are no fins.
if (hasFins(componentList)) {
drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width);
drawMarkingGuide(g2, x, y, (int) Math.ceil(circumferenceInPoints), width);
//Sort so that fins always precede lugs
sort(componentList); double radialOrigin = findRadialOrigin(componentList);
boolean hasMultipleComponents = componentList.size() > 1; boolean hasMultipleComponents = componentList.size() > 1;
double baseSpacing = 0d; //fin1: 42 fin2: 25
double baseYOrigin = 0; for (ExternalComponent externalComponent : componentList) {
double finRadial = 0d; if (externalComponent instanceof FinSet) {
int yFirstFin = y; FinSet fins = (FinSet) externalComponent;
int yLastFin = y; int finCount = fins.getFinCount();
boolean firstFinSet = true; double baseAngularSpacing = (TWO_PI / finCount);
double baseAngularOffset = fins.getBaseRotation();
//fin1: 42 fin2: 25 //Draw the fin marking lines.
for (ExternalComponent externalComponent : componentList) { for (int fin = 0; fin < finCount; fin++) {
if (externalComponent instanceof FinSet) { double angle = baseAngularOffset + fin * baseAngularSpacing - radialOrigin;
FinSet fins = (FinSet) externalComponent; // Translate angle into pixels using a linear transformation:
int finCount = fins.getFinCount(); // radialOrigin -> y
int offset = 0; // radialOrigin + TWO_PI -> y + circumferenceInPoints
baseSpacing = (circumferenceInPoints / finCount);
double baseRotation = fins.getBaseRotation(); while (angle < 0) {
if (!firstFinSet) { angle += TWO_PI;
//Adjust the rotation to a positive number. }
while (baseRotation < 0) { while (angle > TWO_PI) {
baseRotation += TWO_PI / finCount; angle -= TWO_PI;
} }
offset = computeYOffset(y, circumferenceInPoints, baseSpacing, baseYOrigin, finRadial, baseRotation);
} int offset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints);
else {
//baseYOrigin is the distance from the top of the marking guide to the first fin of the first fin set. drawDoubleArrowLine(g2, x, offset, x + width, offset);
//This measurement is used to base all subsequent finsets and lugs off of. // if (hasMultipleComponents) {
baseYOrigin = baseSpacing / 2; g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2);
offset = (int) (baseYOrigin) + y; // }
firstFinSet = false; }
} }
yFirstFin = y; else if (externalComponent instanceof LaunchLug) {
yLastFin = y; LaunchLug lug = (LaunchLug) externalComponent;
finRadial = baseRotation; double angle = lug.getRadialDirection() - radialOrigin;
while (angle < 0) {
//Draw the fin marking lines. angle += TWO_PI;
for (int fin = 0; fin < finCount; fin++) { }
if (fin > 0) { int yLLOffset = (int) Math.round(y + angle / TWO_PI * circumferenceInPoints);
offset += baseSpacing; drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset);
yLastFin = offset; g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2);
}
else { }
yFirstFin = offset; }
} //Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'.
drawDoubleArrowLine(g2, x, offset, x + width, offset); if (hasMultipleComponents) {
// if (hasMultipleComponents) { drawFrontIndication(g2, x, y, 0, (int) circumferenceInPoints, width);
g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2); }
// }
} //At most, two marking guides horizontally. After that, move down and back to the left margin.
} column++;
else if (externalComponent instanceof LaunchLug) { if (column % 2 == 0) {
LaunchLug lug = (LaunchLug) externalComponent; x = MARGIN;
double yLLOffset = (lug.getRadialDirection() - finRadial) / TWO_PI; y += circumferenceInPoints + MARGIN;
//The placement of the lug line must respect the boundary of the outer marking guide. In order }
//to do that, place it above or below either the top or bottom fin line, based on the difference else {
//between their rotational directions. x += MARGIN + width;
if (yLLOffset < 0) { }
yLLOffset = yLLOffset * circumferenceInPoints + yLastFin; }
} }
else { }
yLLOffset = yLLOffset * circumferenceInPoints + yFirstFin;
} /**
drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset); * This function finds a origin in radians for the template so no component is on the template seam.
g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2); *
* If no fin or launch lug is at 0.0 radians, then the origin is 0. If there is one, then half the distance
} * between the two are taken.
} *
//Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'. * @param components
if (hasMultipleComponents) { * @return
drawFrontIndication(g2, x, y, (int) baseSpacing, (int) circumferenceInPoints, width); */
} private double findRadialOrigin(List<ExternalComponent> components) {
//At most, two marking guides horizontally. After that, move down and back to the left margin. ArrayList<Double> positions = new ArrayList<Double>(3 * components.size());
column++;
if (column % 2 == 0) { for (ExternalComponent component : components) {
x = MARGIN;
y += circumferenceInPoints + MARGIN; if (component instanceof LaunchLug) {
} double componentPosition = ((LaunchLug) component).getRadialDirection();
else {
x += MARGIN + width; positions.add(makeZeroTwoPi(componentPosition));
} }
}
} if (component instanceof FinSet) {
} FinSet fins = (FinSet) component;
double basePosition = fins.getBaseRotation();
/** double angle = TWO_PI / fins.getFinCount();
* Compute the y offset for the next fin line. for (int i = fins.getFinCount(); i > 0; i--) {
* positions.add(makeZeroTwoPi(basePosition));
* @param y the top margin basePosition += angle;
* @param circumferenceInPoints the circumference (height) of the guide }
* @param baseSpacing the circumference / fin count }
* @param baseYOrigin the offset from the top of the guide to the first fin of the first fin set drawn }
* @param prevBaseRotation the rotation of the previous finset
* @param baseRotation the rotation of the current finset Collections.sort(positions);
*
* @return number of points from the top of the marking guide to the line to be drawn Double[] pos = positions.toArray(new Double[0]);
*/
private int computeYOffset(int y, double circumferenceInPoints, double baseSpacing, double baseYOrigin, double prevBaseRotation, double baseRotation) { if (pos.length == 1) {
int offset;
double finRadialDifference = (baseRotation - prevBaseRotation) / TWO_PI; return makeZeroTwoPi(pos[0] + PI);
//If the fin line would be off the top of the marking guide, then readjust.
if (baseYOrigin + finRadialDifference * circumferenceInPoints < 0) { }
offset = (int) (baseYOrigin + baseSpacing + finRadialDifference * circumferenceInPoints) + y;
} double biggestDistance = TWO_PI - pos[pos.length - 1] + pos[0];
else if (baseYOrigin - finRadialDifference * circumferenceInPoints > 0) { double center = makeZeroTwoPi(pos[0] - biggestDistance / 2.0);
offset = (int) (finRadialDifference * circumferenceInPoints + baseYOrigin) + y;
} for (int i = 1; i < pos.length; i++) {
else {
offset = (int) (finRadialDifference * circumferenceInPoints - baseYOrigin) + y; double d = pos[i] - pos[i - 1];
} if (d > biggestDistance) {
return offset; biggestDistance = d;
} center = makeZeroTwoPi(pos[i - 1] + biggestDistance / 2.0);
}
/** }
* Determines if the list contains a FinSet.
* return center;
* @param list a list of ExternalComponent }
*
* @return true if the list contains at least one FinSet private static double makeZeroTwoPi(double value) {
*/
private boolean hasFins(List<ExternalComponent> list) { double v = value;
for (ExternalComponent externalComponent : list) { while (v < 0) {
if (externalComponent instanceof FinSet) { v += TWO_PI;
return true; }
} while (v > TWO_PI) {
} v -= TWO_PI;
return false; }
}
return v;
/** }
* Sort a list of ExternalComponent in-place. Forces FinSets to precede Launch Lugs.
* /**
* @param componentList a list of ExternalComponent * Determines if the list contains a FinSet.
*/ *
private void sort(List<ExternalComponent> componentList) { * @param list a list of ExternalComponent
Collections.sort(componentList, new Comparator<ExternalComponent>() { *
@Override * @return true if the list contains at least one FinSet
public int compare(ExternalComponent o1, ExternalComponent o2) { */
if (o1 instanceof FinSet) { private boolean hasFins(List<ExternalComponent> list) {
return -1; for (ExternalComponent externalComponent : list) {
} if (externalComponent instanceof FinSet) {
if (o2 instanceof FinSet) { return true;
return 1; }
} }
return 0; return false;
} }
});
} /**
* Draw the marking guide outline.
/** *
* Draw the marking guide outline. * @param g2 the graphics context
* * @param x the starting x coordinate
* @param g2 the graphics context * @param y the starting y coordinate
* @param x the starting x coordinate * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
* @param y the starting y coordinate * circumference
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube * @param width the width of the marking guide in print units; somewhat arbitrary
* circumference */
* @param width the width of the marking guide in print units; somewhat arbitrary private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) {
*/ Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) { outline.moveTo(x, y);
Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4); outline.lineTo(width + x, y);
outline.moveTo(x, y); outline.lineTo(width + x, length + y);
outline.lineTo(width + x, y); outline.lineTo(x, length + y);
outline.lineTo(width + x, length + y); outline.closePath();
outline.lineTo(x, length + y); g2.draw(outline);
outline.closePath();
g2.draw(outline); //Draw tick marks for alignment, 1/4 of the width in from either edge
int fromEdge = (width) / 4;
//Draw tick marks for alignment, 1/4 of the width in from either edge final int tickLength = 8;
int fromEdge = (width) / 4; //Upper left
final int tickLength = 8; g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength);
//Upper left //Upper right
g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength); g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
//Upper right //Lower left
g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength); g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length);
//Lower left //Lower right
g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length); g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length);
//Lower right }
g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length);
} /**
* Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give
/** * proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin).
* Draw a vertical string indicating the front of the rocket. This is necessary when a launch lug exists to give *
* proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin). * @param g2 the graphics context
* * @param x the starting x coordinate
* @param g2 the graphics context * @param y the starting y coordinate
* @param x the starting x coordinate * @param spacing the space between fin lines
* @param y the starting y coordinate * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube
* @param spacing the space between fin lines * circumference
* @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube * @param width the width of the marking guide in print units; somewhat arbitrary
* circumference */
* @param width the width of the marking guide in print units; somewhat arbitrary private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) {
*/ //The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be
private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) { //readable, without going to complex string layout prediction logic.
//The magic numbers here are fairly arbitrary. They're chosen in a manner that best positions 'Front' to be int rotateX = x + width - 16;
//readable, without going to complex string layout prediction logic. int rotateY = y + (int) (spacing * 1.5) + 20;
int rotateX = x + width - 16; if (rotateY > y + length + 14) {
int rotateY = y + (int) (spacing * 1.5) + 20; rotateY = y + length / 2 - 10;
if (rotateY > y + length + 14) { }
rotateY = y + length / 2 - 10; g2.translate(rotateX, rotateY);
} g2.rotate(Math.PI / 2);
g2.translate(rotateX, rotateY); g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0);
g2.rotate(Math.PI / 2); g2.rotate(-Math.PI / 2);
g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0); g2.translate(-rotateX, -rotateY);
g2.rotate(-Math.PI / 2); }
g2.translate(-rotateX, -rotateY);
} /**
* Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment.
/** *
* Draw a horizontal line with arrows on both endpoints. Depicts a fin alignment. * @param g2 the graphics context
* * @param x1 the starting x coordinate
* @param g2 the graphics context * @param y1 the starting y coordinate
* @param x1 the starting x coordinate * @param x2 the ending x coordinate
* @param y1 the starting y coordinate * @param y2 the ending y coordinate
* @param x2 the ending x coordinate */
* @param y2 the ending y coordinate void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) {
*/ int len = x2 - x1;
void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) {
int len = x2 - x1; g2.drawLine(x1, y1, x1 + len, y2);
g2.fillPolygon(new int[] { x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len },
g2.drawLine(x1, y1, x1 + len, y2); new int[] { y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2 }, 4);
g2.fillPolygon(new int[]{x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len},
new int[]{y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2}, 4); g2.fillPolygon(new int[] { x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1 },
new int[] { y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1 }, 4);
g2.fillPolygon(new int[]{x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1}, }
new int[]{y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1}, 4);
}
} }