]> git.gag.com Git - debian/openrocket/commitdiff
DGP - initial support for fin marking guides, transition templates, and nose cones
authorrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 19 Nov 2011 03:37:54 +0000 (03:37 +0000)
committerrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 19 Nov 2011 03:37:54 +0000 (03:37 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@205 180e2498-e6e9-4542-8430-84ac67f01cd8

17 files changed:
ChangeLog
l10n/messages.properties
src/net/sf/openrocket/gui/print/AbstractPrintableTransition.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/FinMarkingGuide.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/ITextHelper.java
src/net/sf/openrocket/gui/print/OpenRocketPrintable.java
src/net/sf/openrocket/gui/print/PrintController.java
src/net/sf/openrocket/gui/print/PrintableFinSet.java
src/net/sf/openrocket/gui/print/PrintableNoseCone.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/PrintableTransition.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/components/RocketPrintTree.java
src/net/sf/openrocket/gui/print/visitor/FinMarkingGuideStrategy.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java [deleted file]
src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java [new file with mode: 0644]
src/net/sf/openrocket/gui/rocketfigure/SymmetricComponentShapes.java
src/net/sf/openrocket/gui/rocketfigure/TransitionShapes.java

index 84038e12272a901f6f6f3f2722496e19a3358687..b678d506a6af0408f9ec959cdf087eb4dbe0a09b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-11-18  Doug Pedrick
+
+    * Printable Fin Marking Guides, Transitions, and Nose Cones (simple projection only)
+
 2011-10-11  Sampo Niskanen
 
        * [BUG] Translators fetched before initialization
index f9a55ae8261006ee902abe1c9ec59f66fa0d5143..d59680dc7792121d5f94ad10169c6f10d13fc0cc 100644 (file)
@@ -593,6 +593,9 @@ FinSetConfig.lbl.Tabposition = Tab position:
 FinSetConfig.ttip.Tabposition = The position of the fin tab.
 FinSetConfig.lbl.relativeto = relative to
 
+!FinMarkingGuide
+FinMarkingGuide.lbl.Front = Front
+
 ! MotorDatabaseLoadingDialog
 MotorDbLoadDlg.title = Loading motors
 MotorDbLoadDlg.Loadingmotors = Loading motors...
@@ -1317,6 +1320,9 @@ Icons.Redo = Redo
 
 OpenRocketPrintable.Partsdetail = Parts detail
 OpenRocketPrintable.Fintemplates = Fin templates
+OpenRocketPrintable.Transitiontemplates = Transition templates
+OpenRocketPrintable.Noseconetemplates = Nose Cone templates
+OpenRocketPrintable.Finmarkingguide = Fin marking guide
 OpenRocketPrintable.DesignReport = Design Report
 
 OpenRocketDocument.Redo = Redo
diff --git a/src/net/sf/openrocket/gui/print/AbstractPrintableTransition.java b/src/net/sf/openrocket/gui/print/AbstractPrintableTransition.java
new file mode 100644 (file)
index 0000000..8e6042d
--- /dev/null
@@ -0,0 +1,86 @@
+package net.sf.openrocket.gui.print;
+
+import net.sf.openrocket.rocketcomponent.Transition;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public abstract class AbstractPrintableTransition extends JPanel {
+    /**
+     * The stroke of the transition arc.
+     */
+    private final static BasicStroke thinStroke = new BasicStroke(1.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.
+     *
+     * @param isDoubleBuffered  a boolean, true for double-buffering
+     * @param transition  the component to be printed
+     */
+    public AbstractPrintableTransition(boolean isDoubleBuffered, Transition transition) {
+        super(isDoubleBuffered);
+        setBackground(Color.white);
+        init(transition);
+    }
+
+    /**
+     * Compute the basic values of each arc of the transition/shroud.  This is adapted from
+     * <a href="http://www.rocketshoppe.com/info/Transitions.pdf">The Properties of
+     * Model Rocket Body Tube Transitions, by J.R. Brohm</a>
+     *
+     * @param component the transition component
+     */
+    protected abstract void init(Transition component);
+
+    /**
+     * Draw the component onto the graphics context.
+     *
+     * @param g2 the graphics context
+     */
+    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
+     * another image/picture format and used accordingly.
+     *
+     * @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
+        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        // Create a graphics contents on the buffered image
+        Graphics2D g2d = bufferedImage.createGraphics();
+        // Draw graphics
+        g2d.setBackground(Color.white);
+        g2d.clearRect(0, 0, width, height);
+        paintComponent(g2d);
+        // Graphics context no longer needed so dispose it
+        g2d.dispose();
+        return bufferedImage;
+    }
+
+    @Override
+    public void paintComponent(Graphics g) {
+        Graphics2D g2 = (Graphics2D) g;
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+
+        g2.setColor(Color.BLACK);
+        g2.setStroke(thinStroke);
+
+        draw(g2);
+    }
+}
diff --git a/src/net/sf/openrocket/gui/print/FinMarkingGuide.java b/src/net/sf/openrocket/gui/print/FinMarkingGuide.java
new file mode 100644 (file)
index 0000000..e9a2577
--- /dev/null
@@ -0,0 +1,456 @@
+package net.sf.openrocket.gui.print;
+
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.startup.Application;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Path2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This is the core Swing representation of a fin marking guide.  It can handle multiple fin sets on the same or
+ * different body tubes. One marking guide will be created for any body tube that has a finset.  If a tube has
+ * multiple finsets, then they are combined onto one marking guide. It also includes launch lug marking line(s) if lugs
+ * are present. If (and only if) a launch lug exists, then the word 'Front' is affixed to the leading edge of the
+ * guide to give orientation.
+ * <p/>
+ */
+public class FinMarkingGuide extends JPanel {
+
+    /**
+     * The stroke of normal lines.
+     */
+    private final static BasicStroke thinStroke = new BasicStroke(1.0f);
+
+    /**
+     * The size of the arrow in points.
+     */
+    private static final int ARROW_SIZE = 10;
+
+    /**
+     * The default guide width in inches.
+     */
+    public final static double DEFAULT_GUIDE_WIDTH = 3d;
+
+    /**
+     * 2 PI radians (represents a circle).
+     */
+    public final static double TWO_PI = 2 * Math.PI;
+
+    /**
+     * The I18N translator.
+     */
+    private static final Translator trans = Application.getTranslator();
+
+    /**
+     * The margin.
+     */
+    private static final int MARGIN = (int) PrintUnit.INCHES.toPoints(0.25f);
+
+    /**
+     * The height (circumference) of the biggest body tube with a finset.
+     */
+    private int maxHeight = 0;
+
+    /**
+     * A map of body tubes, to a list of components that contains finsets and launch lugs.
+     */
+    private Map<BodyTube, java.util.List<ExternalComponent>> markingGuideItems;
+
+    /**
+     * Constructor.
+     *
+     * @param rocket the rocket instance
+     */
+    public FinMarkingGuide(Rocket rocket) {
+        super(false);
+        setBackground(Color.white);
+        markingGuideItems = init(rocket);
+        //Max of 2 drawing guides horizontally per page.
+        setSize((int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH) * 2 + 3 * MARGIN, maxHeight);
+    }
+
+    /**
+     * Initialize the marking guide class by iterating over a rocket and finding all finsets.
+     *
+     * @param component the root rocket component - this is iterated to find all finset and launch lugs
+     * @return a map of body tubes to lists of finsets and launch lugs.
+     */
+    private Map<BodyTube, java.util.List<ExternalComponent>> init(Rocket component) {
+        Iterator<RocketComponent> iter = component.iterator(false);
+        Map<BodyTube, java.util.List<ExternalComponent>> results = new LinkedHashMap<BodyTube, List<ExternalComponent>>();
+        BodyTube current = null;
+        int totalHeight = 0;
+        int iterationHeight = 0;
+        int count = 0;
+
+        while (iter.hasNext()) {
+            RocketComponent next = iter.next();
+            if (next instanceof BodyTube) {
+                current = (BodyTube) next;
+            } else if (next instanceof FinSet || next instanceof LaunchLug) {
+                java.util.List<ExternalComponent> list = results.get(current);
+                if (list == null && current != null) {
+                    list = new ArrayList<ExternalComponent>();
+                    results.put(current, list);
+
+                    double radius = current.getOuterRadius();
+                    int circumferenceInPoints = (int) PrintUnit.METERS.toPoints(radius * TWO_PI);
+
+                    // Find the biggest body tube circumference.
+                    if (iterationHeight < (circumferenceInPoints + MARGIN)) {
+                        iterationHeight = circumferenceInPoints + MARGIN;
+                    }
+                    //At most, two marking guides horizontally.  After that, move down and back to the left margin.
+                    count++;
+                    if (count % 2 == 0) {
+                        totalHeight += iterationHeight;
+                        iterationHeight = 0;
+                    }
+                }
+                if (list != null) {
+                    list.add((ExternalComponent) next);
+                }
+            }
+        }
+        maxHeight = totalHeight + iterationHeight;
+        return results;
+    }
+
+    /**
+     * Returns a generated image of the fin marking guide.  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 fin marking guide
+     */
+    public Image createImage() {
+        int width = getWidth() + 25;
+        int height = getHeight() + 25;
+        // Create a buffered image in which to draw
+        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        // Create a graphics context on the buffered image
+        Graphics2D g2d = bufferedImage.createGraphics();
+        // Draw graphics
+        g2d.setBackground(Color.white);
+        g2d.clearRect(0, 0, width, height);
+        paintComponent(g2d);
+        // Graphics context no longer needed so dispose it
+        g2d.dispose();
+        return bufferedImage;
+    }
+
+    /**
+     * <pre>
+     *   ---------------------- Page Edge --------------------------------------------------------
+     *   |                                        ^
+     *   |                                        |
+     *   |
+     *   |                                        y
+     *   |
+     *   |                                        |
+     *   P                                        v
+     *   a      ---                 +----------------------------+  ------------
+     *   g<------^-- x ------------>+                            +       ^
+     *   e       |                  +                            +       |
+     *           |                  +                            +     baseYOffset
+     *   E       |                  +                            +       v
+     *   d       |                  +<----------Fin------------->+ -------------
+     *   g       |                  +                            +
+     *   e       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +   baseSpacing
+     *   |       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +<----------Fin------------->+  --------------
+     *   |       |                  +                            +
+     *   | circumferenceInPoints    +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +
+     *   |       |                  +                            +    baseSpacing
+     *   |       |                  +<------Launch Lug --------->+           -----
+     *   |       |                  +                            +                 \
+     *   |       |                  +                            +                 + yLLOffset
+     *   |       |                  +                            +                 /
+     *   |       |                  +<----------Fin------------->+  --------------
+     *   |       |                  +                            +       ^
+     *   |       |                  +                            +       |
+     *   |       |                  +                            +    baseYOffset
+     *   |       v                  +                            +       v
+     *   |      ---                 +----------------------------+  --------------
+     *
+     *                              |<-------- width ----------->|
+     *
+     * yLLOffset is computed from the difference between the base rotation of the fin and the radial direction of the lug.
+     *
+     * Note: There is a current limitation that a tube with multiple launch lugs may not render the lug lines correctly.
+     * </pre>
+     *
+     * @param g the Graphics context
+     */
+    @Override
+    public void paintComponent(Graphics g) {
+        super.paintComponent(g);
+        Graphics2D g2 = (Graphics2D) g;
+        paintFinMarkingGuide(g2);
+    }
+
+    private void paintFinMarkingGuide(Graphics2D g2) {
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+
+        g2.setColor(Color.BLACK);
+        g2.setStroke(thinStroke);
+        int x = MARGIN;
+        int y = MARGIN;
+
+        int width = (int) PrintUnit.INCHES.toPoints(DEFAULT_GUIDE_WIDTH);
+
+        int column = 0;
+        for (BodyTube next : markingGuideItems.keySet()) {
+            double circumferenceInPoints = PrintUnit.METERS.toPoints(next.getOuterRadius() * TWO_PI);
+            List<ExternalComponent> componentList = markingGuideItems.get(next);
+            //Don't draw the lug if there are no fins.
+            if (hasFins(componentList)) {
+
+                drawMarkingGuide(g2, x, y, (int) (circumferenceInPoints), width);
+
+                //Sort so that fins always precede lugs
+                sort(componentList);
+
+                boolean hasMultipleComponents = componentList.size() > 1;
+
+                double baseSpacing = 0d;
+                double baseYOrigin = 0;
+                double finRadial = 0d;
+                int yFirstFin = y;
+                int yLastFin = y;
+                boolean firstFinSet = true;
+
+                //fin1: 42  fin2: 25
+                for (ExternalComponent externalComponent : componentList) {
+                    if (externalComponent instanceof FinSet) {
+                        FinSet fins = (FinSet) externalComponent;
+                        int finCount = fins.getFinCount();
+                        int offset = 0;
+                        baseSpacing = (circumferenceInPoints / finCount);
+                        double baseRotation = fins.getBaseRotation();
+                        if (!firstFinSet) {
+                            //Adjust the rotation to a positive number.
+                            while (baseRotation < 0) {
+                                baseRotation += TWO_PI / finCount;
+                            }
+                            offset = computeYOffset(y, circumferenceInPoints, baseSpacing, baseYOrigin, finRadial, baseRotation);
+                        } else {
+                            //baseYOrigin is the distance from the top of the marking guide to the first fin of the first fin set.
+                            //This measurement is used to base all subsequent finsets and lugs off of.
+                            baseYOrigin = baseSpacing / 2;
+                            offset = (int) (baseYOrigin) + y;
+                            firstFinSet = false;
+                        }
+                        yFirstFin = y;
+                        yLastFin = y;
+                        finRadial = baseRotation;
+
+                        //Draw the fin marking lines.
+                        for (int fin = 0; fin < finCount; fin++) {
+                            if (fin > 0) {
+                                offset += baseSpacing;
+                                yLastFin = offset;
+                            } else {
+                                yFirstFin = offset;
+                            }
+                            drawDoubleArrowLine(g2, x, offset, x + width, offset);
+                            if (hasMultipleComponents) {
+                                g2.drawString(externalComponent.getName(), x + (width / 3), offset - 2);
+                            }
+                        }
+                    } else if (externalComponent instanceof LaunchLug) {
+                        LaunchLug lug = (LaunchLug) externalComponent;
+                        double yLLOffset = (lug.getRadialDirection() - finRadial) / TWO_PI;
+                        //The placement of the lug line must respect the boundary of the outer marking guide.  In order
+                        //to do that, place it above or below either the top or bottom fin line, based on the difference
+                        //between their rotational directions.
+                        if (yLLOffset < 0) {
+                            yLLOffset = yLLOffset * circumferenceInPoints + yLastFin;
+                        } else {
+                            yLLOffset = yLLOffset * circumferenceInPoints + yFirstFin;
+                        }
+                        drawDoubleArrowLine(g2, x, (int) yLLOffset, x + width, (int) yLLOffset);
+                        g2.drawString(lug.getName(), x + (width / 3), (int) yLLOffset - 2);
+
+                    }
+                }
+                //Only if the tube has a lug or multiple finsets does the orientation of the marking guide matter. So print 'Front'.
+                if (hasMultipleComponents) {
+                    drawFrontIndication(g2, x, y, (int) baseSpacing, (int) circumferenceInPoints, width);
+                }
+
+                //At most, two marking guides horizontally.  After that, move down and back to the left margin.
+                column++;
+                if (column % 2 == 0) {
+                    x = MARGIN;
+                    y += circumferenceInPoints + MARGIN;
+                } else {
+                    x += MARGIN + width;
+                }
+            }
+        }
+    }
+
+    /**
+     * Compute the y offset for the next fin line.
+     *
+     * @param y                       the top margin
+     * @param circumferenceInPoints   the circumference (height) of the guide
+     * @param baseSpacing             the circumference / fin count
+     * @param baseYOrigin             the offset from the top of the guide to the first fin of the first fin set drawn
+     * @param prevBaseRotation        the rotation of the previous finset
+     * @param baseRotation            the rotation of the current finset
+     * @return number of points from the top of the marking guide to the line to be drawn
+     */
+    private int computeYOffset(int y, double circumferenceInPoints, double baseSpacing, double baseYOrigin, double prevBaseRotation, double baseRotation) {
+        int offset;
+        double finRadialDifference = (baseRotation - prevBaseRotation) / TWO_PI;
+        //If the fin line would be off the top of the marking guide, then readjust.
+        if (baseYOrigin + finRadialDifference * circumferenceInPoints < 0) {
+            offset = (int) (baseYOrigin + baseSpacing + finRadialDifference * circumferenceInPoints) + y;
+        } else if (baseYOrigin - finRadialDifference * circumferenceInPoints > 0) {
+            offset = (int) (finRadialDifference * circumferenceInPoints + baseYOrigin) + y;
+        } else {
+            offset = (int) (finRadialDifference * circumferenceInPoints - baseYOrigin) + y;
+        }
+        return offset;
+    }
+
+    /**
+     * Determines if the list contains a FinSet.
+     *
+     * @param list a list of ExternalComponent
+     * @return true if the list contains at least one FinSet
+     */
+    private boolean hasFins(List<ExternalComponent> list) {
+        for (ExternalComponent externalComponent : list) {
+            if (externalComponent instanceof FinSet) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sort a list of ExternalComponent in-place. Forces FinSets to precede Launch Lugs.
+     *
+     * @param componentList a list of ExternalComponent
+     */
+    private void sort(List<ExternalComponent> componentList) {
+        Collections.sort(componentList, new Comparator<ExternalComponent>() {
+            @Override
+            public int compare(ExternalComponent o1, ExternalComponent o2) {
+                if (o1 instanceof FinSet) {
+                    return -1;
+                }
+                if (o2 instanceof FinSet) {
+                    return 1;
+                }
+                return 0;
+            }
+        });
+    }
+
+    /**
+     * Draw the marking guide outline.
+     *
+     * @param g2     the graphics context
+     * @param x      the starting x coordinate
+     * @param y      the starting y coordinate
+     * @param length the length, or height, in print units of the marking guide; should be equivalent to the outer tube circumference
+     * @param width  the width of the marking guide in print units; somewhat arbitrary
+     */
+    private void drawMarkingGuide(Graphics2D g2, int x, int y, int length, int width) {
+        Path2D outline = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
+        outline.moveTo(x, y);
+        outline.lineTo(width + x, y);
+        outline.lineTo(width + x, length + y);
+        outline.lineTo(x, length + y);
+        outline.closePath();
+        g2.draw(outline);
+
+        //Draw tick marks for alignment, 1/4 of the width in from either edge
+        int fromEdge = (width) / 4;
+        final int tickLength = 8;
+        //Upper left
+        g2.drawLine(x + fromEdge, y, x + fromEdge, y + tickLength);
+        //Upper right
+        g2.drawLine(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
+        //Lower left
+        g2.drawLine(x + fromEdge, y + length - tickLength, x + fromEdge, y + length);
+        //Lower right
+        g2.drawLine(x + width - fromEdge, y + length - tickLength, x + width - fromEdge, y + length);
+    }
+
+    /**
+     * Draw a vertical string indicating the front of the rocket.  This is necessary when a launch lug exists to
+     * give proper orientation of the guide (assuming that the lug is asymmetrically positioned with respect to a fin).
+     *
+     * @param g2      the graphics context
+     * @param x       the starting x coordinate
+     * @param y       the starting y coordinate
+     * @param spacing the space between fin lines
+     * @param length  the length, or height, in print units of the marking guide; should be equivalent to the outer tube circumference
+     * @param width   the width of the marking guide in print units; somewhat arbitrary
+     */
+    private void drawFrontIndication(Graphics2D g2, int x, int y, int spacing, int length, int width) {
+        //The magic numbers here are fairly arbitrary.  They're chosen in a manner that best positions 'Front' to be
+        //readable, without going to complex string layout prediction logic.
+        int rotateX = x + width - 16;
+        int rotateY = y + (int) (spacing * 1.5) + 20;
+        if (rotateY > y + length + 14) {
+            rotateY = y + length / 2 - 10;
+        }
+        g2.translate(rotateX, rotateY);
+        g2.rotate(Math.PI / 2);
+        g2.drawString(trans.get("FinMarkingGuide.lbl.Front"), 0, 0);
+        g2.rotate(-Math.PI / 2);
+        g2.translate(-rotateX, -rotateY);
+    }
+
+    /**
+     * Draw a horizontal line with arrows on both endpoints.  Depicts a fin alignment.
+     *
+     * @param g2 the graphics context
+     * @param x1 the starting x coordinate
+     * @param y1 the starting y coordinate
+     * @param x2 the ending x coordinate
+     * @param y2 the ending y coordinate
+     */
+    void drawDoubleArrowLine(Graphics2D g2, int x1, int y1, int x2, int y2) {
+        int len = x2 - x1;
+
+        g2.drawLine(x1, y1, x1 + len, y2);
+        g2.fillPolygon(new int[]{x1 + len, x1 + len - ARROW_SIZE, x1 + len - ARROW_SIZE, x1 + len},
+                new int[]{y2, y2 - ARROW_SIZE / 2, y2 + ARROW_SIZE / 2, y2}, 4);
+
+        g2.fillPolygon(new int[]{x1, x1 + ARROW_SIZE, x1 + ARROW_SIZE, x1},
+                new int[]{y1, y1 - ARROW_SIZE / 2, y1 + ARROW_SIZE / 2, y1}, 4);
+    }
+
+
+}
index 47276e6a732e3462f737cb0dea8b1babe42c13a3..0b2b6f9521ed320b3688e776402f6712de4cda0d 100644 (file)
@@ -15,7 +15,7 @@ import com.itextpdf.text.pdf.PdfPCell;
 import com.itextpdf.text.pdf.PdfPTable;
 import com.itextpdf.text.pdf.PdfWriter;
 
-import java.awt.Graphics2D;
+import java.awt.*;
 import java.awt.image.BufferedImage;
 
 /**
@@ -200,43 +200,46 @@ public final class ITextHelper {
      */
     public static void renderImageAcrossPages (Rectangle pageSize, Document doc, PdfWriter writer, java.awt.Image image)
             throws DocumentException {
-        // 4/10 of an inch margin
-        final int margin = (int) (PrintUnit.POINTS_PER_INCH * 0.4f);
+        final int margin = (int)Math.min(doc.topMargin(), PrintUnit.POINTS_PER_INCH * 0.3f);
         float wPage = pageSize.getWidth() - 2 * margin;
         float hPage = pageSize.getHeight() - 2 * margin;
 
         float wImage = image.getWidth(null);
         float hImage = image.getHeight(null);
-
         java.awt.Rectangle crop = new java.awt.Rectangle(0, 0, (int) Math.min(wPage, wImage), (int) Math.min(hPage,
                                                                                                              hImage));
         PdfContentByte content = writer.getDirectContent();
 
-        double adjust;
+        int ymargin = 0;
+
         while (true) {
             BufferedImage subImage = ((BufferedImage) image).getSubimage((int) crop.getX(), (int) crop.getY(),
                                                                          (int) crop.getWidth(), (int) crop.getHeight());
 
-            Graphics2D g2 = content.createGraphics(wPage, hPage);
-            g2.drawImage(subImage, margin - 1, margin - 1, null);
+            Graphics2D g2 = content.createGraphics(pageSize.getWidth(), pageSize.getHeight());
+            g2.drawImage(subImage, margin, ymargin, null);
             g2.dispose();
-            doc.newPage();
+
+            // After the first page, the y-margin needs to be set.
+            ymargin = margin;
+
             final int newX = (int) (crop.getWidth() + crop.getX());
             if (newX < wImage) {
-                adjust = Math.min(wImage - newX, wPage);
+                double adjust = Math.min(wImage - newX, wPage);
                 crop = new java.awt.Rectangle(newX, (int) crop.getY(), (int) adjust,
                                               (int) crop.getHeight());
             }
             else {
                 final int newY = (int) (crop.getHeight() + crop.getY());
                 if (newY < hImage) {
-                    adjust = Math.min(hImage - newY, hPage);
+                    double adjust = Math.min(hImage - newY, hPage);
                     crop = new java.awt.Rectangle(0, newY, (int) Math.min(wPage, wImage), (int) adjust);
                 }
                 else {
                     break;
                 }
             }
+            doc.newPage();
         }
     }
 
index d0908a7dade24deec36355978dd25e0c9bc50efc..96ea72678f28c3a3bc7ca04a0cd0d06624225597 100644 (file)
@@ -6,21 +6,29 @@ package net.sf.openrocket.gui.print;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.startup.Application;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This enumeration identifies the various types of information that may be printed.
  */
 
 public enum OpenRocketPrintable {
-       //PARTS_LIST("Parts list", true, 0),
-       //// Parts detail
+       // Parts detail
        PARTS_DETAIL("OpenRocketPrintable.Partsdetail", true, 1),
-       ////
+       // Finset shape
        FIN_TEMPLATE("OpenRocketPrintable.Fintemplates", true, 2),
-       //// Design Report
-       DESIGN_REPORT("OpenRocketPrintable.DesignReport", false, 3);
-       
+       // Fin marking guide.
+       FIN_MARKING_GUIDE("OpenRocketPrintable.Finmarkingguide", false, 3),
+    // Transition Templates
+    TRANSITION_TEMPLATE("OpenRocketPrintable.Transitiontemplates", false, 4),
+    // Nose Cone Templates
+    NOSE_CONE_TEMPLATE("OpenRocketPrintable.Noseconetemplates", false, 5),
+       // Design Report
+       DESIGN_REPORT("OpenRocketPrintable.DesignReport", false, 6);
+
        private static final Translator trans = Application.getTranslator();
-       
+
        /**
         * The description - will be displayed in the JTree.
         */
@@ -93,4 +101,20 @@ public enum OpenRocketPrintable {
                }
                return null;
        }
+
+    /**
+     * Get a list of ordered enum values that do not have stage affinity.
+     *
+     * @return a list of OpenRocketPrintable
+     */
+    public static List<OpenRocketPrintable> getUnstaged() {
+        List<OpenRocketPrintable> unstaged = new ArrayList<OpenRocketPrintable>();
+        OpenRocketPrintable[] values = values();
+        for (OpenRocketPrintable value : values) {
+            if (!value.isStageSpecific()) {
+                unstaged.add(value);
+            }
+        }
+        return unstaged;
+    }
 }
index 0a71bbbe1cca1655d02710d07163016033a8442c..69e4df057b2e1d392be51a167c2d4524009bebe7 100644 (file)
@@ -4,15 +4,6 @@
  */
 package net.sf.openrocket.gui.print;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Iterator;
-import java.util.Set;
-
-import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.print.visitor.FinSetVisitorStrategy;
-import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy;
-
 import com.itextpdf.text.Document;
 import com.itextpdf.text.DocumentException;
 import com.itextpdf.text.ExceptionConverter;
@@ -20,6 +11,16 @@ import com.itextpdf.text.Rectangle;
 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.visitor.FinMarkingGuideStrategy;
+import net.sf.openrocket.gui.print.visitor.FinSetPrintStrategy;
+import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy;
+import net.sf.openrocket.gui.print.visitor.TransitionStrategy;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Set;
 
 /**
  * This is the main active object for printing.  It performs all actions necessary to create and populate the print
@@ -33,7 +34,7 @@ public class PrintController {
         * @param doc         the OR document
         * @param toBePrinted the user chosen items to print
         * @param outputFile  the file being written to
-        * @param msn         the paper size
+        * @param settings    the print settings
         */
        public void print(OpenRocketDocument doc, Iterator<PrintableContext> toBePrinted, OutputStream outputFile,
                                                PrintSettings settings) {
@@ -63,7 +64,7 @@ public class PrintController {
                                        idoc.newPage();
                                        break;
                                case FIN_TEMPLATE:
-                                       final FinSetVisitorStrategy finWriter = new FinSetVisitorStrategy(idoc,
+                                       final FinSetPrintStrategy finWriter = new FinSetPrintStrategy(idoc,
                                                                                                                                                                                        writer,
                                                                                                                                                                                        stages);
                                        finWriter.writeToDocument(doc.getRocket());
@@ -76,7 +77,24 @@ public class PrintController {
                                        detailVisitor.close();
                                        idoc.newPage();
                                        break;
-                               }
+                case TRANSITION_TEMPLATE:
+                    final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages);
+                    tranWriter.writeToDocument(doc.getRocket(), false);
+                    idoc.newPage();
+                    break;
+
+                case NOSE_CONE_TEMPLATE:
+                    final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages);
+                    coneWriter.writeToDocument(doc.getRocket(), true);
+                    idoc.newPage();
+                    break;
+
+                case FIN_MARKING_GUIDE:
+                    final FinMarkingGuideStrategy fmg = new FinMarkingGuideStrategy(idoc, writer);
+                    fmg.writeToDocument(doc.getRocket());
+                    idoc.newPage();
+                    break;
+                }
                        }
                        //Stupid iText throws a really nasty exception if there is no data when close is called.
                        if (writer.getCurrentDocumentSize() <= 140) {
index 8af42f87feb661faa63907b141f7e4cb64651f69..cf07cb13e02f96519f249efdccd5e231dc10b996 100644 (file)
@@ -6,11 +6,8 @@ package net.sf.openrocket.gui.print;
 import net.sf.openrocket.rocketcomponent.FinSet;
 import net.sf.openrocket.util.Coordinate;
 
-import javax.swing.JPanel;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
+import javax.swing.*;
+import java.awt.*;
 import java.awt.geom.GeneralPath;
 import java.awt.image.BufferedImage;
 import java.awt.print.PageFormat;
@@ -32,11 +29,19 @@ public class PrintableFinSet extends JPanel implements Printable {
     /**
      * The X margin.
      */
-    private int marginX = 25;
+    private final int marginX = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
     /**
      * The Y margin.
      */
-    private int marginY = 25;
+    private final int marginY = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
+    /**
+     * The minimum X coordinate.
+     */
+    private int minX = 0;
+    /**
+     * The minimum Y coordinate.
+     */
+    private int minY = 0;
 
     /**
      * Constructor.
@@ -68,8 +73,6 @@ public class PrintableFinSet extends JPanel implements Printable {
         polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, points.length);
         polygon.moveTo(0, 0);
 
-        int minX = 0;
-        int minY = 0;
         int maxX = 0;
         int maxY = 0;
 
@@ -84,13 +87,7 @@ public class PrintableFinSet extends JPanel implements Printable {
         }
         polygon.closePath();
 
-        if (minX < 0) {
-            marginX += Math.abs(minX);
-        }
-        if (minY < 0) {
-            marginY += Math.abs(minY);
-        }
-        setSize(maxX - minX + marginX, maxY - minY + marginY);
+        setSize(maxX - minX, maxY - minY);
     }
 
     /**
@@ -181,7 +178,18 @@ public class PrintableFinSet extends JPanel implements Printable {
         super.paintComponent(g);
         Graphics2D g2d = (Graphics2D) g;
 
-        g2d.translate(marginX, marginY);
+        int x = 0;
+        int y = 0;
+
+        // 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);
+        }
+        if (minY < marginY) {
+            y = marginY + Math.abs(minY);
+        }
+        // Reset the origin.
+        g2d.translate(x, y);
         g2d.setPaint(TemplateProperties.getFillColor());
         g2d.fill(polygon);
         g2d.setPaint(TemplateProperties.getLineColor());
diff --git a/src/net/sf/openrocket/gui/print/PrintableNoseCone.java b/src/net/sf/openrocket/gui/print/PrintableNoseCone.java
new file mode 100644 (file)
index 0000000..77ea45b
--- /dev/null
@@ -0,0 +1,56 @@
+package net.sf.openrocket.gui.print;
+
+import net.sf.openrocket.gui.rocketfigure.TransitionShapes;
+import net.sf.openrocket.rocketcomponent.NoseCone;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.*;
+
+public class PrintableNoseCone extends AbstractPrintableTransition {
+
+    /**
+     * If the component to be drawn is a nose cone, save a reference to it.
+     */
+    private NoseCone target;
+
+    /**
+     * Construct a printable nose cone.
+     *
+     * @param noseCone the component to print
+     */
+    public PrintableNoseCone(Transition noseCone) {
+        super(false, noseCone);
+    }
+
+    @Override
+    protected void init(Transition component) {
+
+        target = (NoseCone) component;
+        double radius = target.getForeRadius();
+        if (radius < target.getAftRadius()) {
+            radius = target.getAftRadius();
+        }
+        setSize((int) PrintUnit.METERS.toPoints(2 * radius) + marginX,
+                (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + marginY);
+    }
+
+    /**
+     * Draw a nose cone.
+     *
+     * @param g2 the graphics context
+     */
+    protected void draw(Graphics2D g2) {
+        Shape[] shapes = TransitionShapes.getShapesSide(target, Transformation.rotate_x(0d), PrintUnit.METERS.toPoints(1));
+
+        if (shapes != null && shapes.length > 0) {
+            Rectangle r = shapes[0].getBounds();
+            g2.translate(marginX + r.getHeight() / 2, marginY);
+            g2.rotate(Math.PI / 2);
+            for (Shape shape : shapes) {
+                g2.draw(shape);
+            }
+            g2.rotate(-Math.PI / 2);
+        }
+    }
+}
diff --git a/src/net/sf/openrocket/gui/print/PrintableTransition.java b/src/net/sf/openrocket/gui/print/PrintableTransition.java
new file mode 100644 (file)
index 0000000..c8e6176
--- /dev/null
@@ -0,0 +1,205 @@
+package net.sf.openrocket.gui.print;
+
+import net.sf.openrocket.rocketcomponent.Transition;
+
+import java.awt.*;
+import java.awt.geom.Arc2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+
+/**
+ * This class allows for a Transition to be printable.  It does so by decorating an existing transition (which will not be
+ * modified) and rendering it within a JPanel.  The JPanel is not actually visualized on a display, but instead renders
+ * it to a print device.
+ * <p/>
+ * Note: Currently nose cones are only supported by drawing the 2D projection of the profile.  A more useful approach
+ * may be to draw a myriahedral projection that can be cut out and bent to form the shape.
+ */
+public class PrintableTransition extends AbstractPrintableTransition {
+
+    /**
+     * Dashed array value.
+     */
+    private final static float dash1[] = {4.0f};
+    /**
+     * The dashed stroke for glue tab.
+     */
+    private final static BasicStroke dashed = new BasicStroke(1.0f,
+            BasicStroke.CAP_BUTT,
+            BasicStroke.JOIN_MITER,
+            10.0f, dash1, 0.0f);
+
+    /**
+     * The layout is an outer arc, an inner arc, and two lines one either endpoints that connect the arcs.
+     * Most of the math involves transposing geometric cartesian coordinates to the Java AWT coordinate system.
+     */
+    private Path2D gp;
+
+    /**
+     * The glue tab.
+     */
+    private Path2D glueTab1;
+
+    /**
+     * The alignment marks.
+     */
+    private Line2D tick1, tick2;
+
+    /**
+     * The x coordinates for the two ticks drawn at theta degrees.
+     */
+    private int tick3X, tick4X;
+
+    /**
+     * The angle, in degrees.
+     */
+    private float theta;
+
+    /**
+     * The x,y coordinates for where the virtual circle center is located.
+     */
+    private int circleCenterX, circleCenterY;
+
+    /**
+     * Constructor.
+     *
+     * @param transition the transition to print
+     */
+    public PrintableTransition(Transition transition) {
+        super(false, transition);
+    }
+
+    @Override
+    protected void init(Transition component) {
+
+        double r1 = component.getAftRadius();
+        double r2 = component.getForeRadius();
+
+        //Regardless of orientation, we have the convention of R1 as the smaller radius.  Flip if different.
+        if (r1 > r2) {
+            r1 = r2;
+            r2 = component.getAftRadius();
+        }
+        double len = component.getLength();
+        double v = r2 - r1;
+        double tmp = Math.sqrt(v * v + len * len);
+        double factor = tmp / v;
+
+        theta = (float) (360d * v / tmp);
+
+        int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
+        int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
+
+        int x = marginX;
+        int tabOffset = 35;
+        int y = tabOffset + marginY;
+
+        Arc2D.Double outerArc = new Arc2D.Double();
+        Arc2D.Double innerArc = new Arc2D.Double();
+
+        //If the arcs are more than 3/4 of a circle, then assume the height (y) is the same as the radius of the bigger arc.
+        if (theta >= 270) {
+            y += r2InPoints;
+        }
+        //If the arc is between 1/2 and 3/4 of a circle, then compute the actual height based upon the angle and radius
+        //of the bigger arc.
+        else if (theta >= 180) {
+            double thetaRads = Math.toRadians(theta - 180);
+            y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
+        }
+
+        circleCenterY = y;
+        circleCenterX = r2InPoints + x;
+
+        //Create the larger arc.
+        outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
+
+        //Create the smaller arc.
+        innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
+
+        //Create the line between the start of the larger arc and the start of the smaller arc.
+        Path2D.Double line = new Path2D.Double();
+        line.setWindingRule(Path2D.WIND_NON_ZERO);
+        line.moveTo(x, y);
+        final int width = r2InPoints - r1InPoints;
+        line.lineTo(width + x, y);
+
+        //Create the line between the endpoint of the larger arc and the endpoint of the smaller arc.
+        Path2D.Double closingLine = new Path2D.Double();
+        closingLine.setWindingRule(Path2D.WIND_NON_ZERO);
+        Point2D innerArcEndPoint = innerArc.getEndPoint();
+        closingLine.moveTo(innerArcEndPoint.getX(), innerArcEndPoint.getY());
+        Point2D outerArcEndPoint = outerArc.getEndPoint();
+        closingLine.lineTo(outerArcEndPoint.getX(), outerArcEndPoint.getY());
+
+        //Add all shapes to the polygon path.
+        gp = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
+        gp.append(line, false);
+        gp.append(outerArc, false);
+        gp.append(closingLine, false);
+        gp.append(innerArc, false);
+
+        //Create the glue tab.
+        glueTab1 = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
+        glueTab1.moveTo(x, y);
+        glueTab1.lineTo(x + tabOffset, y - tabOffset);
+        glueTab1.lineTo(width + x - tabOffset, y - tabOffset);
+        glueTab1.lineTo(width + x, y);
+
+        //Create tick marks for alignment, 1/4 of the width in from either edge
+        int fromEdge = width / 4;
+        final int tickLength = 8;
+        //Upper left
+        tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
+        //Upper right
+        tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
+
+        tick3X = r2InPoints - fromEdge;
+        tick4X = r1InPoints + fromEdge;
+
+        setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
+    }
+
+    /**
+     * Draw alignment marks on an angle.
+     *
+     * @param g2    the graphics context
+     * @param x     the center of the circle's x coordinate
+     * @param y     the center of the circle's y
+     * @param line  the line to draw
+     * @param theta the angle
+     */
+    private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
+        g2.translate(x, y);
+        g2.rotate(Math.toRadians(-theta));
+        g2.draw(line);
+        g2.rotate(Math.toRadians(theta));
+        g2.translate(-x, -y);
+    }
+
+    /**
+     * Draw a transition.
+     *
+     * @param g2 the graphics context
+     */
+    protected void draw(Graphics2D g2) {
+        //Render it.
+        g2.draw(gp);
+        g2.draw(tick1);
+        g2.draw(tick2);
+        drawAlignmentMarks(g2, circleCenterX,
+                circleCenterY,
+                new Line2D.Float(-tick3X, 0, -tick3X, -8),
+                theta);
+        drawAlignmentMarks(g2, circleCenterX,
+                circleCenterY,
+                new Line2D.Float(-tick4X, 0, -tick4X, -8),
+                theta);
+
+        g2.setStroke(dashed);
+        g2.draw(glueTab1);
+    }
+
+}
index d3de7bec36af3e8da0bec07afea0c995a78c0c1a..5296ea478d21c267fd2ebe30e5ef7d9097d47044 100644 (file)
@@ -3,23 +3,22 @@
  */
 package net.sf.openrocket.gui.print.components;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
+import net.sf.openrocket.gui.print.OpenRocketPrintable;
+import net.sf.openrocket.gui.print.PrintableContext;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
 
-import javax.swing.JTree;
+import javax.swing.*;
 import javax.swing.event.TreeExpansionEvent;
 import javax.swing.event.TreeWillExpandListener;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.ExpandVetoException;
 import javax.swing.tree.TreePath;
 import javax.swing.tree.TreeSelectionModel;
-
-import net.sf.openrocket.gui.print.OpenRocketPrintable;
-import net.sf.openrocket.gui.print.PrintableContext;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.rocketcomponent.Stage;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
 
 /**
  * A specialized JTree for displaying various rocket items that can be printed.
@@ -76,9 +75,13 @@ public class RocketPrintTree extends JTree {
                                }
                        }
                }
-               toAddTo.add(new CheckBoxNode(OpenRocketPrintable.DESIGN_REPORT.getDescription(),
+
+        List<OpenRocketPrintable> unstaged = OpenRocketPrintable.getUnstaged();
+        for (int i = 0; i < unstaged.size(); i++) {
+                   toAddTo.add(new CheckBoxNode(unstaged.get(i).getDescription(),
                                                                                INITIAL_CHECKBOX_SELECTED));
-               
+        }
+
                RocketPrintTree tree = new RocketPrintTree(root);
                
                tree.addTreeWillExpandListener
diff --git a/src/net/sf/openrocket/gui/print/visitor/FinMarkingGuideStrategy.java b/src/net/sf/openrocket/gui/print/visitor/FinMarkingGuideStrategy.java
new file mode 100644 (file)
index 0000000..f4e5a52
--- /dev/null
@@ -0,0 +1,167 @@
+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.FinMarkingGuide;
+import net.sf.openrocket.gui.print.ITextHelper;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.startup.Application;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+/**
+ * A strategy for drawing a fin marking guide. As currently implemented, each body tube with a finset will have
+ * a marking guide.  If a tube has multiple fin sets, they are combined onto one marking guide.  Launch lugs are supported
+ * as well.
+ */
+public class FinMarkingGuideStrategy {
+
+    /**
+     * The logger.
+     */
+    private static final LogHelper log = Application.getLogger();
+
+    /**
+     * The iText document.
+     */
+    protected Document document;
+
+    /**
+     * The direct iText writer.
+     */
+    protected PdfWriter writer;
+
+    /**
+     * Constructor.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     */
+    public FinMarkingGuideStrategy(Document doc, PdfWriter theWriter) {
+        document = doc;
+        writer = theWriter;
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param root the root component; all children will be visited recursively
+     */
+    public void writeToDocument(final Rocket root) {
+        render(root);
+    }
+
+
+    /**
+     * The core behavior of this strategy.
+     *
+     * @param rocket the rocket to render all
+     */
+    private void render(final Rocket rocket) {
+        try {
+            FinMarkingGuide pfs = new FinMarkingGuide(rocket);
+
+            java.awt.Dimension size = pfs.getSize();
+            final Dimension pageSize = getPageSize();
+            if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
+                printOnOnePage(pfs);
+            } else {
+                BufferedImage image = (BufferedImage) pfs.createImage();
+                ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
+                        document, writer, image);
+            }
+        } catch (DocumentException e) {
+            log.error("Could not render the fin marking guide.", 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;
+    }
+
+    /**
+     * Print the transition.
+     *
+     * @param theMarkingGuide the fin marking guide
+     */
+    private void printOnOnePage(final FinMarkingGuide theMarkingGuide) {
+        Dimension d = getPageSize();
+        PdfContentByte cb = writer.getDirectContent();
+        Graphics2D g2 = cb.createGraphics(d.width, d.height);
+        theMarkingGuide.print(g2);
+        g2.dispose();
+        document.newPage();
+    }
+
+    /**
+     * 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.
+     */
+    class Dimension {
+        /**
+         * Width, in points.
+         */
+        public float width;
+        /**
+         * Height, in points.
+         */
+        public float height;
+
+        /**
+         * Constructor.
+         *
+         * @param w width
+         * @param h height
+         */
+        public Dimension(float w, float h) {
+            width = w;
+            height = h;
+        }
+
+        /**
+         * Get the width.
+         *
+         * @return the width
+         */
+        public float getWidth() {
+            return width;
+        }
+
+        /**
+         * Get the height.
+         *
+         * @return the height
+         */
+        public float getHeight() {
+            return height;
+        }
+    }
+}
diff --git a/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java b/src/net/sf/openrocket/gui/print/visitor/FinSetPrintStrategy.java
new file mode 100644 (file)
index 0000000..80e15a2
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * FinSetPrintStrategy.java
+ */
+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.PrintableFinSet;
+import net.sf.openrocket.logging.LogHelper;
+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.List;
+import java.util.Set;
+
+/**
+ * A strategy for drawing fin templates.
+ */
+public class FinSetPrintStrategy {
+
+    /**
+     * 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;
+
+    /**
+     * Constructor.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     * @param theStages        The stages to be printed by this strategy
+     */
+    public FinSetPrintStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStages) {
+        document = doc;
+        writer = theWriter;
+        stages = theStages;
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param root the root component; all children will be printed 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 printed recursively
+     */
+    protected void goDeep (final List<RocketComponent> theRc) {
+        for (RocketComponent rocketComponent : theRc) {
+            if (rocketComponent instanceof FinSet) {
+                printFinSet((FinSet) rocketComponent);
+            }
+            else if (rocketComponent.getChildCount() > 0) {
+                goDeep(rocketComponent.getChildren());
+            }
+        }
+    }
+
+    /**
+     * The core behavior of this strategy.
+     *
+     * @param finSet the object to extract info about; a graphical image of the fin shape is drawn to the document
+     */
+    private void printFinSet(final FinSet finSet) {
+        if (shouldPrintStage(finSet.getStageNumber())) {
+            try {
+                PrintableFinSet pfs = new PrintableFinSet(finSet);
+
+                java.awt.Dimension finSize = pfs.getSize();
+                final Dimension pageSize = getPageSize();
+                if (fitsOnOnePage(pageSize, finSize.getWidth(), finSize.getHeight())) {
+                    printOnOnePage(pfs);
+                }
+                else {
+                    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 fin.", e);
+            }
+        }
+    }
+
+    /**
+     * Determine if the strategy's set of stage numbers (to print) contains the specified stage.
+     *
+     * @param stageNumber a stage number
+     *
+     * @return true if the strategy contains the stage number provided
+     */
+    public boolean shouldPrintStage(int stageNumber) {
+        if (stages == null || stages.isEmpty()) {
+            return false;
+        }
+
+        for (final Integer stage : stages) {
+            if (stage == stageNumber) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Print the fin set.
+     *
+     * @param thePfs the printable fin set
+     */
+    private void printOnOnePage (final PrintableFinSet thePfs) {
+        Dimension d = getPageSize();
+        PdfContentByte cb = writer.getDirectContent();
+        Graphics2D g2 = cb.createGraphics(d.width, d.height);
+        thePfs.print(g2);
+        g2.dispose();
+    }
+
+    /**
+     * 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.
+     */
+    class Dimension {
+        /** Width, in points. */
+        public float width;
+        /** Height, in points. */
+        public float height;
+
+        /**
+         * Constructor.
+         * @param w width
+         * @param h height
+         */
+        public Dimension (float w, float h) {
+            width = w;
+            height = h;
+        }
+
+        /**
+         * Get the width.
+         *
+         * @return  the width
+         */
+        public float getWidth () {
+            return width;
+        }
+
+        /**
+         * Get the height.
+         *
+         * @return the height
+         */
+        public float getHeight () {
+            return height;
+        }
+    }
+}
diff --git a/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java
deleted file mode 100644 (file)
index a079bb8..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * FinSetVisitorStrategy.java
- */
-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.PrintableFinSet;
-import net.sf.openrocket.logging.LogHelper;
-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.List;
-import java.util.Set;
-
-/**
- * A strategy for drawing fin templates.
- */
-public class FinSetVisitorStrategy {
-
-    /**
-     * 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;
-
-    /**
-     * Constructor.
-     *
-     * @param doc              The iText document
-     * @param theWriter        The direct iText writer
-     * @param theStagesToVisit The stages to be visited by this strategy
-     */
-    public FinSetVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
-        document = doc;
-        writer = theWriter;
-        stages = theStagesToVisit;
-    }
-
-    /**
-     * 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 FinSet) {
-                doVisit((FinSet)rocketComponent);
-            }
-            else if (rocketComponent.getChildCount() > 0) {
-                goDeep(rocketComponent.getChildren());
-            }
-        }
-    }
-
-    /**
-     * The core behavior of this visitor.
-     *
-     * @param visitable the object to extract info about; a graphical image of the fin shape is drawn to the document
-     */
-    private void doVisit (final FinSet visitable) {
-        if (shouldVisitStage(visitable.getStageNumber())) {
-            try {
-                PrintableFinSet pfs = new PrintableFinSet(visitable);
-
-                java.awt.Dimension finSize = pfs.getSize();
-                final Dimension pageSize = getPageSize();
-                if (fitsOnOnePage(pageSize, finSize.getWidth(), finSize.getHeight())) {
-                    printOnOnePage(pfs);
-                }
-                else {
-                    BufferedImage image = (BufferedImage) pfs.createImage();
-                    ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
-                                                       document, writer, image);
-                }
-            }
-            catch (DocumentException e) {
-                log.error("Could not render fin.", e);
-            }
-        }
-    }
-
-    /**
-     * Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage.
-     *
-     * @param stageNumber a stage number
-     *
-     * @return true if the visitor strategy contains the stage number provided
-     */
-    public boolean shouldVisitStage (int stageNumber) {
-        if (stages == null || stages.isEmpty()) {
-            return false;
-        }
-
-        for (final Integer stage : stages) {
-            if (stage == stageNumber) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Print the fin set.
-     *
-     * @param thePfs the printable fin set
-     */
-    private void printOnOnePage (final PrintableFinSet thePfs) {
-        Dimension d = getPageSize();
-        PdfContentByte cb = writer.getDirectContent();
-        Graphics2D g2 = cb.createGraphics(d.width, d.height);
-        thePfs.print(g2);
-        g2.dispose();
-        document.newPage();
-    }
-
-    /**
-     * 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.
-     */
-    class Dimension {
-        /** Width, in points. */
-        public float width;
-        /** Height, in points. */
-        public float height;
-
-        /**
-         * Constructor.
-         * @param w width
-         * @param h height
-         */
-        public Dimension (float w, float h) {
-            width = w;
-            height = h;
-        }
-
-        /**
-         * Get the width.
-         *
-         * @return  the width
-         */
-        public float getWidth () {
-            return width;
-        }
-
-        /**
-         * Get the height.
-         *
-         * @return the height
-         */
-        public float getHeight () {
-            return height;
-        }
-    }
-}
diff --git a/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java b/src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java
new file mode 100644 (file)
index 0000000..1988ab8
--- /dev/null
@@ -0,0 +1,205 @@
+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.AbstractPrintableTransition;
+import net.sf.openrocket.gui.print.ITextHelper;
+import net.sf.openrocket.gui.print.PrintableNoseCone;
+import net.sf.openrocket.gui.print.PrintableTransition;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.NoseCone;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.startup.Application;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A strategy for drawing transition/shroud/nose cone templates.
+ */
+public class TransitionStrategy {
+
+    /**
+     * 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;
+
+    /**
+     * Constructor.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     * @param theStagesToVisit The stages to be visited by this strategy
+     */
+    public TransitionStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
+        document = doc;
+        writer = theWriter;
+        stages = theStagesToVisit;
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @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
+     */
+    public void writeToDocument(final RocketComponent root, boolean noseCones) {
+        List<RocketComponent> rc = root.getChildren();
+        goDeep(rc, noseCones);
+    }
+
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @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
+     */
+    protected void goDeep(final List<RocketComponent> theRc, boolean noseCones) {
+        for (RocketComponent rocketComponent : theRc) {
+            if (rocketComponent instanceof NoseCone) {
+                if (noseCones) {
+                    render((Transition) rocketComponent);
+                }
+            } else if (rocketComponent instanceof Transition && !noseCones) {
+                render((Transition) rocketComponent);
+            } else if (rocketComponent.getChildCount() > 0) {
+                goDeep(rocketComponent.getChildren(), noseCones);
+            }
+        }
+    }
+
+    /**
+     * 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
+     */
+    private void render(final Transition component) {
+        try {
+            AbstractPrintableTransition pfs;
+            if (component instanceof NoseCone) {
+                pfs = new PrintableNoseCone(component);
+            } else {
+                pfs = new PrintableTransition(component);
+            }
+
+            java.awt.Dimension size = pfs.getSize();
+            final Dimension pageSize = getPageSize();
+            if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
+                printOnOnePage(pfs);
+            } else {
+                BufferedImage image = (BufferedImage) pfs.createImage();
+                ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
+                        document, writer, image);
+            }
+        } catch (DocumentException e) {
+            log.error("Could not render the transition.", 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;
+    }
+
+    /**
+     * Print the transition.
+     *
+     * @param theTransition the printable transition
+     */
+    private void printOnOnePage(final AbstractPrintableTransition theTransition) {
+        Dimension d = getPageSize();
+        PdfContentByte cb = writer.getDirectContent();
+        Graphics2D g2 = cb.createGraphics(d.width, d.height);
+        theTransition.print(g2);
+        g2.dispose();
+        document.newPage();
+    }
+
+    /**
+     * 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.
+     */
+    class Dimension {
+        /**
+         * Width, in points.
+         */
+        public float width;
+        /**
+         * Height, in points.
+         */
+        public float height;
+
+        /**
+         * Constructor.
+         *
+         * @param w width
+         * @param h height
+         */
+        public Dimension(float w, float h) {
+            width = w;
+            height = h;
+        }
+
+        /**
+         * Get the width.
+         *
+         * @return the width
+         */
+        public float getWidth() {
+            return width;
+        }
+
+        /**
+         * Get the height.
+         *
+         * @return the height
+         */
+        public float getHeight() {
+            return height;
+        }
+    }
+}
index 53dc895d9a413805f656aa3048a8503e580bcc84..791057c77311ed2045a84812c168755d5c98c84f 100644 (file)
@@ -1,13 +1,13 @@
 package net.sf.openrocket.gui.rocketfigure;
 
-import java.awt.Shape;
-import java.awt.geom.Path2D;
-import java.util.ArrayList;
-
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.Transformation;
 
+import java.awt.*;
+import java.awt.geom.Path2D;
+import java.util.ArrayList;
+
 
 public class SymmetricComponentShapes extends RocketComponentShapes {
        private static final int MINPOINTS = 91;
@@ -16,9 +16,14 @@ public class SymmetricComponentShapes extends RocketComponentShapes {
        // TODO: HIGH: adaptiveness sucks, remove it.
        
        // TODO: LOW: Uses only first component of cluster (not currently clusterable)
-       
-       public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component,
-                       Transformation transformation) {
+
+    public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component,
+                                        Transformation transformation) {
+        return getShapesSide(component, transformation, S);
+    }
+
+    public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component,
+                                        Transformation transformation, final double scaleFactor) {
                net.sf.openrocket.rocketcomponent.SymmetricComponent c = (net.sf.openrocket.rocketcomponent.SymmetricComponent) component;
                int i;
                
@@ -77,14 +82,14 @@ public class SymmetricComponentShapes extends RocketComponentShapes {
                
                // TODO: LOW: curved path instead of linear
                Path2D.Double path = new Path2D.Double();
-               path.moveTo(points.get(len - 1).x * S, points.get(len - 1).y * S);
+               path.moveTo(points.get(len - 1).x * scaleFactor, points.get(len - 1).y * scaleFactor);
                for (i = len - 2; i >= 0; i--) {
-                       path.lineTo(points.get(i).x * S, points.get(i).y * S);
+                       path.lineTo(points.get(i).x * scaleFactor, points.get(i).y * scaleFactor);
                }
                for (i = 0; i < len; i++) {
-                       path.lineTo(points.get(i).x * S, -points.get(i).y * S);
+                       path.lineTo(points.get(i).x * scaleFactor, -points.get(i).y * scaleFactor);
                }
-               path.lineTo(points.get(len - 1).x * S, points.get(len - 1).y * S);
+               path.lineTo(points.get(len - 1).x * scaleFactor, points.get(len - 1).y * scaleFactor);
                path.closePath();
                
                //s[len] = path;
index 205cd502afdd51505b77a391d502e2335ec554b5..2312c1bd313e0d298f4a2456f410c917dc664029 100644 (file)
@@ -1,21 +1,26 @@
 package net.sf.openrocket.gui.rocketfigure;
 
-import java.awt.Shape;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.Path2D;
-import java.awt.geom.Rectangle2D;
-
 import net.sf.openrocket.rocketcomponent.Transition;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.Transformation;
 
+import java.awt.*;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+
 
 public class TransitionShapes extends RocketComponentShapes {
 
        // TODO: LOW: Uses only first component of cluster (not currently clusterable).
-       
-       public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component, 
-                       Transformation transformation) {
+
+    public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component,
+                                        Transformation transformation) {
+        return getShapesSide(component, transformation, S);
+    }
+
+    public static Shape[] getShapesSide(net.sf.openrocket.rocketcomponent.RocketComponent component,
+                                        Transformation transformation, final double scaleFactor) {
                net.sf.openrocket.rocketcomponent.Transition transition = (net.sf.openrocket.rocketcomponent.Transition)component;
 
                Shape[] mainShapes;
@@ -29,15 +34,15 @@ public class TransitionShapes extends RocketComponentShapes {
                                        toAbsolute(Coordinate.NUL)[0]);
                        
                        Path2D.Float path = new Path2D.Float();
-                       path.moveTo(start.x*S, r1*S);
-                       path.lineTo((start.x+length)*S, r2*S);
-                       path.lineTo((start.x+length)*S, -r2*S);
-                       path.lineTo(start.x*S, -r1*S);
+                       path.moveTo(start.x* scaleFactor, r1* scaleFactor);
+                       path.lineTo((start.x+length)* scaleFactor, r2* scaleFactor);
+                       path.lineTo((start.x+length)* scaleFactor, -r2* scaleFactor);
+                       path.lineTo(start.x* scaleFactor, -r1* scaleFactor);
                        path.closePath();
                        
                        mainShapes = new Shape[] { path };
                } else {
-                       mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation);
+                       mainShapes = SymmetricComponentShapes.getShapesSide(component, transformation, scaleFactor);
                }
                
                Rectangle2D.Double shoulder1=null, shoulder2=null;
@@ -48,7 +53,7 @@ public class TransitionShapes extends RocketComponentShapes {
                                        toAbsolute(Coordinate.NUL)[0]);
                        double r = transition.getForeShoulderRadius();
                        double l = transition.getForeShoulderLength();
-                       shoulder1 = new Rectangle2D.Double((start.x-l)*S, -r*S, l*S, 2*r*S);
+                       shoulder1 = new Rectangle2D.Double((start.x-l)* scaleFactor, -r* scaleFactor, l* scaleFactor, 2*r* scaleFactor);
                        arrayLength++;
                }
                if (transition.getAftShoulderLength() > 0.0005) {
@@ -56,7 +61,7 @@ public class TransitionShapes extends RocketComponentShapes {
                                        toAbsolute(new Coordinate(transition.getLength()))[0]);
                        double r = transition.getAftShoulderRadius();
                        double l = transition.getAftShoulderLength();
-                       shoulder2 = new Rectangle2D.Double(start.x*S, -r*S, l*S, 2*r*S);
+                       shoulder2 = new Rectangle2D.Double(start.x* scaleFactor, -r* scaleFactor, l* scaleFactor, 2*r* scaleFactor);
                        arrayLength++;
                }
                if (shoulder1==null && shoulder2==null)