DGP - initial support for fin marking guides, transition templates, and nose cones
This commit is contained in:
parent
fb3df1b97b
commit
8f3b051d61
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
456
src/net/sf/openrocket/gui/print/FinMarkingGuide.java
Normal file
456
src/net/sf/openrocket/gui/print/FinMarkingGuide.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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());
|
||||
|
||||
56
src/net/sf/openrocket/gui/print/PrintableNoseCone.java
Normal file
56
src/net/sf/openrocket/gui/print/PrintableNoseCone.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
205
src/net/sf/openrocket/gui/print/PrintableTransition.java
Normal file
205
src/net/sf/openrocket/gui/print/PrintableTransition.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* FinSetVisitorStrategy.java
|
||||
* FinSetPrintStrategy.java
|
||||
*/
|
||||
package net.sf.openrocket.gui.print.visitor;
|
||||
|
||||
@ -23,7 +23,7 @@ import java.util.Set;
|
||||
/**
|
||||
* A strategy for drawing fin templates.
|
||||
*/
|
||||
public class FinSetVisitorStrategy {
|
||||
public class FinSetPrintStrategy {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
@ -50,18 +50,18 @@ public class FinSetVisitorStrategy {
|
||||
*
|
||||
* @param doc The iText document
|
||||
* @param theWriter The direct iText writer
|
||||
* @param theStagesToVisit The stages to be visited by this strategy
|
||||
* @param theStages The stages to be printed by this strategy
|
||||
*/
|
||||
public FinSetVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
|
||||
public FinSetPrintStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStages) {
|
||||
document = doc;
|
||||
writer = theWriter;
|
||||
stages = theStagesToVisit;
|
||||
stages = theStages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurse through the given rocket component.
|
||||
*
|
||||
* @param root the root component; all children will be visited recursively
|
||||
* @param root the root component; all children will be printed recursively
|
||||
*/
|
||||
public void writeToDocument (final RocketComponent root) {
|
||||
List<RocketComponent> rc = root.getChildren();
|
||||
@ -72,12 +72,12 @@ public class FinSetVisitorStrategy {
|
||||
/**
|
||||
* Recurse through the given rocket component.
|
||||
*
|
||||
* @param theRc an array of rocket components; all children will be visited recursively
|
||||
* @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) {
|
||||
doVisit((FinSet)rocketComponent);
|
||||
printFinSet((FinSet) rocketComponent);
|
||||
}
|
||||
else if (rocketComponent.getChildCount() > 0) {
|
||||
goDeep(rocketComponent.getChildren());
|
||||
@ -86,14 +86,14 @@ public class FinSetVisitorStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* The core behavior of this visitor.
|
||||
* The core behavior of this strategy.
|
||||
*
|
||||
* @param visitable the object to extract info about; a graphical image of the fin shape is drawn to the document
|
||||
* @param finSet 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())) {
|
||||
private void printFinSet(final FinSet finSet) {
|
||||
if (shouldPrintStage(finSet.getStageNumber())) {
|
||||
try {
|
||||
PrintableFinSet pfs = new PrintableFinSet(visitable);
|
||||
PrintableFinSet pfs = new PrintableFinSet(finSet);
|
||||
|
||||
java.awt.Dimension finSize = pfs.getSize();
|
||||
final Dimension pageSize = getPageSize();
|
||||
@ -103,8 +103,9 @@ public class FinSetVisitorStrategy {
|
||||
else {
|
||||
BufferedImage image = (BufferedImage) pfs.createImage();
|
||||
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
|
||||
document, writer, image);
|
||||
document, writer, image);
|
||||
}
|
||||
document.newPage();
|
||||
}
|
||||
catch (DocumentException e) {
|
||||
log.error("Could not render fin.", e);
|
||||
@ -113,13 +114,13 @@ public class FinSetVisitorStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage.
|
||||
* Determine if the 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
|
||||
* @return true if the strategy contains the stage number provided
|
||||
*/
|
||||
public boolean shouldVisitStage (int stageNumber) {
|
||||
public boolean shouldPrintStage(int stageNumber) {
|
||||
if (stages == null || stages.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
@ -163,7 +164,6 @@ public class FinSetVisitorStrategy {
|
||||
Graphics2D g2 = cb.createGraphics(d.width, d.height);
|
||||
thePfs.print(g2);
|
||||
g2.dispose();
|
||||
document.newPage();
|
||||
}
|
||||
|
||||
/**
|
||||
205
src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java
Normal file
205
src/net/sf/openrocket/gui/print/visitor/TransitionStrategy.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user