Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / gui / print / visitor / CenteringRingStrategy.java
diff --git a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java
new file mode 100644 (file)
index 0000000..1df7569
--- /dev/null
@@ -0,0 +1,270 @@
+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.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;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.ArrayList;
+
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A strategy for printing a centering ring to iText.
+ */
+public class CenteringRingStrategy {
+
+    /**
+     * The logger.
+     */
+    private static final LogHelper log = Application.getLogger();
+
+    /**
+     * The iText document.
+     */
+    protected Document document;
+
+    /**
+     * The direct iText writer.
+     */
+    protected PdfWriter writer;
+
+    /**
+     * The stages selected.
+     */
+    protected Set<Integer> stages;
+
+    /**
+     * Strategy for fitting multiple components onto a page.
+     */
+    protected PageFitPrintStrategy pageFitPrint;
+
+    /**
+     * Constructor.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     * @param theStagesToVisit The stages to be visited by this strategy
+     */
+    public CenteringRingStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit, PageFitPrintStrategy pageFit) {
+        document = doc;
+        writer = theWriter;
+        stages = theStagesToVisit;
+        pageFitPrint = pageFit;
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param root the root component; all children will be visited recursively
+     */
+    public void writeToDocument(final RocketComponent root) {
+        List<RocketComponent> rc = root.getChildren();
+        goDeep(rc);
+    }
+
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param theRc an array of rocket components; all children will be visited recursively
+     */
+    protected void goDeep(final List<RocketComponent> theRc) {
+        for (RocketComponent rocketComponent : theRc) {
+            if (rocketComponent instanceof CenteringRing) {
+                render((CenteringRing) rocketComponent);
+            }
+            else if (rocketComponent.getChildCount() > 0) {
+                goDeep(rocketComponent.getChildren());
+            }
+        }
+    }
+
+    /**
+     * Find the inner tubes that are physically supported by the given centering ring.  Note that this only looks for
+     * motor mount tubes that are siblings to the centering ring.
+     *
+     * @param rc the centering ring, for which all motor mount tubes that run through it are located.
+     *
+     * @return the list of tubes found
+     */
+    private List<InnerTube> findMotorMount(CenteringRing rc) {
+        RocketComponent parent = rc.getParent();
+        List<RocketComponent> siblings = parent.getChildren();
+
+        List<InnerTube> mounts = new ArrayList<InnerTube>();
+        for (RocketComponent rocketComponents : siblings) {
+            if (rocketComponents != rc) {
+                if (rocketComponents instanceof InnerTube) {
+                    InnerTube it = (InnerTube) rocketComponents;
+                    if (overlaps(rc, it)) {
+                        mounts.add(it);
+                    }
+                }
+            }
+        }
+
+        return mounts;
+    }
+
+    /**
+     * Determine if the centering ring physically overlaps with the inner tube.
+     *
+     * @param one the centering ring
+     * @param two the inner body tube
+     *
+     * @return true if the two physically intersect, from which we infer that the centering ring supports the tube
+     */
+    private boolean overlaps(CenteringRing one, InnerTube two) {
+        final double crTopPosition = one.asPositionValue(RocketComponent.Position.ABSOLUTE, one.getParent());
+        final double mmTopPosition = two.asPositionValue(RocketComponent.Position.ABSOLUTE, two.getParent());
+        final double crBottomPosition = one.getLength() + crTopPosition;
+        final double mmBottomPosition = two.getLength() + mmTopPosition;
+
+        if (crTopPosition >= mmTopPosition && crTopPosition <= mmBottomPosition) {
+            return true;
+        }
+        if (crBottomPosition >= mmTopPosition && crBottomPosition <= mmBottomPosition) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * The core behavior of this visitor.
+     *
+     * @param component the object to extract info about; a graphical image of the centering ring shape is drawn to the
+     *                  document
+     */
+    private void render(final CenteringRing component) {
+        try {
+            AbstractPrintable pfs;
+            pfs = PrintableCenteringRing.create(component, findMotorMount(component));
+
+            java.awt.Dimension size = pfs.getSize();
+            final Dimension pageSize = getPageSize();
+            if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
+                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) {
+            log.error("Could not render the centering ring.", e);
+        }
+    }
+
+    /**
+     * Determine if the image will fit on the given page.
+     *
+     * @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) {
+        double wPage = pageSize.getWidth();
+        double hPage = pageSize.getHeight();
+
+        int wRatio = (int) Math.ceil(wImage / wPage);
+        int hRatio = (int) Math.ceil(hImage / hPage);
+
+        return wRatio <= 1.0d && hRatio <= 1.0d;
+    }
+
+    /**
+     * Get the dimensions of the paper page.
+     *
+     * @return an internal Dimension
+     */
+    protected Dimension getPageSize() {
+        return new Dimension(document.getPageSize().getWidth(),
+                document.getPageSize().getHeight());
+    }
+
+    /**
+     * Convenience class to model a dimension.
+     */
+    public static class Dimension {
+        /**
+         * Width, in points.
+         */
+        public float width;
+        /**
+         * Height, in points.
+         */
+        public float height;
+        /**
+         * Breadth, in points.
+         */
+        public float breadth = 0f;
+
+        /**
+         * Constructor.
+         *
+         * @param w width
+         * @param h height
+         */
+        public Dimension(float w, float h) {
+            width = w;
+            height = h;
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param w width
+         * @param h height
+         * @param b breadth; optionally used to represent radius
+         */
+        public Dimension(float w, float h, float b) {
+            width = w;
+            height = h;
+            breadth = b;
+        }
+
+        /**
+         * Get the width.
+         *
+         * @return the width
+         */
+        public float getWidth() {
+            return width;
+        }
+
+        /**
+         * Get the height.
+         *
+         * @return the height
+         */
+        public float getHeight() {
+            return height;
+        }
+
+        /**
+         * Get the breadth.
+         *
+         * @return the breadth
+         */
+        public float getBreadth() {
+            return breadth;
+        }
+    }
+}