package net.sf.openrocket.gui.main;
- import java.awt.Dimension;
- import java.awt.Font;
- import java.awt.Toolkit;
- import java.awt.Window;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.event.ComponentAdapter;
- import java.awt.event.ComponentEvent;
- import java.awt.event.KeyEvent;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.awt.event.MouseListener;
- import java.awt.event.WindowAdapter;
- import java.awt.event.WindowEvent;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.net.URL;
- import java.net.URLDecoder;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.concurrent.ExecutionException;
-
- import javax.swing.Action;
- import javax.swing.InputMap;
- import javax.swing.JButton;
- import javax.swing.JComponent;
- import javax.swing.JFileChooser;
- import javax.swing.JFrame;
- import javax.swing.JMenu;
- import javax.swing.JMenuBar;
- import javax.swing.JMenuItem;
- import javax.swing.JOptionPane;
- import javax.swing.JPanel;
- import javax.swing.JScrollPane;
- import javax.swing.JSeparator;
- import javax.swing.JSplitPane;
- import javax.swing.JTabbedPane;
- import javax.swing.JTextField;
- import javax.swing.KeyStroke;
- import javax.swing.ListSelectionModel;
- import javax.swing.ScrollPaneConstants;
- import javax.swing.SwingUtilities;
- import javax.swing.border.TitledBorder;
- import javax.swing.event.TreeSelectionEvent;
- import javax.swing.event.TreeSelectionListener;
- import javax.swing.filechooser.FileFilter;
- import javax.swing.tree.DefaultTreeSelectionModel;
- import javax.swing.tree.TreePath;
- import javax.swing.tree.TreeSelectionModel;
-
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.WarningSet;
-import net.sf.openrocket.communication.UpdateInfo;
-import net.sf.openrocket.communication.UpdateInfoRetriever;
-import net.sf.openrocket.database.Databases;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.file.RocketLoadException;
import net.sf.openrocket.gui.dialogs.ExampleDesignDialog;
import net.sf.openrocket.gui.dialogs.LicenseDialog;
import net.sf.openrocket.gui.dialogs.MotorDatabaseLoadingDialog;
+ import net.sf.openrocket.gui.dialogs.PrintDialog;
import net.sf.openrocket.gui.dialogs.SwingWorkerDialog;
-import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
import net.sf.openrocket.gui.dialogs.WarningDialog;
import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog;
+import net.sf.openrocket.gui.main.componenttree.ComponentTree;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.util.SaveFileWorker;
import net.sf.openrocket.util.TestRockets;
-import javax.swing.Timer;
-import javax.swing.ToolTipManager;
+ import javax.swing.Action;
+ import javax.swing.InputMap;
+ import javax.swing.JButton;
+ import javax.swing.JComponent;
+ import javax.swing.JFileChooser;
+ import javax.swing.JFrame;
+ import javax.swing.JMenu;
+ import javax.swing.JMenuBar;
+ import javax.swing.JMenuItem;
+ import javax.swing.JOptionPane;
+ import javax.swing.JPanel;
+ import javax.swing.JScrollPane;
+ import javax.swing.JSeparator;
+ import javax.swing.JSplitPane;
+ import javax.swing.JTabbedPane;
+ import javax.swing.JTextField;
+ import javax.swing.KeyStroke;
+ import javax.swing.ListSelectionModel;
+ import javax.swing.ScrollPaneConstants;
+ import javax.swing.SwingUtilities;
-import java.lang.reflect.InvocationTargetException;
+ import javax.swing.border.TitledBorder;
+ import javax.swing.event.TreeSelectionEvent;
+ import javax.swing.event.TreeSelectionListener;
+ import javax.swing.filechooser.FileFilter;
+ import javax.swing.tree.DefaultTreeSelectionModel;
+ import javax.swing.tree.TreePath;
+ import javax.swing.tree.TreeSelectionModel;
+ import java.awt.Dimension;
+ import java.awt.Font;
+ import java.awt.Toolkit;
+ import java.awt.Window;
+ import java.awt.event.ActionEvent;
+ import java.awt.event.ActionListener;
+ import java.awt.event.ComponentAdapter;
+ import java.awt.event.ComponentEvent;
+ import java.awt.event.KeyEvent;
+ import java.awt.event.MouseAdapter;
+ import java.awt.event.MouseEvent;
+ import java.awt.event.MouseListener;
+ import java.awt.event.WindowAdapter;
+ import java.awt.event.WindowEvent;
+ import java.io.File;
+ import java.io.FileNotFoundException;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.UnsupportedEncodingException;
+ import java.net.URI;
+ import java.net.URISyntaxException;
+ import java.net.URL;
+ import java.net.URLDecoder;
+ import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.LinkedList;
++import java.util.List;
+ import java.util.concurrent.ExecutionException;
+
public class BasicFrame extends JFrame {
private static final LogHelper log = Application.getLogger();
}
- /**
- * Closes this frame if it is replaceable.
- */
- public void closeIfReplaceable() {
- if (this.replaceable && document.isSaved()) {
- closeAction();
- }
- }
+ /**
+ *
+ */
+ public void printAction() {
+ new PrintDialog(document);
+ }
+
/**
* Open a new design window with a basic rocket+stage.
*/
--- /dev/null
- * DrawingsPrintable.java
+ /*
- Rocket duplicate = theRocket.copy();
++ * DesignReport.java
+ */
+ package net.sf.openrocket.gui.print;
+
+ import com.itextpdf.text.Document;
+ import com.itextpdf.text.DocumentException;
+ import com.itextpdf.text.Element;
+ import com.itextpdf.text.Paragraph;
+ import com.itextpdf.text.Rectangle;
+ import com.itextpdf.text.pdf.BaseFont;
+ import com.itextpdf.text.pdf.DefaultFontMapper;
+ import com.itextpdf.text.pdf.PdfContentByte;
+ import com.itextpdf.text.pdf.PdfPCell;
+ import com.itextpdf.text.pdf.PdfPTable;
+ import com.itextpdf.text.pdf.PdfWriter;
+ import net.sf.openrocket.document.OpenRocketDocument;
+ import net.sf.openrocket.document.Simulation;
+ import net.sf.openrocket.gui.figureelements.FigureElement;
+ import net.sf.openrocket.gui.figureelements.RocketInfo;
+ import net.sf.openrocket.gui.print.visitor.BaseVisitorStrategy;
+ import net.sf.openrocket.gui.print.visitor.MotorMountVisitorStrategy;
+ import net.sf.openrocket.gui.print.visitor.StageVisitorStrategy;
+ import net.sf.openrocket.gui.scalefigure.RocketPanel;
+ import net.sf.openrocket.motor.Motor;
+ import net.sf.openrocket.rocketcomponent.ComponentVisitor;
+ import net.sf.openrocket.rocketcomponent.Configuration;
+ import net.sf.openrocket.rocketcomponent.Rocket;
+ import net.sf.openrocket.simulation.FlightData;
+ import net.sf.openrocket.unit.Unit;
+ import net.sf.openrocket.unit.UnitGroup;
+ import net.sf.openrocket.util.Prefs;
+
+ import java.awt.Graphics2D;
+ import java.io.IOException;
+ import java.text.DecimalFormat;
+ import java.util.List;
+
+ /**
+ * <pre>
+ * # Title # Section describing the rocket in general without motors
+ * # Section describing the rocket in general without motors
+ * <p/>
+ * design name
+ * empty mass & CG
+ * CP position
+ * CP position at 5 degree AOA (or similar)
+ * number of stages
+ * parachute/streamer sizes
+ * max. diameter (caliber)
+ * velocity at exit of rail/rod
+ * minimum safe velocity reached in x inches/cm
+ * <p/>
+ * # Section for each motor configuration
+ * <p/>
+ * a summary of the motors, e.g. 3xC6-0; B4-6
+ * a list of the motors including the manufacturer, designation (maybe also info like burn time, grams of propellant,
+ * total impulse)
+ * total grams of propellant
+ * total impulse
+ * takeoff weight
+ * CG and CP position, stability margin
+ * predicted flight altitude, max. velocity and max. acceleration
+ * predicted velocity at chute deployment
+ * predicted descent rate
+ * Thrust to Weight Ratio of each stage
+ * <p/>
+ * </pre>
+ */
+ public class DesignReport extends BaseVisitorStrategy {
+
+ /**
+ * The OR Document.
+ */
+ private OpenRocketDocument rocketDocument;
+
+ /**
+ * A panel used for rendering of the design diagram.
+ */
+ final RocketPanel panel;
+
+ /**
+ * A stage visitor.
+ */
+ private StageVisitorStrategy svs = new StageVisitorStrategy();
+
+ /**
+ * Constructor.
+ *
+ * @param theRocDoc the OR document
+ * @param theIDoc the iText document
+ */
+ public DesignReport (OpenRocketDocument theRocDoc, Document theIDoc) {
+ super(theIDoc, null);
+ rocketDocument = theRocDoc;
+ panel = new RocketPanel(rocketDocument);
+ }
+
+ /**
+ * Main entry point. Prints the rocket drawing and design data.
+ *
+ * @param writer a direct byte writer
+ */
+ public void print (PdfWriter writer) {
+ if (writer == null) {
+ return;
+ }
+ com.itextpdf.text.Rectangle pageSize = document.getPageSize();
+ int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2;
+ int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop();
+
+ PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, "Rocket Design");
+
+ Rocket rocket = rocketDocument.getRocket();
+ final Configuration configuration = rocket.getDefaultConfiguration();
+ configuration.setAllStages();
+ PdfContentByte canvas = writer.getDirectContent();
+
+ final PrintFigure figure = new PrintFigure(configuration);
+
+ FigureElement cp = panel.getExtraCP();
+ FigureElement cg = panel.getExtraCG();
+ RocketInfo text = panel.getExtraText();
+
+ double scale = paintRocketDiagram(pageImageableWidth, pageImageableHeight, canvas, figure, cp, cg);
+
+ canvas.beginText();
+ try {
+ canvas.setFontAndSize(BaseFont.createFont(PrintUtilities.NORMAL.getFamilyname(), BaseFont.CP1252,
+ BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE);
+ }
+ catch (DocumentException e) {
+ e.printStackTrace();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS
+ .toPoints(1)));
+ final int diagramHeight = pageImageableHeight * 2 - 70 - (int) (figHeightPts);
+ canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight);
+ canvas.moveTextWithLeading(0, -16);
+
+ float initialY = canvas.getYTLM();
+
+ canvas.showText(rocketDocument.getRocket().getName());
+
+ canvas.newlineShowText("Stages: ");
+ canvas.showText("" + rocket.getStageCount());
+
+
+ if (configuration.hasMotors()) {
+ canvas.newlineShowText("Mass (with motor" + ((configuration.getStageCount() > 1) ? "s): " : "): "));
+ }
+ else {
+ canvas.newlineShowText("Mass (Empty): ");
+ }
+ canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit()));
+
+ canvas.newlineShowText("Stability: ");
+ canvas.showText(text.getStability());
+
+ canvas.newlineShowText("Cg: ");
+ canvas.showText(text.getCg());
+
+ canvas.newlineShowText("Cp: ");
+ canvas.showText(text.getCp());
+ canvas.endText();
+
+ try {
+ //Move the internal pointer of the document below that of what was just written using the direct byte buffer.
+ Paragraph paragraph = new Paragraph();
+ float finalY = canvas.getYTLM();
+ int heightOfDiagramAndText = (int) (pageSize.getHeight() - (finalY - initialY + diagramHeight));
+
+ paragraph.setSpacingAfter(heightOfDiagramAndText);
+ document.add(paragraph);
+
+ String[] mids = rocket.getMotorConfigurationIDs();
+
+ List<Double> stages = getStageWeights(rocket);
+
+
+ for (int j = 0; j < mids.length; j++) {
+ String mid = mids[j];
+ if (mid != null) {
+ MotorMountVisitorStrategy mmvs = new MotorMountVisitorStrategy(document, mid);
+ rocket.accept(new ComponentVisitor(mmvs));
+ PdfPTable parent = new PdfPTable(2);
+ parent.setWidthPercentage(100);
+ parent.setHorizontalAlignment(Element.ALIGN_LEFT);
+ parent.setSpacingBefore(0);
+ parent.setWidths(new int[]{1, 3});
+ int leading = 0;
+ //The first motor config is always null. Skip it and the top-most motor, then set the leading.
+ if (j > 1) {
+ leading = 25;
+ }
+ addFlightData(rocket, mid, parent, leading);
+ addMotorData(mmvs.getMotors(), parent, stages);
+ document.add(parent);
+ }
+ }
+ }
+ catch (DocumentException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Paint a diagram of the rocket into the PDF document.
+ *
+ * @param thePageImageableWidth the number of points in the width of the page available for drawing
+ * @param thePageImageableHeight the number of points in the height of the page available for drawing
+ * @param theCanvas the direct byte writer
+ * @param theFigure the print figure
+ * @param theCp the center of pressure figure element
+ * @param theCg the center of gravity figure element
+ *
+ * @return the scale of the diagram
+ */
+ private double paintRocketDiagram (final int thePageImageableWidth, final int thePageImageableHeight,
+ final PdfContentByte theCanvas, final PrintFigure theFigure,
+ final FigureElement theCp, final FigureElement theCg) {
+ theFigure.clearAbsoluteExtra();
+ theFigure.clearRelativeExtra();
+ theFigure.addRelativeExtra(theCp);
+ theFigure.addRelativeExtra(theCg);
+ theFigure.updateFigure();
+
+ double scale =
+ (thePageImageableWidth * 2.2) / theFigure.getFigureWidth();
+
+ theFigure.setScale(scale);
+ /*
+ * page dimensions are in points-per-inch, which, in Java2D, are the same as pixels-per-inch; thus we don't need any conversion
+ */
+ theFigure.setSize(thePageImageableWidth, thePageImageableHeight);
+ theFigure.updateFigure();
+
+
+ final DefaultFontMapper mapper = new DefaultFontMapper();
+ Graphics2D g2d = theCanvas.createGraphics(thePageImageableWidth, thePageImageableHeight * 2, mapper);
+ g2d.translate(20, 120);
+
+ g2d.scale(0.4d, 0.4d);
+ theFigure.paint(g2d);
+ g2d.dispose();
+ return scale;
+ }
+
+ /**
+ * Add the motor data for a motor configuration to the table.
+ *
+ * @param motors a motor configuration's list of motors
+ * @param parent the parent to which the motor data will be added
+ * @param stageWeights the stageWeights of each stage, in order
+ */
+ private void addMotorData (List<Motor> motors, final PdfPTable parent, List<Double> stageWeights) {
+
+ PdfPTable motorTable = new PdfPTable(8);
+ motorTable.setWidthPercentage(68);
+ motorTable.setHorizontalAlignment(Element.ALIGN_LEFT);
+
+ final PdfPCell motorCell = ITextHelper.createCell("Motor", PdfPCell.BOTTOM);
+ final int mPad = 10;
+ motorCell.setPaddingLeft(mPad);
+ motorTable.addCell(motorCell);
+ motorTable.addCell(ITextHelper.createCell("Avg Thrust", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Burn Time", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Max Thrust", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Total Impulse", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Thrust to Wt", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Propellant Wt", PdfPCell.BOTTOM));
+ motorTable.addCell(ITextHelper.createCell("Size", PdfPCell.BOTTOM));
+
+ DecimalFormat df = new DecimalFormat("#,##0.0#");
+ for (int i = 0; i < motors.size(); i++) {
+ int border = Rectangle.BOTTOM;
+ if (i == motors.size() - 1) {
+ border = Rectangle.NO_BORDER;
+ }
+ Motor motor = motors.get(i);
+ double motorWeight = (motor.getLaunchCG().weight - motor.getEmptyCG().weight) * 1000; //convert to grams
+
+ final PdfPCell motorVCell = ITextHelper.createCell(motor.getDesignation(), border);
+ motorVCell.setPaddingLeft(mPad);
+ motorTable.addCell(motorVCell);
+ motorTable.addCell(ITextHelper.createCell(df.format(motor.getAverageThrustEstimate()) + " " + UnitGroup
+ .UNITS_FORCE
+ .getDefaultUnit().toString(), border));
+ motorTable.addCell(ITextHelper.createCell(df.format(motor.getBurnTimeEstimate()) + " " + UnitGroup
+ .UNITS_FLIGHT_TIME
+ .getDefaultUnit().toString(), border));
+ motorTable.addCell(ITextHelper.createCell(df.format(motor.getMaxThrustEstimate()) + " " + UnitGroup
+ .UNITS_FORCE.getDefaultUnit()
+ .toString(), border));
+ motorTable.addCell(ITextHelper.createCell(df.format(motor.getTotalImpulseEstimate()) + " " + UnitGroup
+ .UNITS_IMPULSE
+ .getDefaultUnit().toString(), border));
+ double ttw = motor.getAverageThrustEstimate() / (getStageWeight(stageWeights, i) + (motor
+ .getLaunchCG().weight * 9.80665));
+ motorTable.addCell(ITextHelper.createCell(df.format(ttw) + ":1", border));
+
+ motorTable.addCell(ITextHelper.createCell(df.format(motorWeight) + " " + UnitGroup.UNITS_MASS
+ .getDefaultUnit().toString(), border));
+
+ final Unit motorUnit = UnitGroup.UNITS_MOTOR_DIMENSIONS
+ .getDefaultUnit();
+ motorTable.addCell(ITextHelper.createCell(motorUnit.toString(motor.getDiameter()) +
+ "/" +
+ motorUnit.toString(motor.getLength()) + " " +
+ motorUnit.toString(), border));
+ }
+ PdfPCell c = new PdfPCell(motorTable);
+ c.setBorder(PdfPCell.LEFT);
+ c.setBorderWidthTop(0f);
+ parent.addCell(c);
+ }
+
+
+ /**
+ * Add the motor data for a motor configuration to the table.
+ *
+ * @param theRocket the rocket
+ * @param mid a motor configuration id
+ * @param parent the parent to which the motor data will be added
+ * @param leading the number of points for the leading
+ */
+ private void addFlightData (final Rocket theRocket, final String mid, final PdfPTable parent, int leading) {
+ FlightData flight = null;
+ if (theRocket.getMotorConfigurationIDs().length > 1) {
++ Rocket duplicate= theRocket.copyWithOriginalID();
+ Simulation simulation = Prefs.getBackgroundSimulation(duplicate);
+ simulation.getConditions().setMotorConfigurationID(mid);
+
+ flight = PrintSimulationWorker.doit(simulation);
+
+ if (flight != null) {
+ try {
+ final Unit distanceUnit = UnitGroup.UNITS_DISTANCE.getDefaultUnit();
+ final Unit velocityUnit = UnitGroup.UNITS_VELOCITY.getDefaultUnit();
+ final Unit flightUnit = UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit();
+
+ PdfPTable labelTable = new PdfPTable(2);
+ labelTable.setWidths(new int[]{3, 2});
+ final Paragraph chunk = ITextHelper.createParagraph(stripBrackets(
+ theRocket.getMotorConfigurationNameOrDescription(mid)), PrintUtilities.BOLD);
+ chunk.setLeading(leading);
+ chunk.setSpacingAfter(3f);
+
+ document.add(chunk);
+
+ DecimalFormat df = new DecimalFormat("#,##0.0#");
+
+ final PdfPCell cell = ITextHelper.createCell("Altitude", 2, 2);
+ cell.setUseBorderPadding(false);
+ cell.setBorderWidthTop(0f);
+ labelTable.addCell(cell);
+ labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxAltitude()) + " " + distanceUnit,
+ 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell("Flight Time", 2, 2));
+ labelTable.addCell(ITextHelper.createCell(df.format(flight.getFlightTime()) + " " + flightUnit, 2,
+ 2));
+
+ labelTable.addCell(ITextHelper.createCell("Time to Apogee", 2, 2));
+ labelTable.addCell(ITextHelper.createCell(df.format(flight.getTimeToApogee()) + " " + flightUnit, 2,
+ 2));
+
+ labelTable.addCell(ITextHelper.createCell("Velocity off Pad", 2, 2));
+ labelTable.addCell(ITextHelper.createCell(df.format(
+ flight.getLaunchRodVelocity()) + " " + velocityUnit, 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell("Max Velocity", 2, 2));
+ labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxVelocity()) + " " + velocityUnit,
+ 2, 2));
+
+ labelTable.addCell(ITextHelper.createCell("Landing Velocity", 2, 2));
+ labelTable.addCell(ITextHelper.createCell(df.format(
+ flight.getGroundHitVelocity()) + " " + velocityUnit, 2, 2));
+
+ //Add the table to the parent; have to wrap it in a cell
+ PdfPCell c = new PdfPCell(labelTable);
+ c.setBorder(PdfPCell.RIGHT);
+ c.setBorderWidthTop(0);
+ c.setTop(0);
+ parent.addCell(c);
+ }
+ catch (DocumentException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Strip [] brackets from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with [] removed
+ */
+ private String stripBrackets (String target) {
+ return stripLeftBracket(stripRightBracket(target));
+ }
+
+ /**
+ * Strip [ from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with [ removed
+ */
+ private String stripLeftBracket (String target) {
+ return target.replace("[", "");
+ }
+
+ /**
+ * Strip ] from a string.
+ *
+ * @param target the original string
+ *
+ * @return target with ] removed
+ */
+ private String stripRightBracket (String target) {
+ return target.replace("]", "");
+ }
+
+ /**
+ * Use a visitor to get the sorted list of Stage references, then from those get the stage masses and convert to
+ * weight.
+ *
+ * @param rocket the rocket
+ *
+ * @return a sorted list of Stage weights (mass * gravity), in Newtons
+ */
+ private List<Double> getStageWeights (Rocket rocket) {
+ rocket.accept(new ComponentVisitor(svs));
+ svs.close();
+ List<Double> stages = svs.getStages();
+ for (int i = 0; i < stages.size(); i++) {
+ Double stage = stages.get(i);
+ stages.set(i, stage * 9.80665);
+ }
+ return stages;
+ }
+
+ /**
+ * Compute the total stage weight from a list of stage weights. This sums up the weight of the given stage plus all
+ * stages that sit atop it (depend upon it for thrust).
+ *
+ * @param weights the list of stage weights, in Newtons
+ * @param stage a stage number, 0 being topmost stage
+ *
+ * @return the total weight of the stage and all stages sitting atop the given stage, in Newtons
+ */
+ private double getStageWeight (List<Double> weights, int stage) {
+
+ double result = 0d;
+ for (int i = 0; i <= stage; i++) {
+ result += weights.get(i);
+ }
+ return result;
+ }
+ }
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
- /**
- * Check whether the given type can be added to this component. BodyComponents allow any
- * InternalComponents or ExternalComponents, excluding BodyComponents, to be added.
- *
- * @param type The RocketComponent class type to add.
- * @return Whether such a component can be added.
- */
@Override
- public boolean isCompatible(Class<? extends RocketComponent> type) {
- if (InternalComponent.class.isAssignableFrom(type))
- return true;
- if (ExternalComponent.class.isAssignableFrom(type) &&
- !BodyComponent.class.isAssignableFrom(type))
- return true;
- return false;
+ public boolean allowsChildren() {
+ return true;
}
-
+
+ /**
+ * Accept a visitor to this BodyComponent in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this BodyComponent
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
+
+
}
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
- public class BodyTube extends SymmetricComponent implements MotorMount {
+ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial {
-
- private double radius=0;
- private boolean autoRadius = false; // Radius chosen automatically based on parent component
+
+ private double radius = 0;
+ private boolean autoRadius = false; // Radius chosen automatically based on parent component
// When changing the inner radius, thickness is modified
this.thickness = thickness;
}
-
+
/************ Get/set component parameter methods ************/
-
+
/**
* Return the outer radius of the body tube.
+ *
+ * @return the outside radius of the tube
*/
- public double getRadius() {
+ @Override
+ public double getOuterRadius () {
if (autoRadius) {
// Return auto radius from front or rear
double r = -1;
* Set the outer radius of the body tube. If the radius is less than the wall thickness,
* the wall thickness is decreased accordingly of the value of the radius.
* This method sets the automatic radius off.
+ *
+ * @param radius the outside radius in standard units
*/
- public void setRadius(double radius) {
+ @Override
+ public void setOuterRadius (double radius) {
- if ((this.radius == radius) && (autoRadius==false))
+ if ((this.radius == radius) && (autoRadius == false))
return;
this.autoRadius = false;
@Override
- public double getAftRadius() {
- return getRadius();
- }
-
+ public double getAftRadius() { return getOuterRadius(); }
@Override
- public double getForeRadius() {
- return getRadius();
- }
-
+ public double getForeRadius() { return getOuterRadius(); }
@Override
- public boolean isAftRadiusAutomatic() { return isRadiusAutomatic(); }
- @Override
- public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
+ public boolean isAftRadiusAutomatic() {
+ return isRadiusAutomatic();
+ }
+ @Override
+ public boolean isForeRadiusAutomatic() {
+ return isRadiusAutomatic();
+ }
+
@Override
protected double getFrontAutoRadius() {
if (isRadiusAutomatic()) {
return -1;
}
}
- return getRadius();
+ return getOuterRadius();
}
-
+
@Override
protected double getRearAutoRadius() {
if (isRadiusAutomatic()) {
return -1;
}
}
- return getRadius();
+ return getOuterRadius();
}
+
+
-
+
+
+
+
+ @Override
public double getInnerRadius() {
if (filled)
return 0;
*/
@Override
public double getRadius(double x) {
- return getRadius();
+ return getOuterRadius();
}
-
+
/**
* Returns the inner radius at the position x. If the tube is filled, returns always zero.
*/
if (filled)
return 0.0;
else
- return Math.max(getRadius() - thickness, 0);
+ return Math.max(getOuterRadius()-thickness,0);
}
-
+
/**
* Returns the body tube's center of gravity.
*/
*/
@Override
public double getComponentVolume() {
- double r = getRadius();
+ double r = getOuterRadius();
if (filled)
- return getFilledVolume(r,length);
+ return getFilledVolume(r, length);
else
- return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
+ return getFilledVolume(r, length) - getFilledVolume(getInnerRadius(0), length);
}
@Override
public double getLongitudalUnitInertia() {
// 1/12 * (3 * (r1^2 + r2^2) + h^2)
- return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) + MathUtil.pow2(getLength())) / 12;
+ return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
+ MathUtil.pow2(getLength())) / 12;
}
-
+
@Override
public double getRotationalUnitInertia() {
// 1/2 * (r1^2 + r2^2)
- return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius())) / 2;
+ return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
}
-
-
+
+
/**
* Helper function for cylinder volume.
*/
@Override
public Collection<Coordinate> getComponentBounds() {
Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
- double r = getRadius();
+ double r = getOuterRadius();
- addBound(bounds,0,r);
- addBound(bounds,length,r);
+ addBound(bounds, 0, r);
+ addBound(bounds, length, r);
return bounds;
}
+
+
-
+ /**
+ * Check whether the given type can be added to this component. BodyTubes allow any
+ * InternalComponents or ExternalComponents, excluding BodyComponents, to be added.
+ *
+ * @param type The RocketComponent class type to add.
+ * @return Whether such a component can be added.
+ */
+ @Override
+ public boolean isCompatible(Class<? extends RocketComponent> type) {
+ if (InternalComponent.class.isAssignableFrom(type))
+ return true;
+ if (ExternalComponent.class.isAssignableFrom(type) &&
+ !BodyComponent.class.isAssignableFrom(type))
+ return true;
+ return false;
+ }
+
//////////////// Motor mount /////////////////
@Override
package net.sf.openrocket.rocketcomponent;
- import java.util.ArrayList;
- import java.util.Arrays;
-
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+
public class FreeformFinSet extends FinSet {
-
+ private static final LogHelper log = Application.getLogger();
+
private ArrayList<Coordinate> points = new ArrayList<Coordinate>();
public FreeformFinSet() {
package net.sf.openrocket.rocketcomponent;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
-
import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
}
-
+ /**
+ * Accept a visitor to an InnerTube object in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this InnerTube
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
-
+
+
/*
* (non-Javadoc)
* Copy the motor and ejection delay HashMaps.
import java.util.ArrayList;
import java.util.Collection;
- import net.sf.openrocket.util.Coordinate;
- import net.sf.openrocket.util.MathUtil;
- public class LaunchLug extends ExternalComponent {
-
+
+ public class LaunchLug extends ExternalComponent implements Coaxial {
+
private double radius;
private double thickness;
}
- public double getRadius() {
+ public double getOuterRadius () {
return radius;
}
-
+
- public void setRadius(double radius) {
+ public void setOuterRadius (double radius) {
if (MathUtil.equals(this.radius, radius))
return;
this.radius = radius;
this.thickness = Math.min(this.thickness, this.radius);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
+
public double getInnerRadius() {
- return radius-thickness;
+ return radius - thickness;
}
-
+
public void setInnerRadius(double innerRadius) {
- setRadius(innerRadius + thickness);
+ setOuterRadius(innerRadius + thickness);
}
-
+
public double getThickness() {
return thickness;
}
@Override
public double getLongitudalUnitInertia() {
// 1/12 * (3 * (r1^2 + r2^2) + h^2)
- return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) + MathUtil.pow2(getLength())) / 12;
+ return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
+ MathUtil.pow2(getLength())) / 12;
}
-
+
@Override
public double getRotationalUnitInertia() {
// 1/2 * (r1^2 + r2^2)
- return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius())) / 2;
+ return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
}
-
-
+
+ @Override
+ public boolean allowsChildren() {
+ return false;
+ }
+
@Override
public boolean isCompatible(Class<? extends RocketComponent> type) {
// Allow nothing to be attached to a LaunchLug
return false;
}
-
+
+ /**
+ * Accept a visitor to this LaunchLug in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this LaunchLug
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
+
}
package net.sf.openrocket.rocketcomponent;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.UUID;
-
- import javax.swing.event.ChangeListener;
- import javax.swing.event.EventListenerList;
-
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Chars;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.UniqueID;
+ import javax.swing.event.ChangeListener;
+ import javax.swing.event.EventListenerList;
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.LinkedList;
+ import java.util.List;
+ import java.util.UUID;
+
/**
* Base for all rocket components. This is the "starting point" for all rocket trees.
package net.sf.openrocket.rocketcomponent;
- import java.awt.Color;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.EmptyStackException;
- import java.util.Iterator;
- import java.util.List;
- import java.util.NoSuchElementException;
- import java.util.Stack;
-
- import javax.swing.event.ChangeListener;
-
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.logging.TraceException;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.UniqueID;
-import java.util.UUID;
+ import javax.swing.event.ChangeListener;
+ import java.awt.Color;
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.EmptyStackException;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.NoSuchElementException;
+ import java.util.Stack;
+
-public abstract class RocketComponent implements ChangeSource, Cloneable,
+public abstract class RocketComponent implements ChangeSource, Cloneable,
- Iterable<RocketComponent> {
+ Iterable<RocketComponent> , Visitable<ComponentVisitor, RocketComponent> {
-
+ private static final LogHelper log = Application.getLogger();
+
/*
* Text is suitable to the form
* Position relative to: <title>
// These must not fire any events, due to Rocket undo system initialization
this.name = getComponentName();
this.relativePosition = relativePosition;
- this.id = UUID.randomUUID().toString();
+ newID();
}
-
-
+ //////////// Methods that must be implemented ////////////
- //////////// Methods that must be implemented ////////////
-
-
/**
* Static component name. The name may not vary of the parameters, it must be static.
*/
clone.children.add(childCopy);
childCopy.parent = clone;
}
-
+
return clone;
}
-
-
+
+
+ /**
+ * Accept a visitor to this RocketComponent in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this RocketComponent
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
+
////////////// Methods that may not be overridden ////////////
-
+
////////// Common parameter setting/getting //////////
package net.sf.openrocket.rocketcomponent;
public class Stage extends ComponentAssembly {
-
+
- @Override
- public String getComponentName() {
- return "Stage";
- }
+ @Override
+ public String getComponentName () {
+ return "Stage";
+ }
-
-
+
+
+ @Override
+ public boolean allowsChildren() {
+ return true;
+ }
+
- /**
+ /**
- * Check whether the given type can be added to this component. A Stage allows only BodyComponents to be added.
+ * Check whether the given type can be added to this component. A Stage allows
+ * only BodyComponents to be added.
- *
- * @param type The RocketComponent class type to add.
- * @return Whether such a component can be added.
- */
- @Override
- public boolean isCompatible(Class<? extends RocketComponent> type) {
- return BodyComponent.class.isAssignableFrom(type);
- }
-
+ *
+ * @param type The RocketComponent class type to add.
+ *
+ * @return Whether such a component can be added.
+ */
+ @Override
+ public boolean isCompatible (Class<? extends RocketComponent> type) {
+ return BodyComponent.class.isAssignableFrom(type);
+ }
+
+ /**
+ * Accept a visitor to this Stage in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this Stage
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
}
/**
* Numerically solve clipLength from the equation
* r1 == type.getRadius(clipLength,r2,clipLength+length)
- * using a binary search. It assumes getRadius() to be monotonically increasing.
+ * using a binary search. It assumes getOuterRadius() to be monotonically increasing.
*/
private void calculateClip(double r1, double r2) {
- double min=0, max=length;
+ double min = 0, max = length;
if (r1 >= r2) {
- double tmp=r1;
+ double tmp = r1;
r1 = r2;
r2 = tmp;
}
clipLength = -1;
}
+ /**
+ * Accept a visitor to this Transition in the component hierarchy.
+ *
+ * @param theVisitor the visitor that will be called back with a reference to this Transition
+ */
+ @Override
+ public void accept (final ComponentVisitor theVisitor) {
+ theVisitor.visit(this);
+ }
+ /**
+ * Check whether the given type can be added to this component. Transitions allow any
+ * InternalComponents to be added.
+ *
+ * @param type The RocketComponent class type to add.
+ * @return Whether such a component can be added.
+ */
+ @Override
+ public boolean isCompatible(Class<? extends RocketComponent> type) {
+ if (InternalComponent.class.isAssignableFrom(type))
+ return true;
+ return false;
+ }
+
+
/**
* An enumeration listing the possible shapes of transitions.
*