package net.sf.openrocket.gui.print;
-import java.awt.*;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
public abstract class AbstractPrintable<T> extends PrintableComponent {
*/
public final static BasicStroke thickStroke = new BasicStroke(4.0f);
- /**
- * The X margin.
- */
- protected int marginX = (int) PrintUnit.INCHES.toPoints(0.25f);
-
- /**
- * The Y margin.
- */
- protected int marginY = (int) PrintUnit.INCHES.toPoints(0.25f);
-
/**
* Constructor. Initialize this printable with the component to be printed.
*
}
/**
- * Compute the basic values of each arc of the transition/shroud. This is adapted from
- * <a href="http://www.rocketshoppe.com/info/Transitions.pdf">The Properties of
- * Model Rocket Body Tube Transitions, by J.R. Brohm</a>
+ * Initialize the printable.
*
- * @param component the transition component
+ * @param component the component
*/
protected abstract void init(T component);
protected abstract void draw(Graphics2D g2);
/**
- * Returns a generated image of the transition. May then be used wherever AWT images can be used, or converted to
+ * Returns a generated image of the component. 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 transition
+ * @return an awt image of the printable component
*/
public Image createImage() {
- int width = getWidth() + marginX;
- int height = getHeight() + marginY;
+ int width = getWidth() + getOffsetX();
+ int height = getHeight() + getOffsetY();
// Create a buffered image in which to draw
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// Create a graphics contents on the buffered image
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.print.components.Rule;
import net.sf.openrocket.gui.print.visitor.CenteringRingStrategy;
import net.sf.openrocket.gui.print.visitor.FinMarkingGuideStrategy;
import net.sf.openrocket.gui.print.visitor.FinSetPrintStrategy;
// Used to combine multiple components onto fewer sheets of paper
PageFitPrintStrategy pageFitPrint = new PageFitPrintStrategy(idoc, writer);
+ boolean addRule = false;
+
while (toBePrinted.hasNext()) {
PrintableContext printableContext = toBePrinted.next();
dp.writeToDocument(writer);
idoc.newPage();
break;
+
case FIN_TEMPLATE:
final FinSetPrintStrategy finWriter = new FinSetPrintStrategy(idoc, writer, stages, pageFitPrint);
finWriter.writeToDocument(doc.getRocket());
+ addRule = true;
break;
+
case PARTS_DETAIL:
final PartsDetailVisitorStrategy detailVisitor = new PartsDetailVisitorStrategy(idoc, writer, stages);
detailVisitor.writeToDocument(doc.getRocket());
detailVisitor.close();
idoc.newPage();
break;
+
case TRANSITION_TEMPLATE:
final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint);
- tranWriter.writeToDocument(doc.getRocket(), false);
- idoc.newPage();
+ if (tranWriter.writeToDocument(doc.getRocket(), false)) {
+ addRule = true;
+ }
break;
case NOSE_CONE_TEMPLATE:
final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint);
- coneWriter.writeToDocument(doc.getRocket(), true);
- idoc.newPage();
+ if (coneWriter.writeToDocument(doc.getRocket(), true)) {
+ addRule = true;
+ }
break;
case CENTERING_RING_TEMPLATE:
final CenteringRingStrategy crWriter = new CenteringRingStrategy(idoc, writer, stages,
pageFitPrint);
crWriter.writeToDocument(doc.getRocket());
- idoc.newPage();
+ addRule = true;
break;
case FIN_MARKING_GUIDE:
final FinMarkingGuideStrategy fmg = new FinMarkingGuideStrategy(idoc, writer);
fmg.writeToDocument(doc.getRocket());
idoc.newPage();
+ addRule = true;
break;
}
}
+
+ if (addRule) {
+ //Add a ruler to the output.
+ pageFitPrint.addComponent(new Rule(Rule.Orientation.BOTTOM));
+ }
+
// Write out parts that we are going to combine onto single sheets of paper
pageFitPrint.writeToDocument(doc.getRocket());
idoc.newPage();
public double toMillis(double d) { return d/FEET_PER_MM; }
public double toCentis(double d) { return d/(FEET_PER_MM*TEN); }
public double toMeters(double d) { return d/(FEET_PER_MM*TEN*TEN*TEN); }
- public long toPoints(double d) { return (long)(d * POINTS_PER_INCH * 12); }
+ public double toPoints(double d) { return (d * POINTS_PER_INCH * 12); }
public double convert(double d, PrintUnit u) { return u.toInches(d)/12; }
},
INCHES {
public double toMillis(double d) { return d/INCHES_PER_MM; }
public double toCentis(double d) { return d/(INCHES_PER_MM*TEN); }
public double toMeters(double d) { return d/(INCHES_PER_MM*TEN*TEN*TEN); }
- public long toPoints(double d) { return (long)(d * POINTS_PER_INCH); }
+ public double toPoints(double d) { return (d * POINTS_PER_INCH); }
public double convert(double d, PrintUnit u) { return u.toInches(d); }
},
MILLIMETERS {
public double toMillis(double d) { return d; }
public double toCentis(double d) { return d/TEN; }
public double toMeters(double d) { return d/(TEN*TEN*TEN); }
- public long toPoints(double d) { return INCHES.toPoints(toInches(d)); }
+ public double toPoints(double d) { return INCHES.toPoints(toInches(d)); }
public double convert(double d, PrintUnit u) { return u.toMillis(d); }
},
CENTIMETERS {
public double toMillis(double d) { return d * TEN; }
public double toCentis(double d) { return d; }
public double toMeters(double d) { return d/(TEN*TEN); }
- public long toPoints(double d) { return INCHES.toPoints(toInches(d)); }
+ public double toPoints(double d) { return INCHES.toPoints(toInches(d)); }
public double convert(double d, PrintUnit u) { return u.toCentis(d); }
},
METERS {
public double toMillis(double d) { return d * TEN * TEN * TEN; }
public double toCentis(double d) { return d * TEN * TEN; }
public double toMeters(double d) { return d; }
- public long toPoints(double d) { return INCHES.toPoints(toInches(d)); }
+ public double toPoints(double d) { return INCHES.toPoints(toInches(d)); }
public double convert(double d, PrintUnit u) { return u.toMeters(d); }
},
POINTS {
public double toMillis(double d) { return d/(POINTS_PER_INCH * INCHES_PER_MM); }
public double toCentis(double d) { return toMillis(d)/TEN; }
public double toMeters(double d) { return toMillis(d)/(TEN*TEN*TEN); }
- public long toPoints(double d) { return (long)d; }
+ public double toPoints(double d) { return d; }
public double convert(double d, PrintUnit u) { return u.toPoints(d); }
};
* overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
* @see #convert
*/
- public long toPoints(double length) {
+ public double toPoints(double length) {
throw new AbstractMethodError();
}
*/
private CenteringRing target;
- /**
- * The X margin.
- */
- protected int marginX = (int) PrintUnit.INCHES.toPoints(0.25f);
-
- /**
- * The Y margin.
- */
- protected int marginY = (int) PrintUnit.INCHES.toPoints(0.25f);
-
/**
* The line length of the cross hairs.
*/
target = component;
double radius = target.getOuterRadius();
- setSize((int) PrintUnit.METERS.toPoints(2 * radius) + marginX,
- (int) PrintUnit.METERS.toPoints(2 * radius) + marginY);
+ setSize((int) PrintUnit.METERS.toPoints(2 * radius),
+ (int) PrintUnit.METERS.toPoints(2 * radius));
}
/**
double radius = PrintUnit.METERS.toPoints(target.getOuterRadius());
Color original = g2.getBackground();
- double x = marginX;
- double y = marginY;
+ double x = 0;
+ double y = 0;
Shape outerCircle = new Ellipse2D.Double(x, y, radius * 2, radius * 2);
g2.setColor(Color.lightGray);
g2.fill(outerCircle);
*/
package net.sf.openrocket.gui.print;
+import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
-import javax.swing.JPanel;
-
/**
* Common interface for components we want to print. Used by PageFitPrintStrategy
*
* @author Jason Blood <dyster2000@gmail.com>
*/
-public class PrintableComponent extends JPanel implements Printable {
+public class PrintableComponent extends JPanel implements Printable, Comparable<PrintableComponent> {
/**
- * The printing offsets
+ * The printing offsets.
*/
private int offsetX = 0;
private int offsetY = 0;
}
/**
- * Set the offset this component will be printed to the page
+ * Set the offset this component will be printed to the page.
* @param x X offset to print at.
* @param y Y offset to print at.
*/
}
/**
- * Get the X offset this component will be printed to the page
+ * Get the X offset this component will be printed to the page.
* @return X offset to print at.
*/
public int getOffsetX() {
}
/**
- * Get the Y offset this component will be printed to the page
+ * Get the Y offset this component will be printed to the page.
* @return Y offset to print at.
*/
public int getOffsetY() {
return offsetY;
}
+
+
+ /**
+ * Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer
+ * as this object is less than, equal to, or greater than the specified object.
+ *
+ * Bin packing theory says that trying to fit the biggest items first may have a better outcome. So this is sorted
+ * in size descending order, with width taking precedence over height.
+ *
+ * @param other the object to be compared.
+ *
+ * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the
+ * specified object.
+ *
+ * @throws NullPointerException if the specified object is null
+ * @throws ClassCastException if the specified object's type prevents it from being compared to this object.
+ */
+ @Override
+ public int compareTo(final PrintableComponent other) {
+ int widthDiff = other.getWidth() - getWidth();
+ if (widthDiff > 0) {
+ return 1;
+ }
+ else if (widthDiff < 0) {
+ return -1;
+ }
+ return other.getHeight() - getHeight();
+ }
}
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.util.Coordinate;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
-import java.awt.print.PageFormat;
-import java.awt.print.Printable;
-import java.awt.print.PrinterException;
/**
* This class allows for a FinSet to be printable. It does so by decorating an existing finset (which will not be
*/
protected GeneralPath polygon = null;
- /**
- * The X margin.
- */
- private final int marginX = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
- /**
- * The Y margin.
- */
- private final int marginY = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
/**
* The minimum X coordinate.
*/
* @param points an array of points.
*/
public PrintableFinSet (Coordinate[] points) {
- //super(false);
init(points);
- //setBackground(Color.white);
}
/**
int maxY = 0;
for (Coordinate point : points) {
- final long x = PrintUnit.METERS.toPoints(point.x);
- final long y = PrintUnit.METERS.toPoints(point.y);
+ final long x = (long)PrintUnit.METERS.toPoints(point.x);
+ final long y = (long)PrintUnit.METERS.toPoints(point.y);
minX = (int) Math.min(x, minX);
minY = (int) Math.min(y, minY);
maxX = (int) Math.max(x, maxX);
setSize(maxX - minX, maxY - minY);
}
- /**
- * Get the X-axis margin value.
- *
- * @return margin, in points
- */
- protected double getMarginX () {
- return marginX;
- }
-
- /**
- * Get the Y-axis margin value.
- *
- * @return margin, in points
- */
- protected double getMarginY () {
- return marginY;
- }
-
/**
* Returns a generated image of the fin set. 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 set
*/
public Image createImage () {
- int width = getWidth() + marginX;
- int height = getHeight() + marginY;
- // Create a buffered image in which to draw
+ int width = getWidth();
+ int height = getHeight();
+ // Create a buffered image in which to draw
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- // Create a graphics contents on the buffered image
+ // Create a graphics contents on the buffered image
Graphics2D g2d = bufferedImage.createGraphics();
- // Draw graphics
+ // Draw graphics
g2d.setBackground(Color.white);
g2d.clearRect(0, 0, width, height);
paintComponent(g2d);
- // Graphics context no longer needed so dispose it
+ // Graphics context no longer needed so dispose it
g2d.dispose();
return bufferedImage;
}
int x = 0;
int y = 0;
+ int marginX = this.getOffsetX();
+ int marginY = this.getOffsetY();
+
// The minimum X/Y can be negative (primarily only Y due to fin tabs; rarely (never) X, but protect both anyway).
if (minX < marginX) {
x = marginX + Math.abs(minX);
y = marginY + Math.abs(minY);
}
// Reset the origin.
- g2d.translate(x + getOffsetX(), y + getOffsetY());
+ g2d.translate(x, y);
g2d.setPaint(TemplateProperties.getFillColor());
g2d.fill(polygon);
g2d.setPaint(TemplateProperties.getLineColor());
if (radius < target.getAftRadius()) {
radius = target.getAftRadius();
}
- setSize((int) PrintUnit.METERS.toPoints(2 * radius) + marginX,
- (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + marginY);
+ setSize((int) PrintUnit.METERS.toPoints(2 * radius) + 4,
+ (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + 4);
}
/**
- * Draw a nose cone.
+ * Draw a nose cone. Presumes that the graphics context has already had the x/y position translated based on
+ * where it should be drawn.
*
* @param g2 the graphics context
*/
if (shapes != null && shapes.length > 0) {
Rectangle r = shapes[0].getBounds();
- g2.translate(marginX + r.getHeight() / 2 + getOffsetX(), marginY + getOffsetY());
+ g2.translate(r.getHeight() / 2, 0);
g2.rotate(Math.PI / 2);
for (Shape shape : shapes) {
g2.draw(shape);
super(false, transition);
}
+ /**
+ * Compute the basic values of each arc of the transition/shroud. This is adapted from
+ * <a href="http://www.rocketshoppe.com/info/Transitions.pdf">The Properties of
+ * Model Rocket Body Tube Transitions, by J.R. Brohm</a>
+ *
+ * @param component the component
+ */
@Override
protected void init(Transition component) {
int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
- int x = marginX;
+ int x = 0;
int tabOffset = 35;
- int y = tabOffset + marginY;
+ int y = tabOffset;
Arc2D.Double outerArc = new Arc2D.Double();
Arc2D.Double innerArc = new Arc2D.Double();
--- /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.
+ 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
+ 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
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.gui.print.AbstractPrintable;
import net.sf.openrocket.gui.print.ITextHelper;
+import net.sf.openrocket.gui.print.PrintUnit;
import net.sf.openrocket.gui.print.PrintableCenteringRing;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.CenteringRing;
pageFitPrint.addComponent(pfs);
}
else {
+ int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f);
+ pfs.setPrintOffset(off, off);
BufferedImage image = (BufferedImage) pfs.createImage();
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
document, writer, image);
+ document.newPage();
}
}
catch (DocumentException e) {
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.gui.print.ITextHelper;
import net.sf.openrocket.gui.print.PrintUnit;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
-import java.awt.*;
import java.awt.image.BufferedImage;
-import java.util.ArrayList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Set;
/**
* Strategy for fitting multiple components onto a page.
*/
protected PageFitPrintStrategy pageFitPrint;
-
+
/**
* Constructor.
*
pageFitPrint.addComponent(pfs);
}
else {
+ int off = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
+ pfs.setPrintOffset(off, off);
BufferedImage image = (BufferedImage) pfs.createImage();
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
document, writer, image);
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.ListIterator;
import java.util.Set;
/**
* A strategy for drawing multiple rocket components onto as few pages as possible.
- *
+ *
* @author Jason Blood <dyster2000@gmail.com>
*/
public class PageFitPrintStrategy {
protected Set<Integer> stages;
protected ArrayList<PrintableComponent> componentToPrint;
-
+
/**
* Constructor.
*
* @param doc The iText document
* @param theWriter The direct iText writer
- * @param theStages The stages to be printed by this strategy
*/
public PageFitPrintStrategy(Document doc, PdfWriter theWriter) {
document = doc;
writer = theWriter;
componentToPrint = new ArrayList<PrintableComponent>();
}
-
+
/**
* Add a component we want to print.
*
int marginX = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
int marginY = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
PdfContentByte cb = writer.getDirectContent();
-
+
+ Collections.sort(componentToPrint);
+
while (componentToPrint.size() > 0) {
- int pageY = 0;
+ int pageY = marginY;
Boolean anyAddedToRow;
-
+
Graphics2D g2 = cb.createGraphics(pageSize.width, pageSize.height);
-
+
do {
// Fill the row
- int rowX = 0;
+ int rowX = marginX;
int rowY = pageY;
ListIterator<PrintableComponent> entry = componentToPrint.listIterator();
anyAddedToRow = false;
while (entry.hasNext()) {
PrintableComponent component = entry.next();
java.awt.Dimension dim = component.getSize();
- if ((rowX + dim.width < wPage) && (rowY + dim.height < hPage)) {
+ if ((rowX + dim.width + marginX < wPage) && (rowY + dim.height + marginY < hPage)) {
component.setPrintOffset(rowX, rowY);
rowX += dim.width + marginX;
- if (rowY + dim.height + marginY > pageY)
+ if (rowY + dim.height + marginY > pageY) {
pageY = rowY + dim.height + marginY;
+ }
entry.remove();
component.print(g2);
anyAddedToRow = true;
}
}
+ pageY += marginY;
} while (anyAddedToRow);
-
+
g2.dispose();
document.newPage();
}
}
-
+
/**
* Get the dimensions of the paper page.
*
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.gui.print.AbstractPrintable;
import net.sf.openrocket.gui.print.ITextHelper;
+import net.sf.openrocket.gui.print.PrintUnit;
import net.sf.openrocket.gui.print.PrintableNoseCone;
import net.sf.openrocket.gui.print.PrintableTransition;
import net.sf.openrocket.logging.LogHelper;
/**
* Strategy for fitting multiple components onto a page.
*/
- protected PageFitPrintStrategy pageFitPrint;
+ protected PageFitPrintStrategy pageFitPrint;
/**
* Constructor.
*
* @param root the root component; all children will be visited recursively
* @param noseCones nose cones are a special form of a transition; if true, then print nose cones
+ *
+ * @return true if a transition/nosecone was rendered
*/
- public void writeToDocument(final RocketComponent root, boolean noseCones) {
+ public boolean writeToDocument(final RocketComponent root, boolean noseCones) {
List<RocketComponent> rc = root.getChildren();
- goDeep(rc, noseCones);
+ return goDeep(rc, noseCones);
}
*
* @param theRc an array of rocket components; all children will be visited recursively
* @param noseCones nose cones are a special form of a transition; if true, then print nose cones
+ *
+ * @return true if a transition/nosecone was rendered
*/
- protected void goDeep(final List<RocketComponent> theRc, boolean noseCones) {
+ protected boolean goDeep(final List<RocketComponent> theRc, boolean noseCones) {
for (RocketComponent rocketComponent : theRc) {
if (rocketComponent instanceof NoseCone) {
if (noseCones) {
- render((Transition) rocketComponent);
+ return render((Transition) rocketComponent);
}
- } else if (rocketComponent instanceof Transition && !noseCones) {
- render((Transition) rocketComponent);
- } else if (rocketComponent.getChildCount() > 0) {
- goDeep(rocketComponent.getChildren(), noseCones);
+ }
+ else if (rocketComponent instanceof Transition && !noseCones) {
+ return render((Transition) rocketComponent);
+ }
+ else if (rocketComponent.getChildCount() > 0) {
+ return goDeep(rocketComponent.getChildren(), noseCones);
}
}
+ return false;
}
/**
* The core behavior of this visitor.
*
- * @param component the object to extract info about; a graphical image of the transition shape is drawn to the document
+ * @param component the object to extract info about; a graphical image of the transition shape is drawn to the
+ * document
+ *
+ * @return true, always
*/
- private void render(final Transition component) {
+ private boolean render(final Transition component) {
try {
AbstractPrintable pfs;
if (component instanceof NoseCone) {
- pfs = new PrintableNoseCone((NoseCone)component);
- } else {
+ pfs = new PrintableNoseCone((NoseCone) component);
+ }
+ else {
pfs = new PrintableTransition(component);
}
java.awt.Dimension size = pfs.getSize();
final Dimension pageSize = getPageSize();
if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
- pageFitPrint.addComponent(pfs);
- //printOnOnePage(pfs);
- } else {
+ pageFitPrint.addComponent(pfs);
+ }
+ else {
+ int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f);
+ pfs.setPrintOffset(off, off);
BufferedImage image = (BufferedImage) pfs.createImage();
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
document, writer, image);
+ document.newPage();
}
- } catch (DocumentException e) {
+ }
+ catch (DocumentException e) {
log.error("Could not render the transition.", e);
}
+ return true;
}
/**
* @param pageSize the page size
* @param wImage the width of the thing to be printed
* @param hImage the height of the thing to be printed
+ *
* @return true if the thing to be printed will fit on a single page
*/
private boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) {
--- /dev/null
+package net.sf.openrocket.gui.print;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+* PrintUnit Tester.
+*
+*/
+public class PrintUnitTest {
+
+ /**
+ *
+ * Method: toMillis(double length)
+ *
+ */
+ @Test
+ public void testToMillis() throws Exception {
+ Assert.assertEquals(25.400000, PrintUnit.INCHES.toMillis(1), 0.00001);
+ Assert.assertEquals(1, PrintUnit.MILLIMETERS.toInches(PrintUnit.INCHES.toMillis(1)), 0.000001);
+ }
+
+ /**
+ *
+ * Method: toCentis(double length)
+ *
+ */
+ @Test
+ public void testToCentis() throws Exception {
+ Assert.assertEquals(4, PrintUnit.CENTIMETERS.toMeters(PrintUnit.METERS.toCentis(4)), 0.000001);
+ Assert.assertEquals(4, PrintUnit.CENTIMETERS.toPoints(PrintUnit.POINTS.toCentis(4)), 0.000001);
+ }
+
+ /**
+ *
+ * Method: toPoints(double length)
+ *
+ */
+ @Test
+ public void testToPoints() throws Exception {
+ Assert.assertEquals(1, PrintUnit.POINTS.toInches(72), 0.00001);
+ Assert.assertEquals(25.4, PrintUnit.POINTS.toMillis(72), 0.00001);
+ Assert.assertEquals(1, PrintUnit.MILLIMETERS.toPoints(PrintUnit.POINTS.toMillis(1)), 0.000001);
+
+ Assert.assertEquals(28.3464567, PrintUnit.CENTIMETERS.toPoints(1), 0.000001d);
+ }
+
+
+}