--- /dev/null
+package net.sf.openrocket.gui.print.components;
+
+import net.sf.openrocket.gui.print.PrintUnit;
+import net.sf.openrocket.gui.print.PrintableComponent;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+
+/**
+ * This class creates a Swing ruler. The ruler has both vertical and horizontal rules, as well as divisions for both
+ * inches and centimeters.
+ */
+public class Rule extends PrintableComponent {
+
+ public static enum Orientation {
+ TOP,
+ BOTTOM
+ }
+
+ public static final int TINIEST_TICK_LENGTH = 3;
+ public static final int MINOR_TICK_LENGTH = 6;
+ public static final int MID_MAJOR_TICK_LENGTH = 9;
+ public static final int MAJOR_TICK_LENGTH = 14;
+
+ private Orientation orientation;
+
+ /**
+ * Constructor.
+ *
+ * @param theOrientation defines if the horizontal ruler should be on the top or bottom; the vertical is always
+ * left justified
+ */
+ public Rule(Orientation theOrientation) {
+ orientation = theOrientation;
+ int dim = (int) PrintUnit.INCHES.toPoints(2) + 32;
+ setSize(dim, dim);
+ }
+
+ /**
+ * Render the component onto a graphics context.
+ *
+ * @param g the opaque graphics context
+ */
+ public void paintComponent(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+
+ double div = PrintUnit.INCHES.toPoints(1) / 8; //1/8 inch increment
+ final int width = (int) PrintUnit.INCHES.toPoints(2);
+ int x = 20;
+ int y = x + 20;
+ boolean inchOutSide = true;
+
+ g2.translate(getOffsetX(), getOffsetY());
+
+ if (orientation == Orientation.TOP) {
+ Font f = g.getFont();
+ g.setFont(f.deriveFont(f.getSize() - 2f));
+ g.drawString("in cm", x - MAJOR_TICK_LENGTH, y + width + 20);
+ g.drawString("in", x + width + 4, y + 4);
+ g.drawString("cm", x + width + 4, y + 18);
+ y += 6;
+
+ drawVerticalRule(g2, true, inchOutSide, x, y, width, 0, div * 2, div * 4, div * 8);
+ drawHorizontalRule(g2, true, !inchOutSide, x, y, width, 0, div * 2, div * 4, div * 8);
+ div = PrintUnit.MILLIMETERS.toPoints(1); //mm increment
+ drawVerticalRule(g2, true, !inchOutSide, x, y, width, 0, 0, div * 5, div * 10);
+ drawHorizontalRule(g2, true, inchOutSide, x, y, width, 0, 0, div * 5, div * 10);
+ }
+ else {
+ Font f = g.getFont();
+ g.setFont(f.deriveFont(f.getSize() - 2f));
+ g.drawString("in cm", x - MAJOR_TICK_LENGTH, y);
+ g.drawString("cm", x + width + 6, y + width + 4);
+ g.drawString("in", x + width + 6, y + width + 18);
+ y += 6;
+
+ //Draw Inches first, with 1/2", 1/4", and 1/8" tick marks.
+ drawVerticalRule(g2, false, inchOutSide, x, y, width, 0, div * 2, div * 4, div * 8);
+ drawHorizontalRule(g2, true, inchOutSide, x, y + width, width, 0, div * 2, div * 4, div * 8);
+ div = PrintUnit.MILLIMETERS.toPoints(1); //mm increment
+ //Draw cm (10mm) and 1/2 cm (5mm) marks
+ drawVerticalRule(g2, false, !inchOutSide, x, y, width, 0, 0, div * 5, div * 10);
+ drawHorizontalRule(g2, true, !inchOutSide, x, y + width, width, 0, 0, div * 5, div * 10);
+ }
+ }
+
+ /**
+ * Draw a horizontal ruler.
+ *
+ * @param g the graphics context
+ * @param vertexAtLeft true if the horizontal/vertical vertex is oriented to the top
+ * @param drawTicksDown true if the ruler should draw interval tick marks to the underside of the solid ruler line
+ * @param x starting x position of the ruler
+ * @param y starting y position of the rule
+ * @param length the number of points in length to extend the vertical ruler
+ * @param tinyEveryX the number of points for each tiny division tick line; if zero or negative tiny will not be
+ * drawn
+ * @param minorEveryX the number of points for each minor division tick line; if zero or negative minor will not
+ * be drawn
+ * @param midMajorEveryX the number of points for each mid-major division tick line
+ * @param majorEveryX the number of points for each major division tick line (this is typically the inch or cm
+ * distance in points).
+ */
+ private void drawHorizontalRule(Graphics2D g,
+ boolean vertexAtLeft,
+ boolean drawTicksDown,
+ int x, int y, int length,
+ double tinyEveryX,
+ double minorEveryX,
+ double midMajorEveryX,
+ double majorEveryX) {
+
+ //Draw solid horizontal line
+ g.setColor(Color.black);
+ g.drawLine(x, y, x + length, y);
+
+ int tiniest = drawTicksDown ? TINIEST_TICK_LENGTH : -1 * TINIEST_TICK_LENGTH;
+ int minor = drawTicksDown ? MINOR_TICK_LENGTH : -1 * MINOR_TICK_LENGTH;
+ int mid = drawTicksDown ? MID_MAJOR_TICK_LENGTH : -1 * MID_MAJOR_TICK_LENGTH;
+ int major = drawTicksDown ? MAJOR_TICK_LENGTH : -1 * MAJOR_TICK_LENGTH;
+
+ //Draw vertical rule ticks for the horizontal ruler
+ //Draw minor ticks
+ int initial = x;
+ int end = initial + length;
+ double increment = tinyEveryX;
+ boolean lessThanEqual = true;
+ if (!vertexAtLeft) {
+ initial = x + length;
+ end = x;
+ lessThanEqual = false;
+ }
+
+ if (tinyEveryX > 0) {
+ if (!vertexAtLeft) {
+ increment = -1 * increment;
+ }
+ for (double xtick = initial; lessThanEqual ? (xtick <= end) : (xtick >= end); xtick += increment) {
+ g.drawLine((int) xtick, y, (int) xtick, y + tiniest);
+ }
+ }
+ //Draw minor ticks
+ if (minorEveryX > 0) {
+ if (!vertexAtLeft) {
+ increment = -1 * minorEveryX;
+ }
+ else {
+ increment = minorEveryX;
+ }
+ for (double xtick = initial; lessThanEqual ? (xtick <= end) : (xtick >= end); xtick += increment) {
+ g.drawLine((int) xtick, y, (int) xtick, y + minor);
+ }
+ }
+
+ //Draw mid-major ticks
+ if (midMajorEveryX > 0) {
+ if (!vertexAtLeft) {
+ increment = -1 * midMajorEveryX;
+ }
+ else {
+ increment = midMajorEveryX;
+ }
+ for (double xtick = initial; lessThanEqual ? (xtick <= end) : (xtick >= end); xtick += increment) {
+ g.drawLine((int) xtick, y, (int) xtick, y + mid);
+ }
+ }
+ if (!vertexAtLeft) {
+ increment = -1 * majorEveryX;
+ }
+ else {
+ increment = majorEveryX;
+ }
+ //Draw major ticks
+ for (double xtick = initial; lessThanEqual ? (xtick <= end) : (xtick >= end); xtick += increment) {
+ g.drawLine((int) xtick, y, (int) xtick, y + major);
+ }
+
+ }
+
+ /**
+ * Draw a vertical ruler.
+ *
+ * @param g the graphics context
+ * @param vertexAtTop true if the horizontal/vertical vertex is oriented to the top
+ * @param drawTicksRight true if the ruler should draw interval tick marks to the right side of the solid ruler
+ * line
+ * @param x starting x position of the ruler
+ * @param y starting y position of the rule
+ * @param length the number of points in length to extend the vertical ruler
+ * @param tinyEveryY the number of points for each tiny division tick line; if zero or negative tiny will not be
+ * drawn
+ * @param minorEveryY the number of points for each minor division tick line; if zero or negative minor will not
+ * be drawn
+ * @param midMajorEveryY the number of points for each mid-major division tick line
+ * @param majorEveryY the number of points for each major division tick line (this is typically the inch or cm
+ * distance in points).
+ */
+ private void drawVerticalRule(Graphics2D g,
+ boolean vertexAtTop,
+ boolean drawTicksRight, int x, int y, int length,
+ double tinyEveryY,
+ double minorEveryY,
+ double midMajorEveryY,
+ double majorEveryY) {
+
+ int tiniest = drawTicksRight ? TINIEST_TICK_LENGTH : -1 * TINIEST_TICK_LENGTH;
+ int minor = drawTicksRight ? MINOR_TICK_LENGTH : -1 * MINOR_TICK_LENGTH;
+ int mid = drawTicksRight ? MID_MAJOR_TICK_LENGTH : -1 * MID_MAJOR_TICK_LENGTH;
+ int major = drawTicksRight ? MAJOR_TICK_LENGTH : -1 * MAJOR_TICK_LENGTH;
+
+ //Draw solid vertical line
+ g.setColor(Color.black);
+ g.drawLine(x, y, x, y + length);
+
+ //Draw horizontal rule ticks for the vertical ruler
+ //Draw tiny ticks
+ int initial = y;
+ int end = initial + length;
+ double increment = tinyEveryY;
+ boolean lessThanEqual = true;
+ if (!vertexAtTop) {
+ initial = y + length;
+ end = y;
+ lessThanEqual = false;
+ }
+
+ if (tinyEveryY > 0) {
+ if (!vertexAtTop) {
+ increment = -1 * increment;
+ }
+ for (double tick = initial; lessThanEqual ? (tick <= end) : (tick >= end); tick += increment) {
+ g.drawLine(x, (int) tick, x - tiniest, (int) tick);
+ }
+ }
+
+ //Draw minor ticks
+ if (minorEveryY > 0) {
+ if (!vertexAtTop) {
+ increment = -1 * minorEveryY;
+ }
+ else {
+ increment = minorEveryY;
+ }
+ for (double tick = initial; lessThanEqual ? (tick <= end) : (tick >= end); tick += increment) {
+ g.drawLine(x, (int) tick, x - minor, (int) tick);
+ }
+ }
+
+ //Draw mid-major ticks
+ if (!vertexAtTop) {
+ increment = -1 * midMajorEveryY;
+ }
+ else {
+ increment = midMajorEveryY;
+ }
+ for (double tick = initial; lessThanEqual ? (tick <= end) : (tick >= end); tick += increment) {
+ g.drawLine(x, (int) tick, x - mid, (int) tick);
+ }
+
+ //Draw major ticks
+ if (!vertexAtTop) {
+ increment = -1 * majorEveryY;
+ }
+ else {
+ increment = majorEveryY;
+ }
+ for (double tick = initial; lessThanEqual ? (tick <= end) : (tick >= end); tick += increment) {
+ g.drawLine(x, (int) tick, x - major, (int) tick);
+ }
+
+ }
+}
\ No newline at end of file