1 package net.sf.openrocket.gui.print.visitor;
3 import com.itextpdf.text.Document;
4 import com.itextpdf.text.DocumentException;
5 import com.itextpdf.text.Rectangle;
6 import com.itextpdf.text.pdf.PdfWriter;
7 import net.sf.openrocket.gui.print.AbstractPrintable;
8 import net.sf.openrocket.gui.print.ITextHelper;
9 import net.sf.openrocket.gui.print.PrintUnit;
10 import net.sf.openrocket.gui.print.PrintableCenteringRing;
11 import net.sf.openrocket.logging.LogHelper;
12 import net.sf.openrocket.rocketcomponent.CenteringRing;
13 import net.sf.openrocket.rocketcomponent.InnerTube;
14 import net.sf.openrocket.rocketcomponent.RocketComponent;
15 import net.sf.openrocket.startup.Application;
16 import net.sf.openrocket.util.ArrayList;
18 import java.awt.image.BufferedImage;
19 import java.util.List;
23 * A strategy for printing a centering ring to iText.
25 public class CenteringRingStrategy {
30 private static final LogHelper log = Application.getLogger();
35 protected Document document;
38 * The direct iText writer.
40 protected PdfWriter writer;
43 * The stages selected.
45 protected Set<Integer> stages;
48 * Strategy for fitting multiple components onto a page.
50 protected PageFitPrintStrategy pageFitPrint;
55 * @param doc The iText document
56 * @param theWriter The direct iText writer
57 * @param theStagesToVisit The stages to be visited by this strategy
59 public CenteringRingStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit, PageFitPrintStrategy pageFit) {
62 stages = theStagesToVisit;
63 pageFitPrint = pageFit;
67 * Recurse through the given rocket component.
69 * @param root the root component; all children will be visited recursively
71 public void writeToDocument(final RocketComponent root) {
72 List<RocketComponent> rc = root.getChildren();
78 * Recurse through the given rocket component.
80 * @param theRc an array of rocket components; all children will be visited recursively
82 protected void goDeep(final List<RocketComponent> theRc) {
83 for (RocketComponent rocketComponent : theRc) {
84 if (rocketComponent instanceof CenteringRing) {
85 render((CenteringRing) rocketComponent);
87 else if (rocketComponent.getChildCount() > 0) {
88 goDeep(rocketComponent.getChildren());
94 * Find the inner tubes that are physically supported by the given centering ring. Note that this only looks for
95 * motor mount tubes that are siblings to the centering ring.
97 * @param rc the centering ring, for which all motor mount tubes that run through it are located.
99 * @return the list of tubes found
101 private List<InnerTube> findMotorMount(CenteringRing rc) {
102 RocketComponent parent = rc.getParent();
103 List<RocketComponent> siblings = parent.getChildren();
105 List<InnerTube> mounts = new ArrayList<InnerTube>();
106 for (RocketComponent rocketComponents : siblings) {
107 if (rocketComponents != rc) {
108 if (rocketComponents instanceof InnerTube) {
109 InnerTube it = (InnerTube) rocketComponents;
110 if (overlaps(rc, it)) {
121 * Determine if the centering ring physically overlaps with the inner tube.
123 * @param one the centering ring
124 * @param two the inner body tube
126 * @return true if the two physically intersect, from which we infer that the centering ring supports the tube
128 private boolean overlaps(CenteringRing one, InnerTube two) {
129 final double crTopPosition = one.asPositionValue(RocketComponent.Position.ABSOLUTE, one.getParent());
130 final double mmTopPosition = two.asPositionValue(RocketComponent.Position.ABSOLUTE, two.getParent());
131 final double crBottomPosition = one.getLength() + crTopPosition;
132 final double mmBottomPosition = two.getLength() + mmTopPosition;
134 if (crTopPosition >= mmTopPosition && crTopPosition <= mmBottomPosition) {
137 if (crBottomPosition >= mmTopPosition && crBottomPosition <= mmBottomPosition) {
144 * The core behavior of this visitor.
146 * @param component the object to extract info about; a graphical image of the centering ring shape is drawn to the
149 private void render(final CenteringRing component) {
151 AbstractPrintable pfs;
152 pfs = PrintableCenteringRing.create(component, findMotorMount(component));
154 java.awt.Dimension size = pfs.getSize();
155 final Dimension pageSize = getPageSize();
156 if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
157 pageFitPrint.addComponent(pfs);
160 int off = (int) (PrintUnit.POINTS_PER_INCH * 0.3f);
161 pfs.setPrintOffset(off, off);
162 BufferedImage image = (BufferedImage) pfs.createImage();
163 ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
164 document, writer, image);
168 catch (DocumentException e) {
169 log.error("Could not render the centering ring.", e);
174 * Determine if the image will fit on the given page.
176 * @param pageSize the page size
177 * @param wImage the width of the thing to be printed
178 * @param hImage the height of the thing to be printed
180 * @return true if the thing to be printed will fit on a single page
182 private boolean fitsOnOnePage(Dimension pageSize, double wImage, double hImage) {
183 double wPage = pageSize.getWidth();
184 double hPage = pageSize.getHeight();
186 int wRatio = (int) Math.ceil(wImage / wPage);
187 int hRatio = (int) Math.ceil(hImage / hPage);
189 return wRatio <= 1.0d && hRatio <= 1.0d;
193 * Get the dimensions of the paper page.
195 * @return an internal Dimension
197 protected Dimension getPageSize() {
198 return new Dimension(document.getPageSize().getWidth(),
199 document.getPageSize().getHeight());
203 * Convenience class to model a dimension.
205 public static class Dimension {
215 * Breadth, in points.
217 public float breadth = 0f;
225 public Dimension(float w, float h) {
235 * @param b breadth; optionally used to represent radius
237 public Dimension(float w, float h, float b) {
248 public float getWidth() {
257 public float getHeight() {
264 * @return the breadth
266 public float getBreadth() {