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<CenteringRing> {
/**
private final int lineLength = 10;
/**
- * Construct a printable nose cone.
+ * A set of the inner 'holes'. At least one, but will have many if clustered.
+ */
+ private Set<CenteringRingStrategy.Dimension> innerCenterPoints = new HashSet<CenteringRingStrategy.Dimension>();
+
+ /**
+ * Construct a simple, non-clustered, printable centering ring.
*
- * @param theRing the component to print
+ * @param theRing the component to print
+ * @param theMotorMount the motor mount if clustered, else null
*/
- public PrintableCenteringRing(CenteringRing theRing) {
+ 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<Coordinate> 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<InnerTube> theMotorMounts) {
+ super(false, theRing);
+ List<Coordinate> points = new ArrayList<Coordinate>();
+ //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<InnerTube> 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<Coordinate> 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));
+ }
}
/**
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) {
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;
}
}
+ /**
+ * 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 (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.
*
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();
/**
* Convenience class to model a dimension.
*/
- class Dimension {
+ public static class Dimension {
/**
* Width, in points.
*/