diff --git a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java index ed0a66d22..502e045ab 100644 --- a/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java +++ b/core/src/net/sf/openrocket/gui/print/PrintableCenteringRing.java @@ -1,16 +1,24 @@ package net.sf.openrocket.gui.print; +import net.sf.openrocket.gui.print.visitor.CenteringRingStrategy; import net.sf.openrocket.rocketcomponent.CenteringRing; +import net.sf.openrocket.rocketcomponent.ClusterConfiguration; +import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.Coordinate; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Ellipse2D; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** - * This class creates a renderable centering ring. It depends only on AWT/Swing and can be called from other - * actors (like iText handlers) to render the centering ring on different graphics contexts. + * This class creates a renderable centering ring. It depends only on AWT/Swing and can be called from other actors + * (like iText handlers) to render the centering ring on different graphics contexts. */ public class PrintableCenteringRing extends AbstractPrintable { /** @@ -24,12 +32,79 @@ public class PrintableCenteringRing extends AbstractPrintable { private final int lineLength = 10; /** - * Construct a printable nose cone. - * - * @param theRing the component to print + * A set of the inner 'holes'. At least one, but will have many if clustered. */ - public PrintableCenteringRing(CenteringRing theRing) { + private Set innerCenterPoints = new HashSet(); + + /** + * Construct a simple, non-clustered, printable centering ring. + * + * @param theRing the component to print + * @param theMotorMount the motor mount if clustered, else null + */ + private PrintableCenteringRing(CenteringRing theRing, InnerTube theMotorMount) { super(false, theRing); + if (theMotorMount == null || theMotorMount.getClusterConfiguration().equals(ClusterConfiguration.SINGLE)) { + //Single motor. + innerCenterPoints.add(new CenteringRingStrategy.Dimension((float) PrintUnit.METERS.toPoints(target.getOuterRadius()), + (float) PrintUnit.METERS.toPoints(target.getOuterRadius()))); + } + else { + List coords = theMotorMount.getClusterPoints(); + populateCenterPoints(coords); + } + } + + /** + * Constructor for a clustered centering ring. + * + * @param theRing the centering ring component + * @param theMotorMounts a list of the motor mount tubes that are physically supported by the centering ring + */ + private PrintableCenteringRing(CenteringRing theRing, List theMotorMounts) { + super(false, theRing); + List points = new ArrayList(); + //Transform the radial positions of the tubes. + for (InnerTube it : theMotorMounts) { + double y = it.getRadialShiftY(); + double z = it.getRadialShiftZ(); + Coordinate coordinate = new Coordinate(0, y, z); + points.add(coordinate); + } + populateCenterPoints(points); + } + + /** + * Factory method to create a printable centering ring. + * + * @param theRing the component to print + * @param theMotorMounts the motor mount if clustered, else null + */ + public static PrintableCenteringRing create(CenteringRing theRing, List theMotorMounts) { + if (theMotorMounts == null) { + return new PrintableCenteringRing(theRing, (InnerTube) null); + } + else if (theMotorMounts.size() <= 1) { + return new PrintableCenteringRing(theRing, theMotorMounts.isEmpty() ? null : theMotorMounts.get(0)); + } + else { + return new PrintableCenteringRing(theRing, theMotorMounts); + } + } + + /** + * Initialize the set of center points for each motor mount tube, based on the tube coordinates. + * + * @param theCoords the list of tube coordinates; each coordinate is in the OR units (meters) and must be + * transformed to the printing (points) coordinate system + */ + private void populateCenterPoints(final List theCoords) { + float radius = (float) PrintUnit.METERS.toPoints(target.getOuterRadius()); + for (Coordinate coordinate : theCoords) { + innerCenterPoints.add(new CenteringRingStrategy.Dimension((float) PrintUnit.METERS.toPoints + (coordinate.y) + radius, + (float) PrintUnit.METERS.toPoints(coordinate.z) + radius)); + } } /** @@ -55,33 +130,43 @@ public class PrintableCenteringRing extends AbstractPrintable { double radius = PrintUnit.METERS.toPoints(target.getOuterRadius()); Color original = g2.getBackground(); - double x = 0; - double y = 0; - Shape outerCircle = new Ellipse2D.Double(x, y, radius * 2, radius * 2); + Shape outerCircle = new Ellipse2D.Double(0, 0, radius * 2, radius * 2); g2.setColor(Color.lightGray); g2.fill(outerCircle); g2.setColor(Color.black); g2.draw(outerCircle); - x += radius; - y += radius; - double innerRadius = PrintUnit.METERS.toPoints(target.getInnerRadius()); - Shape innerCircle = new Ellipse2D.Double(x - innerRadius, y - innerRadius, innerRadius * 2, innerRadius * 2); + for (CenteringRingStrategy.Dimension next : innerCenterPoints) { + drawInnerCircle(g2, next.getWidth(), next.getHeight()); + } g2.setColor(original); + } + + /** + * Draw one inner circle, representing the motor mount tube, with cross hairs in the center. + * + * @param g2 the graphics context + * @param theCenterX the center x in points + * @param theCenterY the center y in points + */ + private void drawInnerCircle(final Graphics2D g2, final double theCenterX, final double theCenterY) { + double innerRadius = PrintUnit.METERS.toPoints(target.getInnerRadius()); + Shape innerCircle = new Ellipse2D.Double(theCenterX - innerRadius, theCenterY - innerRadius, innerRadius * 2, innerRadius * 2); + g2.setColor(Color.white); g2.fill(innerCircle); g2.setColor(Color.black); g2.draw(innerCircle); - drawCross(g2, (int) x, (int) y, lineLength, lineLength); + drawCross(g2, (int) theCenterX, (int) theCenterY, lineLength, lineLength); } /** * Draw the center cross-hair. * - * @param g the graphics context - * @param x the x coordinate of the center point - * @param y the y coordinate of the center point - * @param width the width in pixels of the horizontal hair + * @param g the graphics context + * @param x the x coordinate of the center point + * @param y the y coordinate of the center point + * @param width the width in pixels of the horizontal hair * @param height the width in pixels of the vertical hair */ private void drawCross(Graphics g, int x, int y, int width, int height) { 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 3cf17cae9..a3315aedd 100644 --- a/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java +++ b/core/src/net/sf/openrocket/gui/print/visitor/CenteringRingStrategy.java @@ -10,8 +10,10 @@ 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; @@ -88,6 +90,58 @@ public class CenteringRingStrategy { } } + /** + * 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 findMotorMount(CenteringRing rc) { + RocketComponent parent = rc.getParent(); + List siblings = parent.getChildren(); + + List mounts = new ArrayList(); + for (RocketComponent rocketComponents : siblings) { + if (rocketComponents != rc) { + if (rocketComponents instanceof InnerTube) { + InnerTube it = (InnerTube) rocketComponents; + if (it.isMotorMount()) { + 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. * @@ -97,7 +151,7 @@ public class CenteringRingStrategy { private void render(final CenteringRing component) { try { AbstractPrintable pfs; - pfs = new PrintableCenteringRing(component); + pfs = PrintableCenteringRing.create(component, findMotorMount(component)); java.awt.Dimension size = pfs.getSize(); final Dimension pageSize = getPageSize(); @@ -150,7 +204,7 @@ public class CenteringRingStrategy { /** * Convenience class to model a dimension. */ - class Dimension { + public static class Dimension { /** * Width, in points. */