From c44e9adf2b89b2f0dfd56b24b277593cb736d732 Mon Sep 17 00:00:00 2001 From: rodinia814 Date: Mon, 28 May 2012 04:28:26 +0000 Subject: [PATCH] Added ruler graphic to printed output; improvements to layout and margins in printing; bug fix in PrintUnit; other minor printing related cleanup. git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@731 180e2498-e6e9-4542-8430-84ac67f01cd8 --- .../gui/print/AbstractPrintable.java | 31 +- .../openrocket/gui/print/PrintController.java | 26 +- .../sf/openrocket/gui/print/PrintUnit.java | 14 +- .../gui/print/PrintableCenteringRing.java | 18 +- .../gui/print/PrintableComponent.java | 41 ++- .../openrocket/gui/print/PrintableFinSet.java | 58 +--- .../gui/print/PrintableNoseCone.java | 9 +- .../gui/print/PrintableTransition.java | 11 +- .../openrocket/gui/print/components/Rule.java | 273 ++++++++++++++++++ .../print/visitor/CenteringRingStrategy.java | 4 + .../print/visitor/FinSetPrintStrategy.java | 8 +- .../print/visitor/PageFitPrintStrategy.java | 32 +- .../gui/print/visitor/TransitionStrategy.java | 52 ++-- .../openrocket/gui/print/PrintUnitTest.java | 49 ++++ 14 files changed, 490 insertions(+), 136 deletions(-) create mode 100644 core/src/net/sf/openrocket/gui/print/components/Rule.java create mode 100644 core/test/net/sf/openrocket/gui/print/PrintUnitTest.java diff --git a/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java b/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java index 739a7d91..585e4975 100644 --- a/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java +++ b/core/src/net/sf/openrocket/gui/print/AbstractPrintable.java @@ -1,6 +1,11 @@ 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 extends PrintableComponent { @@ -14,16 +19,6 @@ public abstract class AbstractPrintable 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. * @@ -35,11 +30,9 @@ public abstract class AbstractPrintable extends PrintableComponent { } /** - * Compute the basic values of each arc of the transition/shroud. This is adapted from - * The Properties of - * Model Rocket Body Tube Transitions, by J.R. Brohm + * Initialize the printable. * - * @param component the transition component + * @param component the component */ protected abstract void init(T component); @@ -51,14 +44,14 @@ public abstract class AbstractPrintable extends PrintableComponent { 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 diff --git a/core/src/net/sf/openrocket/gui/print/PrintController.java b/core/src/net/sf/openrocket/gui/print/PrintController.java index 4088de70..2e62df2a 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintController.java +++ b/core/src/net/sf/openrocket/gui/print/PrintController.java @@ -12,6 +12,7 @@ import com.itextpdf.text.pdf.PdfBoolean; 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; @@ -60,6 +61,8 @@ public class PrintController { // 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(); @@ -71,42 +74,55 @@ public class PrintController { 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(); diff --git a/core/src/net/sf/openrocket/gui/print/PrintUnit.java b/core/src/net/sf/openrocket/gui/print/PrintUnit.java index 310fba32..734e433b 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintUnit.java +++ b/core/src/net/sf/openrocket/gui/print/PrintUnit.java @@ -12,7 +12,7 @@ public enum PrintUnit { 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 { @@ -20,7 +20,7 @@ public enum PrintUnit { 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 { @@ -28,7 +28,7 @@ public enum PrintUnit { 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 { @@ -36,7 +36,7 @@ public enum PrintUnit { 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 { @@ -44,7 +44,7 @@ public enum PrintUnit { 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 { @@ -52,7 +52,7 @@ public enum PrintUnit { 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); } }; @@ -160,7 +160,7 @@ public enum PrintUnit { * overflow, or Long.MAX_VALUE if it would positively overflow. * @see #convert */ - public long toPoints(double length) { + public double toPoints(double length) { throw new AbstractMethodError(); } diff --git a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java index 08d3f12c..ed0a66d2 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java @@ -18,16 +18,6 @@ public class PrintableCenteringRing extends AbstractPrintable { */ 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. */ @@ -51,8 +41,8 @@ public class PrintableCenteringRing extends AbstractPrintable { 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)); } /** @@ -65,8 +55,8 @@ public class PrintableCenteringRing extends AbstractPrintable { 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); diff --git a/core/src/net/sf/openrocket/gui/print/PrintableComponent.java b/core/src/net/sf/openrocket/gui/print/PrintableComponent.java index 10e5c803..de345fb9 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableComponent.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableComponent.java @@ -3,23 +3,22 @@ */ 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 */ -public class PrintableComponent extends JPanel implements Printable { +public class PrintableComponent extends JPanel implements Printable, Comparable { /** - * The printing offsets + * The printing offsets. */ private int offsetX = 0; private int offsetY = 0; @@ -68,7 +67,7 @@ public class PrintableComponent extends JPanel implements Printable { } /** - * 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. */ @@ -78,7 +77,7 @@ public class PrintableComponent extends JPanel implements Printable { } /** - * 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() { @@ -86,10 +85,38 @@ public class PrintableComponent extends JPanel implements Printable { } /** - * 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(); + } } diff --git a/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java b/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java index bed1fdf9..8e5389b2 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableFinSet.java @@ -6,13 +6,12 @@ package net.sf.openrocket.gui.print; 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 @@ -26,14 +25,6 @@ public class PrintableFinSet extends PrintableComponent { */ 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. */ @@ -58,9 +49,7 @@ public class PrintableFinSet extends PrintableComponent { * @param points an array of points. */ public PrintableFinSet (Coordinate[] points) { - //super(false); init(points); - //setBackground(Color.white); } /** @@ -77,8 +66,8 @@ public class PrintableFinSet extends PrintableComponent { 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); @@ -90,24 +79,6 @@ public class PrintableFinSet extends PrintableComponent { 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. @@ -115,17 +86,17 @@ public class PrintableFinSet extends PrintableComponent { * @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; } @@ -144,6 +115,9 @@ public class PrintableFinSet extends PrintableComponent { 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); @@ -152,7 +126,7 @@ public class PrintableFinSet extends PrintableComponent { 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()); diff --git a/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java b/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java index 17f07230..2cfdfa68 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableNoseCone.java @@ -32,12 +32,13 @@ public class PrintableNoseCone extends AbstractPrintable { 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 */ @@ -47,7 +48,7 @@ public class PrintableNoseCone extends AbstractPrintable { 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); diff --git a/core/src/net/sf/openrocket/gui/print/PrintableTransition.java b/core/src/net/sf/openrocket/gui/print/PrintableTransition.java index 4a2d6d81..41a9958a 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableTransition.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableTransition.java @@ -72,6 +72,13 @@ public class PrintableTransition extends AbstractPrintable { super(false, transition); } + /** + * Compute the basic values of each arc of the transition/shroud. This is adapted from + * The Properties of + * Model Rocket Body Tube Transitions, by J.R. Brohm + * + * @param component the component + */ @Override protected void init(Transition component) { @@ -93,9 +100,9 @@ public class PrintableTransition extends AbstractPrintable { 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(); diff --git a/core/src/net/sf/openrocket/gui/print/components/Rule.java b/core/src/net/sf/openrocket/gui/print/components/Rule.java new file mode 100644 index 00000000..05c890f1 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/print/components/Rule.java @@ -0,0 +1,273 @@ +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 diff --git a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java index 5241c228..3cf17cae 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java @@ -6,6 +6,7 @@ import com.itextpdf.text.Rectangle; 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; @@ -104,9 +105,12 @@ public class CenteringRingStrategy { 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) { diff --git a/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java index eb58ba46..38704908 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java @@ -6,7 +6,6 @@ package net.sf.openrocket.gui.print.visitor; 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; @@ -16,11 +15,8 @@ import net.sf.openrocket.rocketcomponent.FinSet; 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; /** @@ -52,7 +48,7 @@ public class FinSetPrintStrategy { * Strategy for fitting multiple components onto a page. */ protected PageFitPrintStrategy pageFitPrint; - + /** * Constructor. * @@ -110,6 +106,8 @@ public class FinSetPrintStrategy { 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); diff --git a/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java index 20ef3c19..10ca7351 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/PageFitPrintStrategy.java @@ -12,14 +12,15 @@ import net.sf.openrocket.logging.LogHelper; 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 */ public class PageFitPrintStrategy { @@ -45,20 +46,19 @@ public class PageFitPrintStrategy { protected Set stages; protected ArrayList 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(); } - + /** * Add a component we want to print. * @@ -87,16 +87,18 @@ public class PageFitPrintStrategy { 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 entry = componentToPrint.listIterator(); anyAddedToRow = false; @@ -104,23 +106,25 @@ public class PageFitPrintStrategy { 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. * diff --git a/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java index 4752b3a7..b6f1ee04 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java @@ -6,6 +6,7 @@ import com.itextpdf.text.Rectangle; 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; @@ -46,7 +47,7 @@ public class TransitionStrategy { /** * Strategy for fitting multiple components onto a page. */ - protected PageFitPrintStrategy pageFitPrint; + protected PageFitPrintStrategy pageFitPrint; /** * Constructor. @@ -67,10 +68,12 @@ public class TransitionStrategy { * * @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 rc = root.getChildren(); - goDeep(rc, noseCones); + return goDeep(rc, noseCones); } @@ -79,48 +82,62 @@ public class TransitionStrategy { * * @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 theRc, boolean noseCones) { + protected boolean goDeep(final List 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; } /** @@ -129,6 +146,7 @@ public class TransitionStrategy { * @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) { diff --git a/core/test/net/sf/openrocket/gui/print/PrintUnitTest.java b/core/test/net/sf/openrocket/gui/print/PrintUnitTest.java new file mode 100644 index 00000000..f92d18bc --- /dev/null +++ b/core/test/net/sf/openrocket/gui/print/PrintUnitTest.java @@ -0,0 +1,49 @@ +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); + } + + +} -- 2.30.2