From ccac17ad89dc4c8be8515eeb6d0451a49cb12810 Mon Sep 17 00:00:00 2001 From: rodinia814 Date: Sat, 5 Mar 2011 21:16:42 +0000 Subject: [PATCH] DGP - ripped out visitor; temp fix for Windows IAE git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@116 180e2498-e6e9-4542-8430-84ac67f01cd8 --- .../openrocket/gui/dialogs/PrintDialog.java | 33 +- .../sf/openrocket/gui/dialogs/PrintPanel.java | 46 +- .../sf/openrocket/gui/print/DesignReport.java | 196 ++- .../gui/print/PDFPrintStreamDoc.java | 33 +- .../sf/openrocket/gui/print/PaperSize.java | 131 +- .../openrocket/gui/print/PrintController.java | 38 +- .../gui/print/PrintServiceDialog.java | 562 +++++++++ .../openrocket/gui/print/PrintUtilities.java | 30 +- .../gui/print/PrintableContext.java | 24 +- .../print/visitor/BaseVisitorStrategy.java | 346 ------ .../print/visitor/FinSetVisitorStrategy.java | 142 ++- .../visitor/MotorMountVisitorStrategy.java | 100 -- .../visitor/PartsDetailVisitorStrategy.java | 1080 ++++++++++------- .../visitor/PartsListVisitorStrategy.java | 459 ++++--- .../print/visitor/StageVisitorStrategy.java | 260 ---- .../rocketcomponent/BodyComponent.java | 43 +- .../openrocket/rocketcomponent/BodyTube.java | 172 ++- .../rocketcomponent/CenteringRing.java | 34 +- .../rocketcomponent/ComponentVisitor.java | 178 --- .../ComponentVisitorStrategy.java | 136 --- .../rocketcomponent/EllipticalFinSet.java | 32 +- .../rocketcomponent/ExternalComponent.java | 63 +- .../rocketcomponent/FreeformFinSet.java | 144 +-- .../openrocket/rocketcomponent/InnerTube.java | 128 +- .../openrocket/rocketcomponent/LaunchLug.java | 94 +- .../rocketcomponent/MassObject.java | 68 +- .../openrocket/rocketcomponent/NoseCone.java | 33 +- .../rocketcomponent/RadiusRingComponent.java | 34 +- .../rocketcomponent/RingComponent.java | 98 +- .../sf/openrocket/rocketcomponent/Rocket.java | 351 +++--- .../rocketcomponent/RocketComponent.java | 721 ++++++----- .../sf/openrocket/rocketcomponent/Stage.java | 20 +- .../rocketcomponent/Transition.java | 272 ++--- .../rocketcomponent/TrapezoidFinSet.java | 43 +- 34 files changed, 2990 insertions(+), 3154 deletions(-) create mode 100644 src/net/sf/openrocket/gui/print/PrintServiceDialog.java delete mode 100644 src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java delete mode 100644 src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java delete mode 100644 src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java delete mode 100644 src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java delete mode 100644 src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java diff --git a/src/net/sf/openrocket/gui/dialogs/PrintDialog.java b/src/net/sf/openrocket/gui/dialogs/PrintDialog.java index a9769eed..f8ffed70 100644 --- a/src/net/sf/openrocket/gui/dialogs/PrintDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/PrintDialog.java @@ -5,31 +5,18 @@ package net.sf.openrocket.gui.dialogs; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.print.PDFPrintStreamDoc; +import net.sf.openrocket.gui.print.PrintServiceDialog; import net.sf.openrocket.gui.print.PrintUtilities; -import sun.print.ServiceDialog; -import javax.print.DocFlavor; -import javax.print.DocPrintJob; -import javax.print.PrintException; -import javax.print.PrintService; -import javax.print.PrintServiceLookup; +import javax.print.*; import javax.print.attribute.Attribute; import javax.print.attribute.AttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.Destination; import javax.print.attribute.standard.Fidelity; -import javax.swing.JDialog; -import javax.swing.JMenu; -import javax.swing.JTabbedPane; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dialog; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.HeadlessException; -import java.awt.Rectangle; +import javax.swing.*; +import java.awt.*; import java.io.ByteArrayOutputStream; /** @@ -41,7 +28,7 @@ public class PrintDialog { /** * The service UI dialog. */ - private ServiceDialog dialog; + private PrintServiceDialog dialog; /** * A javax doc flavor specific for printing PDF documents. @@ -65,12 +52,12 @@ public class PrintDialog { if (ps != null) { DocPrintJob dpj = ps.createPrintJob(); try { - System.err.println(attrs.size()); ByteArrayOutputStream baos = panel.generateReport(); dpj.print(new PDFPrintStreamDoc(baos, null), attrs); } catch (PrintException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); + //dgp } } } @@ -155,7 +142,7 @@ public class PrintDialog { getLocalGraphicsEnvironment().getDefaultScreenDevice(). getDefaultConfiguration().getBounds() : gc.getBounds(); - dialog = new ServiceDialog(gc, + dialog = new PrintServiceDialog(gc, x + gcBounds.x, y + gcBounds.y, services, defaultIndex, @@ -183,7 +170,7 @@ public class PrintDialog { dialog.setVisible(true); - if (dialog.getStatus() == ServiceDialog.APPROVE) { + if (dialog.getStatus() == PrintServiceDialog.APPROVE) { PrintRequestAttributeSet newas = dialog.getAttributes(); Class dstCategory = Destination.class; Class fdCategory = Fidelity.class; @@ -232,7 +219,7 @@ public class PrintDialog { /** * Removes any attributes from the given AttributeSet that are unsupported by the given PrintService/DocFlavor * combination. - * + * * @param ps the print service for which unsupported attributes will be determined * @param flavor the document flavor; PDF in our case * @param aset the set of attributes requested diff --git a/src/net/sf/openrocket/gui/dialogs/PrintPanel.java b/src/net/sf/openrocket/gui/dialogs/PrintPanel.java index ecd1fd8f..73aefd42 100644 --- a/src/net/sf/openrocket/gui/dialogs/PrintPanel.java +++ b/src/net/sf/openrocket/gui/dialogs/PrintPanel.java @@ -18,23 +18,13 @@ import net.sf.openrocket.startup.Application; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.MediaSizeName; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JColorChooser; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.UIManager; +import javax.swing.*; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import java.awt.Color; -import java.awt.Desktop; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; @@ -45,12 +35,18 @@ import java.util.Enumeration; import java.util.Iterator; /** - * This class isolates the Swing components used to create a panel that is added to the standard Java print dialog. + * This class isolates the Swing components used to create a panel that is added to a standard Java print dialog. */ public class PrintPanel extends JPanel implements TreeSelectionListener { private static final LogHelper log = Application.getLogger(); + private static final String TAB_TITLE = "Rocket"; + private static final String SETTINGS_BUTTON_TEXT = "Settings"; + private static final String PREVIEW_BUTTON_TEXT = "Preview"; + private static final String SAVE_AS_PDF_BUTTON_TEXT = "Save as PDF"; + private static final String SHOW_BY_STAGE = "Show By Stage"; + private final RocketPrintTree stagedTree; private final RocketPrintTree noStagedTree; private OpenRocketDocument rocDoc; @@ -61,7 +57,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { JButton previewButton; JButton saveAsPDF; - + /** * Constructor. * @@ -102,9 +98,9 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { currentTree = stagedTree; final JScrollPane scrollPane = new JScrollPane(stagedTree); - add(scrollPane, "width 475!, wrap"); + add(scrollPane, "width 416!, wrap"); - final JCheckBox sortByStage = new JCheckBox("Show By Stage"); + final JCheckBox sortByStage = new JCheckBox(SHOW_BY_STAGE); sortByStage.setEnabled(stages > 1); sortByStage.setSelected(stages > 1); sortByStage.addActionListener(new ActionListener() { @@ -125,7 +121,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { }); add(sortByStage, "wrap"); - saveAsPDF = new JButton("Save as PDF"); + saveAsPDF = new JButton(SAVE_AS_PDF_BUTTON_TEXT); saveAsPDF.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { @@ -134,7 +130,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { }); add(saveAsPDF, "span 2, tag save"); - previewButton = new JButton("Preview"); + previewButton = new JButton(PREVIEW_BUTTON_TEXT); previewButton.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { @@ -143,7 +139,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { }); add(previewButton, "x 150"); - JButton settingsButton = new JButton("Settings"); + JButton settingsButton = new JButton(SETTINGS_BUTTON_TEXT); settingsButton.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { @@ -151,7 +147,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { settingsDialog.setVisible(true); } }); - add(settingsButton, "x 400"); + add(settingsButton, "x 340"); expandAll(currentTree, true); if (currentTree != noStagedTree) { @@ -166,10 +162,10 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { * @return a title */ public String getTitle () { - return "Rocket"; + return TAB_TITLE; } - @Override + @Override public void valueChanged (final TreeSelectionEvent e) { final TreePath path = e.getNewLeadSelectionPath(); if (path != null){ @@ -190,7 +186,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { */ public void expandAll (RocketPrintTree theTree, boolean expand) { TreeNode root = (TreeNode) theTree.getModel().getRoot(); - // Traverse theTree from root + // Traverse theTree from root expandAll(theTree, new TreePath(root), expand); } @@ -204,7 +200,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { */ private void expandAll (RocketPrintTree theTree, TreePath parent, boolean expand) { theTree.addSelectionPath(parent); - // Traverse children + // Traverse children TreeNode node = (TreeNode) parent.getLastPathComponent(); if (node.getChildCount() >= 0) { for (Enumeration e = node.children(); e.hasMoreElements();) { @@ -213,7 +209,7 @@ public class PrintPanel extends JPanel implements TreeSelectionListener { expandAll(theTree, path, expand); } } - // Expansion or collapse must be done bottom-up + // Expansion or collapse must be done bottom-up if (expand) { theTree.expandPath(parent); } diff --git a/src/net/sf/openrocket/gui/print/DesignReport.java b/src/net/sf/openrocket/gui/print/DesignReport.java index 247881e8..8bc1f0fd 100644 --- a/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/src/net/sf/openrocket/gui/print/DesignReport.java @@ -3,37 +3,29 @@ */ 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.*; 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 com.itextpdf.text.pdf.*; 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.logging.LogHelper; 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.rocketcomponent.*; import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Prefs; -import java.awt.Graphics2D; +import java.awt.*; import java.io.IOException; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.List; /** @@ -67,7 +59,12 @@ import java.util.List; *

* */ -public class DesignReport extends BaseVisitorStrategy { +public class DesignReport { + + /** + * The logger. + */ + private static final LogHelper log = Application.getLogger(); /** * The OR Document. @@ -80,9 +77,34 @@ public class DesignReport extends BaseVisitorStrategy { final RocketPanel panel; /** - * A stage visitor. + * The iText document. */ - private StageVisitorStrategy svs = new StageVisitorStrategy(); + protected Document document; + + /** The displayed strings. */ + private static final String STAGES = "Stages: "; + private static final String MASS_WITH_MOTORS = "Mass (with motors): "; + private static final String MASS_WITH_MOTOR = "Mass (with motor): "; + private static final String MASS_EMPTY = "Mass (Empty): "; + private static final String STABILITY = "Stability: "; + private static final String CG = "Cg: "; + private static final String CP = "Cp: "; + private static final String MOTOR = "Motor"; + private static final String AVG_THRUST = "Avg Thrust"; + private static final String BURN_TIME = "Burn Time"; + private static final String MAX_THRUST = "Max Thrust"; + private static final String TOTAL_IMPULSE = "Total Impulse"; + private static final String THRUST_TO_WT = "Thrust to Wt"; + private static final String PROPELLANT_WT = "Propellant Wt"; + private static final String SIZE = "Size"; + private static final String ALTITUDE = "Altitude"; + private static final String FLIGHT_TIME = "Flight Time"; + private static final String TIME_TO_APOGEE = "Time to Apogee"; + private static final String VELOCITY_OFF_PAD = "Velocity off Pad"; + private static final String MAX_VELOCITY = "Max Velocity"; + private static final String LANDING_VELOCITY = "Landing Velocity"; + private static final String ROCKET_DESIGN = "Rocket Design"; + private static final double GRAVITY_CONSTANT = 9.80665d; /** * Constructor. @@ -91,7 +113,7 @@ public class DesignReport extends BaseVisitorStrategy { * @param theIDoc the iText document */ public DesignReport (OpenRocketDocument theRocDoc, Document theIDoc) { - super(theIDoc, null); + document = theIDoc; rocketDocument = theRocDoc; panel = new RocketPanel(rocketDocument); } @@ -101,7 +123,7 @@ public class DesignReport extends BaseVisitorStrategy { * * @param writer a direct byte writer */ - public void print (PdfWriter writer) { + public void writeToDocument (PdfWriter writer) { if (writer == null) { return; } @@ -109,7 +131,7 @@ public class DesignReport extends BaseVisitorStrategy { 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"); + PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN); Rocket rocket = rocketDocument.getRocket(); final Configuration configuration = rocket.getDefaultConfiguration(); @@ -130,10 +152,10 @@ public class DesignReport extends BaseVisitorStrategy { BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE); } catch (DocumentException e) { - e.printStackTrace(); + log.error("Could not set font.", e); } catch (IOException e) { - e.printStackTrace(); + log.error("Could not create font.", e); } int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS .toPoints(1))); @@ -145,25 +167,30 @@ public class DesignReport extends BaseVisitorStrategy { canvas.showText(rocketDocument.getRocket().getName()); - canvas.newlineShowText("Stages: "); + canvas.newlineShowText(STAGES); canvas.showText("" + rocket.getStageCount()); if (configuration.hasMotors()) { - canvas.newlineShowText("Mass (with motor" + ((configuration.getStageCount() > 1) ? "s): " : "): ")); + if (configuration.getStageCount() > 1) { + canvas.newlineShowText(MASS_WITH_MOTORS); + } + else { + canvas.newlineShowText(MASS_WITH_MOTOR); + } } else { - canvas.newlineShowText("Mass (Empty): "); + canvas.newlineShowText(MASS_EMPTY); } canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit())); - canvas.newlineShowText("Stability: "); + canvas.newlineShowText(STABILITY); canvas.showText(text.getStability()); - canvas.newlineShowText("Cg: "); + canvas.newlineShowText(CG); canvas.showText(text.getCg()); - canvas.newlineShowText("Cp: "); + canvas.newlineShowText(CP); canvas.showText(text.getCp()); canvas.endText(); @@ -180,12 +207,11 @@ public class DesignReport extends BaseVisitorStrategy { List 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)); + final List motorList = getMotorList(rocket, mid); + PdfPTable parent = new PdfPTable(2); parent.setWidthPercentage(100); parent.setHorizontalAlignment(Element.ALIGN_LEFT); @@ -197,14 +223,38 @@ public class DesignReport extends BaseVisitorStrategy { leading = 25; } addFlightData(rocket, mid, parent, leading); - addMotorData(mmvs.getMotors(), parent, stages); + addMotorData(motorList, parent, stages); document.add(parent); } } } catch (DocumentException e) { - e.printStackTrace(); + log.error("Could not modify document.", e); + } + } + + /** + * Get the motor list for all motor mounts. + * + * @param theRocket the rocket object + * @param theMid the motor id + * + * @return a list of Motor + */ + private List getMotorList (final Rocket theRocket, final String theMid) { + Iterator components = theRocket.deepIterator(); + final List motorList = new ArrayList(); + while (components.hasNext()) { + RocketComponent rocketComponent = components.next(); + if (rocketComponent instanceof MotorMount) { + MotorMount mm = (MotorMount) rocketComponent; + final Motor motor = mm.getMotor(theMid); + if (motor != null) { + motorList.add(motor); + } + } } + return motorList; } /** @@ -262,17 +312,17 @@ public class DesignReport extends BaseVisitorStrategy { motorTable.setWidthPercentage(68); motorTable.setHorizontalAlignment(Element.ALIGN_LEFT); - final PdfPCell motorCell = ITextHelper.createCell("Motor", PdfPCell.BOTTOM); + 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)); + 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++) { @@ -299,7 +349,7 @@ public class DesignReport extends BaseVisitorStrategy { .UNITS_IMPULSE .getDefaultUnit().toString(), border)); double ttw = motor.getAverageThrustEstimate() / (getStageWeight(stageWeights, i) + (motor - .getLaunchCG().weight * 9.80665)); + .getLaunchCG().weight * GRAVITY_CONSTANT)); motorTable.addCell(ITextHelper.createCell(df.format(ttw) + ":1", border)); motorTable.addCell(ITextHelper.createCell(df.format(motorWeight) + " " + UnitGroup.UNITS_MASS @@ -330,7 +380,7 @@ public class DesignReport extends BaseVisitorStrategy { 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(); + Rocket duplicate = theRocket.copyWithOriginalID(); Simulation simulation = Prefs.getBackgroundSimulation(duplicate); simulation.getConditions().setMotorConfigurationID(mid); @@ -353,30 +403,30 @@ public class DesignReport extends BaseVisitorStrategy { DecimalFormat df = new DecimalFormat("#,##0.0#"); - final PdfPCell cell = ITextHelper.createCell("Altitude", 2, 2); + 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(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(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(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(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(LANDING_VELOCITY, 2, 2)); labelTable.addCell(ITextHelper.createCell(df.format( flight.getGroundHitVelocity()) + " " + velocityUnit, 2, 2)); @@ -388,7 +438,7 @@ public class DesignReport extends BaseVisitorStrategy { parent.addCell(c); } catch (DocumentException e) { - e.printStackTrace(); + log.error("Could not add flight data to document.", e); } } } @@ -428,20 +478,48 @@ public class DesignReport extends BaseVisitorStrategy { } /** - * Use a visitor to get the sorted list of Stage references, then from those get the stage masses and convert to - * weight. + * From a list of Stages 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 getStageWeights (Rocket rocket) { - rocket.accept(new ComponentVisitor(svs)); - svs.close(); - List stages = svs.getStages(); + List stages = getStageMasses(rocket); + for (int i = 0; i < stages.size(); i++) { Double stage = stages.get(i); - stages.set(i, stage * 9.80665); + stages.set(i, stage * GRAVITY_CONSTANT); + } + return stages; + } + + /** + * From a list of Stages get the stage masses. + * + * @param rocket the rocket + * + * @return a sorted list of Stage masses + */ + private List getStageMasses (final Rocket rocket) { + Double mass = 0d; + + List stages = new ArrayList(); + Iterator iter = rocket.deepIterator(); + while (iter.hasNext()) { + RocketComponent rocketComponent = iter.next(); + if (rocketComponent instanceof Stage) { + if (mass > 0d) { + stages.add(mass); + mass = 0d; + } + } + else { + mass += rocketComponent.getMass(); + } + } + if (mass > 0d) { + stages.add(mass); } return stages; } diff --git a/src/net/sf/openrocket/gui/print/PDFPrintStreamDoc.java b/src/net/sf/openrocket/gui/print/PDFPrintStreamDoc.java index 4b6a56b9..90cf4610 100644 --- a/src/net/sf/openrocket/gui/print/PDFPrintStreamDoc.java +++ b/src/net/sf/openrocket/gui/print/PDFPrintStreamDoc.java @@ -7,19 +7,25 @@ import javax.print.Doc; import javax.print.DocFlavor; import javax.print.attribute.AttributeSetUtilities; import javax.print.attribute.DocAttributeSet; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; +import java.io.*; /** + * This class implements a javax Doc specifically for PDF printing. All reports in OpenRocket are PDF (iText) based. */ public class PDFPrintStreamDoc implements Doc { + /** The source stream of the PDF document. */ private InputStream stream; + + /** The document's attributes. */ private DocAttributeSet attributeSet; + /** + * Constructor. + * + * @param ostream an output stream representing the pdf doc + * @param attributes the attributes of the document + */ public PDFPrintStreamDoc (ByteArrayOutputStream ostream, DocAttributeSet attributes) { stream = new ByteArrayInputStream(ostream.toByteArray()); if (attributes != null) { @@ -27,10 +33,17 @@ public class PDFPrintStreamDoc implements Doc { } } + /** + * Flavor is PDF. + * + * @return PDF flavor + */ + @Override public DocFlavor getDocFlavor () { return DocFlavor.INPUT_STREAM.PDF; } + @Override public DocAttributeSet getAttributes () { return attributeSet; } @@ -38,11 +51,17 @@ public class PDFPrintStreamDoc implements Doc { /* Since the data is to be supplied as an InputStream delegate to * getStreamForBytes(). */ - + @Override public Object getPrintData () throws IOException { return getStreamForBytes(); } + /** + * Intentionally null since the flavor is PDF. + * + * @return null + */ + @Override public Reader getReaderForText () { return null; } @@ -50,7 +69,7 @@ public class PDFPrintStreamDoc implements Doc { /* Return the print data as an InputStream. * Always return the same instance. */ - + @Override public InputStream getStreamForBytes () throws IOException { return stream; } diff --git a/src/net/sf/openrocket/gui/print/PaperSize.java b/src/net/sf/openrocket/gui/print/PaperSize.java index e40d6f1c..63930e2a 100644 --- a/src/net/sf/openrocket/gui/print/PaperSize.java +++ b/src/net/sf/openrocket/gui/print/PaperSize.java @@ -12,27 +12,62 @@ import java.util.HashMap; import java.util.Map; /** - * Various mappings of paper sizes. + * Various mappings of paper sizes and their names. */ public class PaperSize { - + + /** Map of name to MediaSizeName instance. */ private static Map paperNames = new HashMap(); + /** Map of identifying name to displayable name. */ + private static Map displayableNames = new HashMap(); + /** Map of MediaSizeName to rectangle, which defines the paper size. */ private static Map paperItext = new HashMap(); - + + /** + * Init. + */ static { populateNameMap(); populateITextSizeMap(); + populateDisplayableNameMap(); } + + /** Disallow construction. */ private PaperSize() {} - + + /** + * Map an identifying paper name to it's corresponding MediaSizeName. + * + * @param name a paper name + * + * @return the associated MediaSizeName (or null if not found). + */ public static MediaSizeName convert(String name) { return paperNames.get(name); } + /** + * Map a MediaSizeName to it's size Rectangle. + * + * @param name a paper name + * + * @return a Rectangle or null + */ public static Rectangle convert(MediaSizeName name) { return paperItext.get(name); } - + + /** + * Map an identifying paper name to a displayable name (usually it's common name). + * + * @param name a paper name + * + * @return a displayable name + */ + public static String toDisplayable(String name) { + return displayableNames.get(name); + } + private static void populateNameMap() { paperNames.put("iso-a0", MediaSizeName.ISO_A0); paperNames.put("iso-a1", MediaSizeName.ISO_A1); @@ -124,5 +159,89 @@ public class PaperSize { paperItext.put(MediaSizeName.D, new RectangleReadOnly(PrintUnit.INCHES.toPoints(22), PrintUnit.INCHES.toPoints(34))); paperItext.put(MediaSizeName.E, new RectangleReadOnly(PrintUnit.INCHES.toPoints(34), PrintUnit.INCHES.toPoints(44))); } - + + /** + * Create a name map from standard to displayable names + */ + private static void populateDisplayableNameMap() { + displayableNames.put("iso-a0", "A0"); + displayableNames.put("iso-a1", "A1"); + displayableNames.put("iso-a2", "A2"); + displayableNames.put("iso-a3", "A3"); + displayableNames.put("iso-a4", "A4"); + displayableNames.put("iso-a5", "A5"); + displayableNames.put("iso-a6", "A6"); + displayableNames.put("iso-a7", "A7"); + displayableNames.put("iso-a8", "A8"); + displayableNames.put("iso-a9", "A9"); + displayableNames.put("iso-a10", "A10"); + displayableNames.put("iso-b0", "B0"); + displayableNames.put("iso-b1", "B1"); + displayableNames.put("iso-b2", "B2"); + displayableNames.put("iso-b3", "B3"); + displayableNames.put("iso-b4", "B4"); + displayableNames.put("iso-b5", "B5"); + displayableNames.put("iso-b6", "B6"); + displayableNames.put("iso-b7", "B7"); + displayableNames.put("iso-b8", "B8"); + displayableNames.put("iso-b9", "B9"); + displayableNames.put("iso-b10", "B10"); + displayableNames.put("na-letter", "US Letter"); + displayableNames.put("na-legal", "US Legal"); + displayableNames.put("na-8x10", "US 8x10 inch"); + displayableNames.put("na-5x7", "US 5x7 inch"); + displayableNames.put("executive", "Executive"); + displayableNames.put("folio", "Folio"); + displayableNames.put("invoice", "Invoice"); + displayableNames.put("tabloid", "Tabloid"); + displayableNames.put("ledger", "Ledger"); + displayableNames.put("quarto", "Quarto"); + displayableNames.put("iso-c0", "C0"); + displayableNames.put("iso-c1", "C1"); + displayableNames.put("iso-c2", "C2"); + displayableNames.put("iso-c3", "C3"); + displayableNames.put("iso-c4", "C4"); + displayableNames.put("iso-c5", "C5"); + displayableNames.put("iso-c6", "C6"); + displayableNames.put("iso-designated-long", "ISO Designated Long Size"); + displayableNames.put("jis-b0", "Japanese B0"); + displayableNames.put("jis-b1", "Japanese B1"); + displayableNames.put("jis-b2", "Japanese B2"); + displayableNames.put("jis-b3", "Japanese B3"); + displayableNames.put("jis-b4", "Japanese B4"); + displayableNames.put("jis-b5", "Japanese B5"); + displayableNames.put("jis-b6", "Japanese B6"); + displayableNames.put("jis-b7", "Japanese B7"); + displayableNames.put("jis-b8", "Japanese B8"); + displayableNames.put("jis-b9", "Japanese B9"); + displayableNames.put("jis-b10", "Japanese B10"); + displayableNames.put("a", "US Letter"); + displayableNames.put("b", "Engineering ANSI B"); + displayableNames.put("c", "Engineering ANSI C"); + displayableNames.put("d", "Engineering ANSI D"); + displayableNames.put("e", "Engineering ANSI E"); + displayableNames.put("arch-a", "Architectural A"); + displayableNames.put("arch-b", "Architectural B"); + displayableNames.put("arch-c", "Architectural C"); + displayableNames.put("arch-d", "Architectural D"); + displayableNames.put("arch-e", "Architectural E"); + displayableNames.put("japanese-postcard", "Japanese Postcard"); + displayableNames.put("oufuko-postcard", "Oufuko Postcard"); + displayableNames.put("italian-envelope", "Italian Envelope"); + displayableNames.put("personal-envelope", "Personal Envelope"); + displayableNames.put("na-number-11-envelope", "#11 Envelope"); + displayableNames.put("na-number-12-envelope", "#12 Envelope"); + displayableNames.put("na-number-14-envelope", "#14 Envelope"); + displayableNames.put("na-10x13-envelope", "10\"x13\" Envelope"); + displayableNames.put("na-9x12-envelope", "9\"x12\" Envelope"); + displayableNames.put("na-number-10-envelope", "#10 Envelope"); + displayableNames.put("na-7x9-envelope", "7\"x9\" Envelope"); + displayableNames.put("na-9x11-envelope", "9\"x11\" Envelope"); + displayableNames.put("na-10x14-envelope", "10\"x14\" Envelope"); + displayableNames.put("na-number-9-envelope", "#9 Envelope"); + displayableNames.put("na-6x9-envelope", "6\"x9\" Envelope"); + displayableNames.put("na-10x15-envelope", "10\"x15\" Envelope"); + displayableNames.put("monarch-envelope", "Monarch Envelope"); + } + } diff --git a/src/net/sf/openrocket/gui/print/PrintController.java b/src/net/sf/openrocket/gui/print/PrintController.java index 1cb64c43..00028871 100644 --- a/src/net/sf/openrocket/gui/print/PrintController.java +++ b/src/net/sf/openrocket/gui/print/PrintController.java @@ -14,9 +14,9 @@ import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.print.visitor.FinSetVisitorStrategy; import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy; -import net.sf.openrocket.rocketcomponent.ComponentVisitor; import javax.print.attribute.standard.MediaSizeName; +import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.Set; @@ -59,33 +59,23 @@ public class PrintController { switch (printableContext.getPrintable()) { case DESIGN_REPORT: DesignReport dp = new DesignReport(doc, idoc); - dp.print(writer); + dp.writeToDocument(writer); idoc.newPage(); break; case FIN_TEMPLATE: - final ComponentVisitor finVisitor = new ComponentVisitor(new FinSetVisitorStrategy(idoc, - writer, - stages)); - finVisitor.visit(doc.getRocket()); - finVisitor.close(); + final FinSetVisitorStrategy finWriter = new FinSetVisitorStrategy(idoc, + writer, + stages); + finWriter.writeToDocument(doc.getRocket()); break; case PARTS_DETAIL: - final ComponentVisitor detailVisitor = new ComponentVisitor(new PartsDetailVisitorStrategy(idoc, - writer, - stages)); - detailVisitor.visit(doc.getRocket()); + final PartsDetailVisitorStrategy detailVisitor = new PartsDetailVisitorStrategy(idoc, + writer, + stages); + detailVisitor.writeToDocument(doc.getRocket()); detailVisitor.close(); idoc.newPage(); break; - /* case PARTS_LIST: - final ComponentVisitor partsVisitor = new ComponentVisitor(new PartsListVisitorStrategy(idoc, - writer, - stages)); - partsVisitor.visit(doc.getRocket()); - partsVisitor.close(); - idoc.newPage(); - break; - */ } } //Stupid iText throws a really nasty exception if there is no data when close is called. @@ -99,16 +89,16 @@ public class PrintController { } catch (ExceptionConverter ec) { } -/* finally { - if (fileOutputStream != null) { + finally { + if (outputFile != null) { try { - fileOutputStream.close(); + outputFile.close(); } catch (IOException e) { } } } - */ + } private Rectangle convertWithDefault (final MediaSizeName msn) { diff --git a/src/net/sf/openrocket/gui/print/PrintServiceDialog.java b/src/net/sf/openrocket/gui/print/PrintServiceDialog.java new file mode 100644 index 00000000..98045f88 --- /dev/null +++ b/src/net/sf/openrocket/gui/print/PrintServiceDialog.java @@ -0,0 +1,562 @@ +/* + * PrintServiceDialog.java + * + */ +package net.sf.openrocket.gui.print; + +import net.miginfocom.swing.MigLayout; + +import javax.print.DocFlavor; +import javax.print.PrintService; +import javax.print.ServiceUIFactory; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttribute; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.PrintServiceAttribute; +import javax.print.attribute.standard.*; +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +public class PrintServiceDialog extends JDialog implements ActionListener { + + public static final int APPROVE = 1; + private JButton btnCancel, btnPrint; + private boolean pdfFlavorSupported = true; + private PrintService services[]; + private int defaultServiceIndex, status; + private PrintRequestAttributeSet asOriginal; + private HashPrintRequestAttributeSet asCurrent; + private PrintService psCurrent; + private DocFlavor docFlavor; + private GeneralPanel pnlGeneral; + private static final String GENERAL_TAB_TITLE = "General"; + private static final String PRINT_BUTTON_LABEL = "Print"; + private static final String CANCEL_BUTTON_LABEL = "Cancel"; + + private class MediaPanel extends JPanel + implements ItemListener { + + private static final String strTitle = "Media"; + private static final String SOURCE = "Source:"; + private static final String SIZE = "Size:"; + + private JLabel lblSize, lblSource; + private JComboBox cbSize, cbSource; + private ArrayList sizes; + private ArrayList sources; + + private String getMediaName (String s) { + String s1 = s.replace(' ', '-'); + s1 = s1.replace('#', 'n'); + return PaperSize.toDisplayable(s1); + } + + public void itemStateChanged (ItemEvent itemevent) { + Object obj = itemevent.getSource(); + if (itemevent.getStateChange() == 1) { + if (obj == cbSize) { + int i = cbSize.getSelectedIndex(); + if (i >= 0 && i < sizes.size()) { + if (cbSource.getItemCount() > 1 && cbSource.getSelectedIndex() >= 1) { + int k = cbSource.getSelectedIndex() - 1; + MediaTray mediatray = (MediaTray) sources.get(k); + asCurrent.add(new MediaWrapper(mediatray)); + } + asCurrent.add(sizes.get(i)); + } + } + else if (obj == cbSource) { + int j = cbSource.getSelectedIndex(); + if (j >= 1 && j < sources.size() + 1) { + asCurrent.remove(MediaWrapper.class); + asCurrent.add((MediaTray) sources.get(j - 1)); + } + else if (j == 0) { + asCurrent.remove(MediaWrapper.class); + if (cbSize.getItemCount() > 0) { + int l = cbSize.getSelectedIndex(); + asCurrent.add(sizes.get(l)); + } + } + } + } + } + + + public void updateInfo () { + boolean flag = false; + cbSize.removeItemListener(this); + cbSize.removeAllItems(); + cbSource.removeItemListener(this); + cbSource.removeAllItems(); + cbSource.addItem(getMediaName("auto-select")); + sizes.clear(); + sources.clear(); + if (psCurrent.isAttributeCategorySupported(Media.class)) { + flag = true; + Object obj = null; + try { + obj = psCurrent.getSupportedAttributeValues(Media.class, docFlavor, asCurrent); + } + catch (IllegalArgumentException iae) { + pdfFlavorSupported = false; + //dgp + } + if (obj instanceof Media[]) { + Media amedia[] = (Media[]) obj; + Set sizeSet = new TreeSet(); + + for (int i = 0; i < amedia.length; i++) { + Media media = amedia[i]; + if (media instanceof MediaSizeName) { + sizeSet.add(new SortableMediaSizeName((MediaSizeName) media)); + } + else if (media instanceof MediaTray) { + sources.add(media); + cbSource.addItem(getMediaName(media.toString())); + } + } + + //The set eliminates duplicates. + for (Iterator mediaSizeNameIterator = sizeSet.iterator(); mediaSizeNameIterator + .hasNext();) { + SortableMediaSizeName media = mediaSizeNameIterator.next(); + + sizes.add(media.getMediaSizeName()); + cbSize.addItem(media.toString()); + } + + } + } + boolean flag1 = flag && sizes.size() > 0; + lblSize.setEnabled(flag1); + cbSize.setEnabled(flag1); + cbSource.setEnabled(false); + lblSource.setEnabled(false); + if (flag) { + Media media = (Media) asCurrent.get(Media.class); + boolean attributeValueSupported = false; + try { + attributeValueSupported = psCurrent.isAttributeValueSupported(media, docFlavor, asCurrent); + } + catch (IllegalArgumentException iae) { + pdfFlavorSupported = false; + //dgp + } + if (media == null || !attributeValueSupported) { + media = (Media) psCurrent.getDefaultAttributeValue(Media.class); + if (media == null && sizes.size() > 0) { + media = sizes.get(0); + } + if (media != null) { + asCurrent.add(media); + } + } + if (media != null) { + if (media instanceof MediaSizeName) { + MediaSizeName mediasizename = (MediaSizeName) media; + cbSize.setSelectedIndex(sizes.indexOf(mediasizename)); + } + else if (media instanceof MediaTray) { + MediaTray mediatray = (MediaTray) media; + cbSource.setSelectedIndex(sources.indexOf(mediatray) + 1); + } + } + else { + cbSize.setSelectedIndex(sizes.size() <= 0 ? -1 : 0); + cbSource.setSelectedIndex(0); + } + int j = cbSize.getSelectedIndex(); + if (j >= 0 && j < sizes.size()) { + asCurrent.add(sizes.get(j)); + } + j = cbSource.getSelectedIndex(); + if (j >= 1 && j < sources.size() + 1) { + asCurrent.add((MediaTray) sources.get(j - 1)); + } + } + cbSize.addItemListener(this); + cbSource.addItemListener(this); + } + + public MediaPanel () { + super(new MigLayout("fill, gap rel unrel")); + sizes = new ArrayList(); + sources = new ArrayList(); + setBorder(BorderFactory.createTitledBorder(strTitle)); + cbSize = new JComboBox(); + cbSource = new JComboBox(); + lblSize = new JLabel(SIZE, 11); + lblSize.setDisplayedMnemonic(PrintServiceDialog.getMnemonic(SIZE)); + lblSize.setLabelFor(cbSize); + add(lblSize); + add(cbSize, "wrap"); + lblSource = new JLabel(SOURCE, 11); + lblSource.setDisplayedMnemonic(PrintServiceDialog.getMnemonic(SOURCE)); + lblSource.setLabelFor(cbSource); + add(lblSource); + add(cbSource); + } + + class SortableMediaSizeName implements Comparable { + MediaSizeName delegate; + + String displayableName; + + SortableMediaSizeName(MediaSizeName msn) { + delegate = msn; + displayableName = getMediaName(delegate.toString()); + if (displayableName == null) { + displayableName = delegate.toString(); + } + } + + /** + * Returns a string value corresponding to this enumeration value. + */ + @Override + public String toString () { + return displayableName; + } + + @Override + public int compareTo (final Object o) { + String name = displayableName; + if (name != null) { + return name.compareTo(o.toString()); + } + return 1; + } + + @Override + public boolean equals (final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final SortableMediaSizeName that = (SortableMediaSizeName) o; + + return displayableName.equals(that.displayableName); + } + + @Override + public int hashCode () { + return displayableName.hashCode(); + } + + MediaSizeName getMediaSizeName() { + return delegate; + } + } + } + + private class PrintServicePanel extends JPanel + implements ActionListener, ItemListener, PopupMenuListener { + + private final String strTitle = "Print Service"; + private JButton btnProperties; + private JComboBox cbName; + private JLabel lblType, lblStatus, lblInfo; + private JLabel vType, vStatus, vInfo; + private ServiceUIFactory uiFactory; + private boolean changedService; + + public PrintServicePanel () { + super(new MigLayout("fill, gap rel unrel")); + changedService = false; + uiFactory = psCurrent.getServiceUIFactory(); + setBorder(BorderFactory.createTitledBorder(strTitle)); + String as[] = new String[services.length]; + for (int i = 0; i < as.length; i++) { + as[i] = services[i].getName(); + } + + cbName = new JComboBox(as); + cbName.setSelectedIndex(defaultServiceIndex); + cbName.addItemListener(this); + cbName.addPopupMenuListener(this); + JLabel jlabel = new JLabel(("Name:"), 11); + jlabel.setDisplayedMnemonic(PrintServiceDialog.getMnemonic("Name")); + jlabel.setLabelFor(cbName); + add(jlabel); + add(cbName); + btnProperties = PrintServiceDialog.createButton("Properties...", this); + add(btnProperties, "wrap"); + lblStatus = new JLabel("Status:", 11); + add(lblStatus); + vStatus = new JLabel(); + add(vStatus, "wrap"); + lblType = new JLabel("Type:", 11); + vType = new JLabel(); + add(lblType); + add(vType, "wrap"); + lblInfo = new JLabel("Info:", 11); + vInfo = new JLabel(); + add(lblInfo); + add(vInfo, "wrap"); + } + + public void actionPerformed (ActionEvent actionevent) { + Object obj = actionevent.getSource(); + if (obj == btnProperties && uiFactory != null) { + JDialog jdialog = (JDialog) uiFactory.getUI(3, "javax.swing.JDialog"); + if (jdialog != null) { + jdialog.show(); + } + else { + btnProperties.setEnabled(false); + } + } + } + + public void itemStateChanged (ItemEvent itemevent) { + if (itemevent.getStateChange() == 1) { + int i = cbName.getSelectedIndex(); + if (i >= 0 && i < services.length && !services[i].equals(psCurrent)) { + psCurrent = services[i]; + uiFactory = psCurrent.getServiceUIFactory(); + changedService = true; + Destination destination = (Destination) asOriginal.get( + Destination.class); + if ((destination != null) && psCurrent.isAttributeCategorySupported( + Destination.class)) { + asCurrent.add(destination); + } + else { + asCurrent.remove(Destination.class); + } + } + } + } + + public void popupMenuWillBecomeVisible (PopupMenuEvent popupmenuevent) { + changedService = false; + } + + public void popupMenuWillBecomeInvisible (PopupMenuEvent popupmenuevent) { + if (changedService) { + changedService = false; + updatePanels(); + } + } + + public void popupMenuCanceled (PopupMenuEvent popupmenuevent) { + } + + public void updateInfo () { + PrintServiceAttribute psa = psCurrent.getAttribute(PrinterMakeAndModel.class); + if (psa != null) { + vType.setText(psa.toString()); + } + psa = psCurrent.getAttribute(PrinterIsAcceptingJobs.class); + if (psa != null) { + vStatus.setText((psa.toString())); + } + psa = psCurrent.getAttribute(PrinterInfo.class); + if (psa != null) { + vInfo.setText(psa.toString()); + } + btnProperties.setEnabled(uiFactory != null); + } + + } + + private class GeneralPanel extends JPanel { + + public void updateInfo () { + pnlPrintService.updateInfo(); + pnlMedia.updateInfo(); + } + + private PrintServicePanel pnlPrintService; + private MediaPanel pnlMedia; + + public GeneralPanel () { + super(new MigLayout("fill, gap rel unrel")); + pnlPrintService = new PrintServicePanel(); + add(pnlPrintService, "wrap"); + pnlMedia = new MediaPanel(); + add(pnlMedia, "wrap"); + } + } + + + public PrintServiceDialog (GraphicsConfiguration graphicsconfiguration, int i, int j, PrintService aprintservice[], + int k, DocFlavor docflavor, PrintRequestAttributeSet printrequestattributeset, + Dialog dialog, JPanel... additional) { + super(dialog, PRINT_BUTTON_LABEL, true, graphicsconfiguration); + initPrintDialog(i, j, aprintservice, k, docflavor, printrequestattributeset, additional); + } + + void initPrintDialog (int i, int j, PrintService aprintservice[], int k, DocFlavor docflavor, + PrintRequestAttributeSet printrequestattributeset, + JPanel... additional) { + services = aprintservice; + defaultServiceIndex = k; + asOriginal = printrequestattributeset; + asCurrent = new HashPrintRequestAttributeSet(printrequestattributeset); + psCurrent = aprintservice[k]; + docFlavor = docflavor; + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + final JTabbedPane tpTabs = new JTabbedPane(); + tpTabs.setBorder(new EmptyBorder(5, 5, 5, 5)); + + if (additional != null) { + for (JPanel anAdditional : additional) { + tpTabs.add(anAdditional, anAdditional.getName(), 0); + } + } + pnlGeneral = new GeneralPanel(); + tpTabs.add(GENERAL_TAB_TITLE, pnlGeneral); + + container.add(tpTabs, "Center"); + updatePanels(); + JPanel jpanel = new JPanel(new MigLayout()); + btnPrint = createExitButton(PRINT_BUTTON_LABEL, this); + jpanel.add(btnPrint, "x 300"); + getRootPane().setDefaultButton(btnPrint); + btnPrint.setEnabled(pdfFlavorSupported); + + btnCancel = createExitButton(CANCEL_BUTTON_LABEL, this); + handleEscKey(btnCancel); + jpanel.add(btnCancel, "x 380"); + container.add(jpanel, "South"); + addWindowListener(new WindowAdapter() { + public void windowClosing (WindowEvent windowevent) { + dispose(2); + } + } + ); + setResizable(false); + setLocation(i, j); + pack(); + } + + private void handleEscKey (JButton jbutton) { + AbstractAction abstractaction = new AbstractAction() { + + public void actionPerformed (ActionEvent actionevent) { + dispose(2); + } + + }; + KeyStroke keystroke = KeyStroke.getKeyStroke('\033', false); + InputMap inputmap = jbutton.getInputMap(2); + ActionMap actionmap = jbutton.getActionMap(); + if (inputmap != null && actionmap != null) { + inputmap.put(keystroke, "cancel"); + actionmap.put("cancel", abstractaction); + } + } + + public int getStatus () { + return status; + } + + public PrintRequestAttributeSet getAttributes () { + if (status == 1) { + return asCurrent; + } + else { + return asOriginal; + } + } + + public PrintService getPrintService () { + if (status == 1) { + return psCurrent; + } + else { + return null; + } + } + + public void dispose (int i) { + status = i; + super.dispose(); + } + + public void actionPerformed (ActionEvent actionevent) { + Object obj = actionevent.getSource(); + boolean flag = false; + if (obj == btnPrint) { + flag = true; + if (pnlGeneral != null) { + asCurrent.remove(Destination.class); + } + } + dispose(flag ? 1 : 2); + } + + + private void updatePanels () { + pnlGeneral.updateInfo(); + } + + private static char getMnemonic (String s) { + if (s != null && s.length() > 0) { + return s.charAt(0); + } + else { + return '\0'; + } + } + + private static JButton createButton (String s, ActionListener actionlistener) { + JButton jbutton = new JButton(s); + jbutton.setMnemonic(getMnemonic(s)); + jbutton.addActionListener(actionlistener); + return jbutton; + } + + private static JButton createExitButton (String s, ActionListener actionlistener) { + JButton jbutton = new JButton(s); + jbutton.addActionListener(actionlistener); + jbutton.getAccessibleContext().setAccessibleDescription(s); + return jbutton; + } + + static class MediaWrapper + implements PrintRequestAttribute { + + private Media media; + + MediaWrapper (Media theMedia) { + media = theMedia; + } + + Media getMedia () { + return media; + } + + public final Class getCategory () { + return this.getClass(); + } + + public final String getName () { + return "mw"; + } + + public String toString () { + return media.toString(); + } + + public int hashCode () { + return media.hashCode(); + } + + } + +} diff --git a/src/net/sf/openrocket/gui/print/PrintUtilities.java b/src/net/sf/openrocket/gui/print/PrintUtilities.java index b2bc5c53..a7efd907 100644 --- a/src/net/sf/openrocket/gui/print/PrintUtilities.java +++ b/src/net/sf/openrocket/gui/print/PrintUtilities.java @@ -4,11 +4,10 @@ package net.sf.openrocket.gui.print; -import com.itextpdf.text.Chunk; -import com.itextpdf.text.Document; -import com.itextpdf.text.DocumentException; +import com.itextpdf.text.*; import com.itextpdf.text.Font; -import com.itextpdf.text.Paragraph; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.startup.Application; import javax.print.DocFlavor; import javax.print.PrintService; @@ -17,18 +16,24 @@ import javax.print.ServiceUI; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.MediaSize; -import javax.swing.RepaintManager; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Graphics2D; +import javax.swing.*; +import java.awt.*; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.util.Locale; +/** + * Utilities methods and fonts used for printing. + */ public class PrintUtilities implements Printable { + /** + * The logger. + */ + private static final LogHelper log = Application.getLogger(); + public static final int NORMAL_FONT_SIZE = Font.DEFAULTSIZE - 3; public static final int SMALL_FONT_SIZE = NORMAL_FONT_SIZE - 3; @@ -127,6 +132,13 @@ public class PrintUtilities implements Printable { g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); } + /** + * Add text as a new paragraph in a given font to the document. + * + * @param document the document + * @param font the font + * @param title the title + */ public static void addText (Document document, com.itextpdf.text.Font font, String title) { Chunk sectionHeader = new Chunk(title); sectionHeader.setFont(font); @@ -136,7 +148,7 @@ public class PrintUtilities implements Printable { document.add(p); } catch (DocumentException e) { - e.printStackTrace(); + log.error("Could not add paragraph.", e); } } } diff --git a/src/net/sf/openrocket/gui/print/PrintableContext.java b/src/net/sf/openrocket/gui/print/PrintableContext.java index 6645b696..1f37435d 100644 --- a/src/net/sf/openrocket/gui/print/PrintableContext.java +++ b/src/net/sf/openrocket/gui/print/PrintableContext.java @@ -4,11 +4,7 @@ */ package net.sf.openrocket.gui.print; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; +import java.util.*; /** * Instances of this class are meant to keep track of what the user has selected to be printed. @@ -25,9 +21,14 @@ public class PrintableContext implements Comparable, Iterable< */ private OpenRocketPrintable printable; + /** + * Sort of a reverse map that tracks each type of printable item and the stages for which that item is to be printed. + */ private final Map> previous = new TreeMap>(); - + /** + * Constructor. + */ public PrintableContext () { } @@ -48,8 +49,13 @@ public class PrintableContext implements Comparable, Iterable< printable = thePrintable; } - public void add (final Integer theStageNumber, final OpenRocketPrintable thePrintable) - throws IllegalArgumentException { + /** + * Add a type of printable to a stage (number). + * + * @param theStageNumber the stage number + * @param thePrintable the printable to associate with the stage + */ + public void add (final Integer theStageNumber, final OpenRocketPrintable thePrintable) { Set stages = previous.get(thePrintable); if (stages == null) { stages = new TreeSet(); @@ -60,7 +66,7 @@ public class PrintableContext implements Comparable, Iterable< } } - + /** PrintableContext iterator. */ public Iterator iterator () { return new Iterator() { diff --git a/src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java deleted file mode 100644 index 8998a830..00000000 --- a/src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * BaseVisitorStrategy.java - */ -package net.sf.openrocket.gui.print.visitor; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.ComponentVisitor; -import net.sf.openrocket.rocketcomponent.ComponentVisitorStrategy; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; - -import com.itextpdf.text.Document; -import com.itextpdf.text.pdf.PdfWriter; - -/** - * This abstract class contains boilerplate functionality to support visiting the components of a rocket. It is a - * visitor strategy, not a visitor per se. - */ -public abstract class BaseVisitorStrategy implements ComponentVisitorStrategy { - - /** - * The owning visitor. - */ - protected ComponentVisitor parent; - - /** - * The iText document. - */ - protected Document document; - - /** - * The direct iText writer. - */ - protected PdfWriter writer; - - /** - * The stages selected. - */ - protected Set stages; - - /** - * State variable to track the level of hierarchy. - */ - protected int level = 0; - - /** - * Default no-arg constructor. - */ - public BaseVisitorStrategy() { - } - - /** - * Constructor. - * - * @param doc the iText document - */ - public BaseVisitorStrategy(Document doc) { - this(doc, null); - } - - /** - * Constructor. - * - * @param doc the iText document - * @param theWriter an iText byte writer - */ - public BaseVisitorStrategy(Document doc, PdfWriter theWriter) { - this(doc, theWriter, new HashSet()); - } - - /** - * Constructor. - * - * @param doc the iText document - * @param theWriter an iText byte writer - * @param theStages a set of stage numbers - */ - public BaseVisitorStrategy(Document doc, PdfWriter theWriter, Set theStages) { - document = doc; - writer = theWriter; - stages = theStages; - } - - /** - * Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage. - * - * @param stageNumber a stage number - * - * @return true if the visitor strategy contains the stage number provided - */ - public boolean shouldVisitStage(int stageNumber) { - if (stages == null || stages.isEmpty()) { - return false; - } - - for (final Integer stage : stages) { - if (stage == stageNumber) { - return true; - } - } - - return false; - } - - /** - * Recurse through the given rocket component. - * - * @param root the root component; all children will be visited recursively - */ - protected void goDeep(final RocketComponent root) { - List rc = root.getChildren(); - goDeep(rc); - } - - - /** - * Recurse through the given rocket component. - * - * @param theRc an array of rocket components; all children will be visited recursively - */ - protected void goDeep(final List theRc) { - level++; - for (RocketComponent rocketComponent : theRc) { - rocketComponent.accept(parent); - } - level--; - } - - /** - * Get the dimensions of the paper page. - * - * @return an internal Dimension - */ - protected Dimension getPageSize() { - return new Dimension(document.getPageSize().getWidth(), - document.getPageSize().getHeight()); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Rocket visitable) { - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RocketComponent visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Stage visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final ExternalComponent visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyComponent visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RingComponent visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final InnerTube visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final LaunchLug visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Transition visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RadiusRingComponent visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final MassObject visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final NoseCone visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyTube visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final TrapezoidFinSet visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final EllipticalFinSet visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final FreeformFinSet visitable) { - if (shouldVisitStage(visitable.getStageNumber())) { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void setParent(final ComponentVisitor theParent) { - parent = theParent; - } - - /** - * {@inheritDoc} - */ - @Override - public void close() { - } -} - -class Dimension { - public float width; - public float height; - - public Dimension(float w, float h) { - width = w; - height = h; - } - - public float getWidth() { - return width; - } - - public float getHeight() { - return height; - } -} \ No newline at end of file diff --git a/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java index 112ddb86..a079bb80 100644 --- a/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java +++ b/src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java @@ -10,19 +10,40 @@ import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.gui.print.ITextHelper; import net.sf.openrocket.gui.print.PrintableFinSet; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.image.BufferedImage; +import java.util.List; import java.util.Set; /** - * A visitor strategy for drawing fin templates. + * A strategy for drawing fin templates. */ -public class FinSetVisitorStrategy extends BaseVisitorStrategy { +public class FinSetVisitorStrategy { + + /** + * The logger. + */ + private static final LogHelper log = Application.getLogger(); + + /** + * The iText document. + */ + protected Document document; + + /** + * The direct iText writer. + */ + protected PdfWriter writer; + + /** + * The stages selected. + */ + protected Set stages; /** * Constructor. @@ -32,31 +53,36 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy { * @param theStagesToVisit The stages to be visited by this strategy */ public FinSetVisitorStrategy (Document doc, PdfWriter theWriter, Set theStagesToVisit) { - super(doc, theWriter, theStagesToVisit); + document = doc; + writer = theWriter; + stages = theStagesToVisit; } /** - * {@inheritDoc} + * Recurse through the given rocket component. + * + * @param root the root component; all children will be visited recursively */ - @Override - public void visit (final TrapezoidFinSet visitable) { - doVisit(visitable); + public void writeToDocument (final RocketComponent root) { + List rc = root.getChildren(); + goDeep(rc); } - /** - * {@inheritDoc} - */ - @Override - public void visit (final EllipticalFinSet visitable) { - doVisit(visitable); - } /** - * {@inheritDoc} + * Recurse through the given rocket component. + * + * @param theRc an array of rocket components; all children will be visited recursively */ - @Override - public void visit (final FreeformFinSet visitable) { - doVisit(visitable); + protected void goDeep (final List theRc) { + for (RocketComponent rocketComponent : theRc) { + if (rocketComponent instanceof FinSet) { + doVisit((FinSet)rocketComponent); + } + else if (rocketComponent.getChildCount() > 0) { + goDeep(rocketComponent.getChildren()); + } + } } /** @@ -81,11 +107,32 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy { } } catch (DocumentException e) { - e.printStackTrace(); + log.error("Could not render fin.", e); } } } + /** + * Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage. + * + * @param stageNumber a stage number + * + * @return true if the visitor strategy contains the stage number provided + */ + public boolean shouldVisitStage (int stageNumber) { + if (stages == null || stages.isEmpty()) { + return false; + } + + for (final Integer stage : stages) { + if (stage == stageNumber) { + return true; + } + } + + return false; + } + /** * Determine if the image will fit on the given page. * @@ -118,5 +165,52 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy { g2.dispose(); document.newPage(); } -} + /** + * Get the dimensions of the paper page. + * + * @return an internal Dimension + */ + protected Dimension getPageSize () { + return new Dimension(document.getPageSize().getWidth(), + document.getPageSize().getHeight()); + } + + /** + * Convenience class to model a dimension. + */ + class Dimension { + /** Width, in points. */ + public float width; + /** Height, in points. */ + public float height; + + /** + * Constructor. + * @param w width + * @param h height + */ + public Dimension (float w, float h) { + width = w; + height = h; + } + + /** + * Get the width. + * + * @return the width + */ + public float getWidth () { + return width; + } + + /** + * Get the height. + * + * @return the height + */ + public float getHeight () { + return height; + } + } +} diff --git a/src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java deleted file mode 100644 index 579cb2da..00000000 --- a/src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * MotorMountVisitor.java - */ -package net.sf.openrocket.gui.print.visitor; - -import com.itextpdf.text.Document; -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.MotorMount; - -import java.util.ArrayList; -import java.util.List; - -/** - * A visitor strategy for finding data about motor configurations. This visitor accumulates information about the - * motors currently 'installed' into each motor mount in the rocket. When the visitor is complete, invoke {@link - * #getMotors()} to obtain the list of Motor instances that correspond to the motor configuration. - */ -public class MotorMountVisitorStrategy extends BaseVisitorStrategy { - - /** - * The motor configuration identifier. - */ - private String mid; - - /** The accumulating list of motors. */ - private List motors = new ArrayList(); - - /** - * Constructor. - * - * @param doc the iText document - * @param motorConfigID the motor configuration ID - */ - public MotorMountVisitorStrategy (Document doc, - String motorConfigID) { - super(doc); - mid = motorConfigID; - } - - /** - * Override the method that determines if the visiting should be going deep. - * - * @param stageNumber a stage number - * - * @return true, always - */ - public boolean shouldVisitStage (int stageNumber) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void visit (final BodyTube visitable) { - if (visitable.isMotorMount()) { - doVisit(visitable); - } - else { - goDeep(visitable); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void visit (final InnerTube visitable) { - if (visitable.isMotorMount()) { - doVisit(visitable); - } - } - - /** - * The core behavior of this visitor. - * - * @param visitable the object to extract info about; a graphical image of the fin shape is drawn to the document - */ - private void doVisit (final MotorMount visitable) { - final Motor motor = visitable.getMotor(mid); - if (motor != null) { - motors.add(motor); - } - } - - /** - * Answer with the list of motors that have been accumulated from visiting all of the motor mount components in the - * rocket component hierarchy. - * - * @return a list of motors - */ - public List getMotors () { - return motors; - } - -} - - diff --git a/src/net/sf/openrocket/gui/print/visitor/PartsDetailVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/PartsDetailVisitorStrategy.java index 6151149c..cdeb9247 100644 --- a/src/net/sf/openrocket/gui/print/visitor/PartsDetailVisitorStrategy.java +++ b/src/net/sf/openrocket/gui/print/visitor/PartsDetailVisitorStrategy.java @@ -3,452 +3,662 @@ */ package net.sf.openrocket.gui.print.visitor; -import java.io.IOException; -import java.text.NumberFormat; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import javax.swing.ImageIcon; - +import com.itextpdf.text.*; +import com.itextpdf.text.pdf.PdfPCell; +import com.itextpdf.text.pdf.PdfPTable; +import com.itextpdf.text.pdf.PdfWriter; import net.sf.openrocket.gui.main.ComponentIcons; import net.sf.openrocket.gui.print.ITextHelper; import net.sf.openrocket.gui.print.PrintUtilities; import net.sf.openrocket.gui.print.PrintableFinSet; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.material.Material; -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.Coaxial; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.*; +import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; -import com.itextpdf.text.BadElementException; -import com.itextpdf.text.Chunk; -import com.itextpdf.text.Document; -import com.itextpdf.text.DocumentException; -import com.itextpdf.text.Element; -import com.itextpdf.text.Font; -import com.itextpdf.text.Image; -import com.itextpdf.text.Phrase; -import com.itextpdf.text.Rectangle; -import com.itextpdf.text.pdf.PdfPCell; -import com.itextpdf.text.pdf.PdfPTable; -import com.itextpdf.text.pdf.PdfWriter; +import javax.swing.*; +import java.text.NumberFormat; +import java.util.Collection; +import java.util.List; +import java.util.Set; /** * A visitor strategy for creating documentation about parts details. */ -public class PartsDetailVisitorStrategy extends BaseVisitorStrategy { - - /** - * The number of columns in the table. - */ - private static final int TABLE_COLUMNS = 7; - - /** - * The parts detail is represented as an iText table. - */ - PdfPTable grid; - - /** - * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts. - * - * @param doc The iText document - * @param theWriter The direct iText writer - * @param theStagesToVisit The stages to be visited by this strategy - */ - public PartsDetailVisitorStrategy(Document doc, PdfWriter theWriter, Set theStagesToVisit) { - super(doc, theWriter, theStagesToVisit); - PrintUtilities.addText(doc, PrintUtilities.BIG_BOLD, "Parts Detail"); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Stage visitable) { - try { - if (grid != null) { - document.add(grid); - } - document.add(ITextHelper.createPhrase(visitable.getName())); - grid = new PdfPTable(TABLE_COLUMNS); - grid.setWidthPercentage(100); - grid.setHorizontalAlignment(Element.ALIGN_LEFT); - } catch (DocumentException e) { - } - - List rc = visitable.getChildren(); - goDeep(rc); - } - - - /** - * {@inheritDoc} - */ - @Override - public void visit(final ExternalComponent visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(ITextHelper.createCell()); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyComponent visitable) { - grid.addCell(visitable.getName()); - grid.completeRow(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RingComponent visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(createOuterDiaCell(visitable)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final InnerTube visitable) { - grid.addCell(iconToImage(visitable)); - final PdfPCell pCell = createNameCell(visitable.getName(), true); - grid.addCell(pCell); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(createOuterDiaCell(visitable)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final LaunchLug visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(createOuterDiaCell(visitable)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Transition visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - - Chunk fore = new Chunk("Fore Dia: " + appendLength(visitable.getForeRadius() * 2)); - fore.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - Chunk aft = new Chunk("Aft Dia: " + appendLength(visitable.getAftRadius() * 2)); - aft.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - final PdfPCell cell = ITextHelper.createCell(); - cell.addElement(fore); - cell.addElement(aft); - grid.addCell(cell); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RadiusRingComponent visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(createOuterDiaCell(visitable)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final MassObject visitable) { - PdfPCell cell = ITextHelper.createCell(); - cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); - cell.setPaddingBottom(12f); - - grid.addCell(iconToImage(visitable)); - final PdfPCell nameCell = createNameCell(visitable.getName(), true); - nameCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); - nameCell.setPaddingBottom(12f); - grid.addCell(nameCell); - grid.addCell(cell); - grid.addCell(cell); - grid.addCell(cell); - grid.addCell(createMassCell(visitable.getMass())); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final NoseCone visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(ITextHelper.createCell(visitable.getType().getName(), PdfPCell.BOTTOM)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyTube visitable) { - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName(), true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(createOuterDiaCell(visitable)); - grid.addCell(createLengthCell(visitable.getLength())); - grid.addCell(createMassCell(visitable.getMass())); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final TrapezoidFinSet visitable) { - visitFins(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final EllipticalFinSet visitable) { - visitFins(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final FreeformFinSet visitable) { - visitFins(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void close() { - try { - if (grid != null) { - document.add(grid); - } - } catch (DocumentException e) { - e.printStackTrace(); - } - } - - private PdfPCell createOuterDiaCell(final Coaxial visitable) { - PdfPCell result = new PdfPCell(); - Phrase p = new Phrase(); - p.setLeading(12f); - result.setVerticalAlignment(Element.ALIGN_TOP); - result.setBorder(Rectangle.BOTTOM); - Chunk c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - c.append("Dia"); - p.add(c); - - c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); - c.append("out"); - p.add(c); - - c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - c.append(" " + appendLength(visitable.getOuterRadius() * 2)); - p.add(c); - createInnerDiaCell(visitable, result); - result.addElement(p); - return result; - } - - private void createInnerDiaCell(final Coaxial visitable, PdfPCell cell) { - Phrase p = new Phrase(); - p.setLeading(14f); - Chunk c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - c.append("Dia"); - p.add(c); - - c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); - c.append("in "); - p.add(c); - - c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - c.append(" " + appendLength(visitable.getInnerRadius() * 2)); - p.add(c); - cell.addElement(p); - } - - private void visitFins(FinSet visitable) { - - Image img = null; - java.awt.Image awtImage = new PrintableFinSet(visitable).createImage(); - - Collection x = visitable.getComponentBounds(); - - try { - img = Image.getInstance(writer, awtImage, 0.25f); - } catch (BadElementException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - grid.addCell(iconToImage(visitable)); - grid.addCell(createNameCell(visitable.getName() + " (" + visitable.getFinCount() + ")", true)); - grid.addCell(createMaterialCell(visitable.getMaterial())); - grid.addCell(ITextHelper.createCell("Thick: " + appendLength(visitable.getThickness()), PdfPCell.BOTTOM)); - final PdfPCell pCell = new PdfPCell(); - pCell.setBorder(Rectangle.BOTTOM); - pCell.addElement(img); - - grid.addCell(ITextHelper.createCell()); - grid.addCell(createMassCell(visitable.getMass())); - - List rc = visitable.getChildren(); - goDeep(rc); - } - - protected PdfPCell createLengthCell(double length) { - return ITextHelper.createCell("Len: " + appendLength(length), PdfPCell.BOTTOM); - } - - protected PdfPCell createMassCell(double mass) { - return ITextHelper.createCell("Mass: " + appendMass(mass), PdfPCell.BOTTOM); - } - - protected PdfPCell createNameCell(String v, boolean withIndent) { - PdfPCell result = new PdfPCell(); - result.setBorder(Rectangle.BOTTOM); - Chunk c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - if (withIndent) { - for (int x = 0; x < (level - 2) * 10; x++) { - c.append(" "); - } - } - c.append(v); - result.setColspan(2); - result.addElement(c); - return result; - } - - protected PdfPCell createMaterialCell(Material material) { - PdfPCell cell = ITextHelper.createCell(); - cell.setLeading(13f, 0); - - Chunk c = new Chunk(); - c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); - c.append(appendMaterial(material)); - cell.addElement(c); - Chunk density = new Chunk(); - density.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); - density.append(appendMaterialDensity(material)); - cell.addElement(density); - return cell; - } - - protected PdfPCell iconToImage(final RocketComponent visitable) { - final ImageIcon icon = (ImageIcon) ComponentIcons.getLargeIcon(visitable.getClass()); - try { - Image im = Image.getInstance(icon.getImage(), null); - im.scaleToFit(icon.getIconWidth() * 0.6f, icon.getIconHeight() * 0.6f); - PdfPCell cell = new PdfPCell(im); - cell.setFixedHeight(icon.getIconHeight() * 0.6f); - cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); - cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); - cell.setBorder(PdfPCell.NO_BORDER); - return cell; - } catch (BadElementException e) { - } catch (IOException e) { - } - return null; - } - - protected String appendLength(double length) { - final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); - return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString(); - } - - protected String appendMass(double mass) { - final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit(); - return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString(); - } - - protected String appendMaterial(Material material) { - return material.getName(); - } - - protected String appendMaterialDensity(Material material) { - return " (" + material.getType().getUnitGroup().getDefaultUnit().toStringUnit(material.getDensity()) + ")"; - } - +public class PartsDetailVisitorStrategy { + + /** + * The logger. + */ + private static final LogHelper log = Application.getLogger(); + + /** + * The number of columns in the table. + */ + private static final int TABLE_COLUMNS = 7; + + /** + * The parts detail is represented as an iText table. + */ + PdfPTable grid; + + /** + * The iText document. + */ + protected Document document; + + /** + * The direct iText writer. + */ + protected PdfWriter writer; + + /** + * The stages selected. + */ + protected Set stages; + + /** + * State variable to track the level of hierarchy. + */ + protected int level = 0; + + private static final String LINES = "Lines: "; + private static final String MASS = "Mass: "; + private static final String LEN = "Len: "; + private static final String THICK = "Thick: "; + private static final String INNER = "in "; + private static final String DIAMETER = "Dia"; + private static final String OUTER = "out"; + private static final String WIDTH = "Width"; + private static final String LENGTH = "Length"; + private static final String SHROUD_LINES = "Shroud Lines"; + private static final String AFT_DIAMETER = "Aft Dia: "; + private static final String FORE_DIAMETER = "Fore Dia: "; + private static final String PARTS_DETAIL = "Parts Detail"; + + /** + * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts. + * + * @param doc The iText document + * @param theWriter The direct iText writer + * @param theStagesToVisit The stages to be visited by this strategy + */ + public PartsDetailVisitorStrategy (Document doc, PdfWriter theWriter, Set theStagesToVisit) { + document = doc; + writer = theWriter; + stages = theStagesToVisit; + PrintUtilities.addText(doc, PrintUtilities.BIG_BOLD, PARTS_DETAIL); + } + + /** + * Print the parts detail. + * + * @param root the root component + */ + public void writeToDocument (final RocketComponent root) { + goDeep(root.getChildren()); + } + + /** + * Recurse through the given rocket component. + * + * @param theRc an array of rocket components; all children will be visited recursively + */ + protected void goDeep (final List theRc) { + level++; + for (RocketComponent rocketComponent : theRc) { + handle(rocketComponent); + } + level--; + } + + /** + * Add a line to the detail report based upon the type of the component. + * + * @param component the component to print the detail for + */ + private void handle (RocketComponent component) { + //This ugly if-then-else construct is not object oriented. Originally it was an elegant, and very OO savy, design + //using the Visitor pattern. Unfortunately, it was misunderstood and was removed. + if (component instanceof Stage) { + try { + if (grid != null) { + document.add(grid); + } + document.add(ITextHelper.createPhrase(component.getName())); + grid = new PdfPTable(TABLE_COLUMNS); + grid.setWidthPercentage(100); + grid.setHorizontalAlignment(Element.ALIGN_LEFT); + } + catch (DocumentException e) { + } + + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof LaunchLug) { + LaunchLug ll = (LaunchLug) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + + grid.addCell(createMaterialCell(ll.getMaterial())); + grid.addCell(createOuterInnerDiaCell(ll)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + } + else if (component instanceof NoseCone) { + NoseCone nc = (NoseCone) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(nc.getMaterial())); + grid.addCell(ITextHelper.createCell(nc.getType().getName(), PdfPCell.BOTTOM)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof Transition) { + Transition tran = (Transition) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(tran.getMaterial())); + + Chunk fore = new Chunk(FORE_DIAMETER + toLength(tran.getForeRadius() * 2)); + fore.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + Chunk aft = new Chunk(AFT_DIAMETER + toLength(tran.getAftRadius() * 2)); + aft.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + final PdfPCell cell = ITextHelper.createCell(); + cell.addElement(fore); + cell.addElement(aft); + grid.addCell(cell); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof BodyTube) { + BodyTube bt = (BodyTube) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(bt.getMaterial())); + grid.addCell(createOuterInnerDiaCell(bt)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof FinSet) { + handleFins((FinSet) component); + } + else if (component instanceof BodyComponent) { + grid.addCell(component.getName()); + grid.completeRow(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof ExternalComponent) { + ExternalComponent ext = (ExternalComponent) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + + grid.addCell(createMaterialCell(ext.getMaterial())); + grid.addCell(ITextHelper.createCell()); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof InnerTube) { + InnerTube it = (InnerTube) component; + grid.addCell(iconToImage(component)); + final PdfPCell pCell = createNameCell(component.getName(), true); + grid.addCell(pCell); + grid.addCell(createMaterialCell(it.getMaterial())); + grid.addCell(createOuterInnerDiaCell(it)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof RadiusRingComponent) { + RadiusRingComponent rrc = (RadiusRingComponent) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(rrc.getMaterial())); + if (component instanceof Bulkhead) { + grid.addCell(createDiaCell(rrc.getOuterRadius()*2)); + } + else { + grid.addCell(createOuterInnerDiaCell(rrc)); + } + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof RingComponent) { + RingComponent ring = (RingComponent) component; + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(ring.getMaterial())); + grid.addCell(createOuterInnerDiaCell(ring)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof ShockCord) { + ShockCord ring = (ShockCord) component; + PdfPCell cell = ITextHelper.createCell(); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setPaddingBottom(12f); + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(ring.getMaterial())); + grid.addCell(cell); + grid.addCell(createLengthCell(ring.getCordLength())); + grid.addCell(createMassCell(component.getMass())); + } + else if (component instanceof Parachute) { + Parachute chute = (Parachute) component; + PdfPCell cell = ITextHelper.createCell(); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setPaddingBottom(12f); + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(chute.getMaterial())); + grid.addCell(createDiaCell(chute.getDiameter())); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + + grid.addCell(iconToImage(null)); + grid.addCell(createNameCell(SHROUD_LINES, true)); + grid.addCell(createMaterialCell(chute.getLineMaterial())); + grid.addCell(createLinesCell(chute.getLineCount())); + grid.addCell(createLengthCell(chute.getLineLength())); + grid.addCell(cell); + } + else if (component instanceof Streamer) { + Streamer ring = (Streamer) component; + PdfPCell cell = ITextHelper.createCell(); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setPaddingBottom(12f); + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(ring.getMaterial())); + grid.addCell(createStrip(ring)); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + } + else if (component instanceof RecoveryDevice) { + RecoveryDevice device = (RecoveryDevice) component; + PdfPCell cell = ITextHelper.createCell(); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setPaddingBottom(12f); + grid.addCell(iconToImage(component)); + grid.addCell(createNameCell(component.getName(), true)); + grid.addCell(createMaterialCell(device.getMaterial())); + grid.addCell(cell); + grid.addCell(createLengthCell(component.getLength())); + grid.addCell(createMassCell(component.getMass())); + } + else if (component instanceof MassObject) { + PdfPCell cell = ITextHelper.createCell(); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setPaddingBottom(12f); + + grid.addCell(iconToImage(component)); + final PdfPCell nameCell = createNameCell(component.getName(), true); + nameCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + nameCell.setPaddingBottom(12f); + grid.addCell(nameCell); + grid.addCell(cell); + grid.addCell(createDiaCell(((MassObject) component).getRadius() * 2)); + grid.addCell(cell); + grid.addCell(createMassCell(component.getMass())); + } + } + + /** + * Close the strategy by adding the last grid to the document. + */ + public void close () { + try { + if (grid != null) { + document.add(grid); + } + } + catch (DocumentException e) { + log.error("Could not write last cell to document.", e); + } + } + + /** + * Create a cell to document an outer 'diameter'. This is used for components that have no inner diameter, such as + * a solid parachute or bulkhead. + * + * @param diameter the diameter in default length units + * + * @return a formatted cell containing the diameter + */ + private PdfPCell createDiaCell (final double diameter) { + PdfPCell result = new PdfPCell(); + Phrase p = new Phrase(); + p.setLeading(12f); + result.setVerticalAlignment(Element.ALIGN_TOP); + result.setBorder(Rectangle.BOTTOM); + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(DIAMETER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); + c.append(OUTER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(" " + toLength(diameter)); + p.add(c); + result.addElement(p); + return result; + } + + /** + * Create a PDF cell for a streamer. + * + * @param component a component that is a Coaxial + * @return the PDF cell that has the streamer documented + */ + private PdfPCell createStrip (final Streamer component) { + PdfPCell result = new PdfPCell(); + Phrase p = new Phrase(); + p.setLeading(12f); + result.setVerticalAlignment(Element.ALIGN_TOP); + result.setBorder(Rectangle.BOTTOM); + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(LENGTH); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(" " + toLength(component.getStripLength())); + p.add(c); + result.addElement(p); + + Phrase pw = new Phrase(); + pw.setLeading(14f); + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(WIDTH); + pw.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(" " + toLength(component.getStripWidth())); + pw.add(c); + result.addElement(pw); + + return result; + } + + /** + * Create a PDF cell that documents both an outer and an inner diameter of a component. + * + * @param component a component that is a Coaxial + * @return the PDF cell that has the outer and inner diameters documented + */ + private PdfPCell createOuterInnerDiaCell (final Coaxial component) { + PdfPCell result = new PdfPCell(); + Phrase p = new Phrase(); + p.setLeading(12f); + result.setVerticalAlignment(Element.ALIGN_TOP); + result.setBorder(Rectangle.BOTTOM); + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(DIAMETER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); + c.append(OUTER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(" " + toLength(component.getOuterRadius() * 2)); + p.add(c); + createInnerDiaCell(component, result); + result.addElement(p); + return result; + } + + /** + * Add inner diameter data to a cell. + * + * @param component a component that is a Coaxial + * @param cell the PDF cell to add the inner diameter data to + */ + private void createInnerDiaCell (final Coaxial component, PdfPCell cell) { + Phrase p = new Phrase(); + p.setLeading(14f); + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(DIAMETER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); + c.append(INNER); + p.add(c); + + c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(" " + toLength(component.getInnerRadius() * 2)); + p.add(c); + cell.addElement(p); + } + + /** + * Add PDF cells for a fin set. + * + * @param theFinSet the fin set + */ + private void handleFins (FinSet theFinSet) { + + Image img = null; + java.awt.Image awtImage = new PrintableFinSet(theFinSet).createImage(); + + Collection x = theFinSet.getComponentBounds(); + + try { + img = Image.getInstance(writer, awtImage, 0.25f); + } + catch (Exception e) { + log.error("Could not write image to document.", e); + } + + grid.addCell(iconToImage(theFinSet)); + grid.addCell(createNameCell(theFinSet.getName() + " (" + theFinSet.getFinCount() + ")", true)); + grid.addCell(createMaterialCell(theFinSet.getMaterial())); + grid.addCell(ITextHelper.createCell(THICK + toLength(theFinSet.getThickness()), PdfPCell.BOTTOM)); + final PdfPCell pCell = new PdfPCell(); + pCell.setBorder(Rectangle.BOTTOM); + pCell.addElement(img); + + grid.addCell(ITextHelper.createCell()); + grid.addCell(createMassCell(theFinSet.getMass())); + + List rc = theFinSet.getChildren(); + goDeep(rc); + } + + /** + * Create a length formatted cell. + * + * @param length the length, in default length units + * + * @return a PdfPCell that is formatted with the length + */ + protected PdfPCell createLengthCell (double length) { + return ITextHelper.createCell(LEN + toLength(length), PdfPCell.BOTTOM); + } + + /** + * Create a mass formatted cell. + * + * @param mass the mass, in default mass units + * + * @return a PdfPCell that is formatted with the mass + */ + protected PdfPCell createMassCell (double mass) { + return ITextHelper.createCell(MASS + toMass(mass), PdfPCell.BOTTOM); + } + + /** + * Create a (shroud) line count formatted cell. + * + * @param count the number of shroud lines + * + * @return a PdfPCell that is formatted with the line count + */ + protected PdfPCell createLinesCell (int count) { + return ITextHelper.createCell(LINES + count, PdfPCell.BOTTOM); + } + + /** + * Create a cell formatted for a name (or any string for that matter). + * + * @param v the string to format into a PDF cell + * @param withIndent if true, then an indention is made scaled to the level of the part in the parent hierarchy + * + * @return a PdfPCell that is formatted with the string v + */ + protected PdfPCell createNameCell (String v, boolean withIndent) { + PdfPCell result = new PdfPCell(); + result.setBorder(Rectangle.BOTTOM); + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + if (withIndent) { + for (int x = 0; x < (level - 2) * 10; x++) { + c.append(" "); + } + } + c.append(v); + result.setColspan(2); + result.addElement(c); + return result; + } + + /** + * Create a cell that describes a material. + * + * @param material the material + * + * @return a PdfPCell that is formatted with a description of the material + */ + protected PdfPCell createMaterialCell (Material material) { + PdfPCell cell = ITextHelper.createCell(); + cell.setLeading(13f, 0); + + Chunk c = new Chunk(); + c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE)); + c.append(toMaterialName(material)); + cell.addElement(c); + Chunk density = new Chunk(); + density.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE)); + density.append(toMaterialDensity(material)); + cell.addElement(density); + return cell; + } + + /** + * Get the icon of the particular type of rocket component and conver it to an image in a PDF cell. + * + * @param visitable the rocket component to create a cell with it's image + * + * @return a PdfPCell that is just an image that can be put into a PDF + */ + protected PdfPCell iconToImage (final RocketComponent visitable) { + if (visitable != null) { + final ImageIcon icon = (ImageIcon) ComponentIcons.getLargeIcon(visitable.getClass()); + try { + if (icon != null) { + Image im = Image.getInstance(icon.getImage(), null); + if (im != null) { + im.scaleToFit(icon.getIconWidth() * 0.6f, icon.getIconHeight() * 0.6f); + PdfPCell cell = new PdfPCell(im); + cell.setFixedHeight(icon.getIconHeight() * 0.6f); + cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setBorder(PdfPCell.NO_BORDER); + return cell; + } + } + } + catch (Exception e) { + } + } + PdfPCell cell = new PdfPCell(); + cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); + cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); + cell.setBorder(PdfPCell.NO_BORDER); + return cell; + } + + /** + * Format the length as a displayable string. + * + * @param length the length (assumed to be in default length units) + * + * @return a string representation of the length with unit abbreviation + */ + protected String toLength (double length) { + final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); + return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString(); + } + + /** + * Format the mass as a displayable string. + * + * @param mass the mass (assumed to be in default mass units) + * + * @return a string representation of the mass with mass abbreviation + */ + protected String toMass (double mass) { + final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit(); + return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString(); + } + + /** + * Get a displayable string of the material's name. + * + * @param material the material to output + * + * @return the material name + */ + protected String toMaterialName (Material material) { + return material.getName(); + } + + /** + * Format the material density as a displayable string. + * + * @param material the material to output + * + * @return a string representation of the material density + */ + protected String toMaterialDensity (Material material) { + return " (" + material.getType().getUnitGroup().getDefaultUnit().toStringUnit(material.getDensity()) + ")"; + } + } diff --git a/src/net/sf/openrocket/gui/print/visitor/PartsListVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/PartsListVisitorStrategy.java index 699a4263..e8ecc0ba 100644 --- a/src/net/sf/openrocket/gui/print/visitor/PartsListVisitorStrategy.java +++ b/src/net/sf/openrocket/gui/print/visitor/PartsListVisitorStrategy.java @@ -3,256 +3,233 @@ */ package net.sf.openrocket.gui.print.visitor; +import com.itextpdf.text.Document; +import com.itextpdf.text.pdf.PdfWriter; +import net.sf.openrocket.rocketcomponent.*; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.Coaxial; -import net.sf.openrocket.rocketcomponent.ComponentVisitor; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; - -import com.itextpdf.text.Document; -import com.itextpdf.text.pdf.PdfWriter; - /** * A visitor strategy for creating documentation about a parts list. */ -public class PartsListVisitorStrategy extends BaseVisitorStrategy { - - /** - * Accumulator for parts data. - */ - private Map crap = new HashMap(); - - /** - * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts. - * - * @param doc The iText document - * @param theWriter The direct iText writer - * @param theStagesToVisit The stages to be visited by this strategy - */ - public PartsListVisitorStrategy(Document doc, PdfWriter theWriter, Set theStagesToVisit) { - super(doc, theWriter, theStagesToVisit); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RingComponent visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final InnerTube visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final LaunchLug visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Transition visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RadiusRingComponent visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final NoseCone visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyTube visitable) { - final PartsAccumulator key = new PartsAccumulator(visitable); - PartsAccumulator pa = crap.get(key); - if (pa == null) { - pa = key; - crap.put(pa, pa); - } - pa.increment(); - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final TrapezoidFinSet visitable) { - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final EllipticalFinSet visitable) { - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final FreeformFinSet visitable) { - } - - /** - * {@inheritDoc} - */ - @Override - public void setParent(final ComponentVisitor theParent) { - parent = theParent; - } - - /** - * {@inheritDoc} - */ - @Override - public void close() { - for (PartsAccumulator partsAccumulator : crap.keySet()) { - System.err.println(partsAccumulator.component.getComponentName() + " " + partsAccumulator.quantity); - } - } - +public class PartsListVisitorStrategy { + + /** + * Accumulator for parts data. + */ + private Map crap = new HashMap(); + + /** + * The iText document. + */ + protected Document document; + + /** + * The direct iText writer. + */ + protected PdfWriter writer; + + /** + * The stages selected. + */ + protected Set stages; + + + /** + * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts. + * + * @param doc The iText document + * @param theWriter The direct iText writer + * @param theStagesToVisit The stages to be visited by this strategy + */ + public PartsListVisitorStrategy (Document doc, PdfWriter theWriter, Set theStagesToVisit) { + document = doc; + writer = theWriter; + stages = theStagesToVisit; + } + + + /** + * Print the parts detail. + * + * @param root the root component + */ + public void doVisit (final RocketComponent root) { + goDeep(root.getChildren()); + } + + /** + * Recurse through the given rocket component. + * + * @param theRc an array of rocket components; all children will be visited recursively + */ + protected void goDeep (final List theRc) { + for (RocketComponent rocketComponent : theRc) { + doIt(rocketComponent); + } + } + + /** + * {@inheritDoc} + */ + private void doIt (final RocketComponent component) { + if (component instanceof InnerTube) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof LaunchLug) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + } + + else if (component instanceof NoseCone) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof Transition) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof RadiusRingComponent) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof RingComponent) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof BodyTube) { + final PartsAccumulator key = new PartsAccumulator(component); + PartsAccumulator pa = crap.get(key); + if (pa == null) { + pa = key; + crap.put(pa, pa); + } + pa.increment(); + List rc = component.getChildren(); + goDeep(rc); + } + else if (component instanceof TrapezoidFinSet) { + } + else if (component instanceof EllipticalFinSet) { + } + else if (component instanceof FreeformFinSet) { + } + } + + + /** + * {@inheritDoc} + */ + public void close () { + for (PartsAccumulator partsAccumulator : crap.keySet()) { + System.err.println(partsAccumulator.component.getComponentName() + " " + partsAccumulator.quantity); + } + } + } class PartsAccumulator { - - int quantity = 0; - - RocketComponent component; - - PartsAccumulator(RocketComponent theComponent) { - component = theComponent; - } - - void increment() { - quantity++; - } - - int quantity() { - return quantity; - } - - @Override - public boolean equals(final Object o1) { - if (this == o1) { - return true; - } - - RocketComponent that; - if (o1 instanceof net.sf.openrocket.gui.print.visitor.PartsAccumulator) { - that = ((net.sf.openrocket.gui.print.visitor.PartsAccumulator) o1).component; - } else if (o1 instanceof RocketComponent) { - that = (RocketComponent) o1; - } else { - return false; - } - - if (this.component.getClass().equals(that.getClass())) { - //If - if (that.getLength() == this.component.getLength()) { - if (that.getMass() == this.component.getMass()) { - return true; - } - } - if (this.component instanceof Coaxial && - that instanceof Coaxial) { - Coaxial cThis = (Coaxial) this.component; - Coaxial cThat = (Coaxial) that; - if (cThis.getInnerRadius() == cThat.getInnerRadius() && - cThis.getOuterRadius() == cThat.getOuterRadius()) { - return true; - } - } - return false; - } - return false; - } - - @Override - public int hashCode() { - return component.getComponentName().hashCode(); - } + + int quantity = 0; + + RocketComponent component; + + PartsAccumulator (RocketComponent theComponent) { + component = theComponent; + } + + void increment () { + quantity++; + } + + int quantity () { + return quantity; + } + + @Override + public boolean equals (final Object o1) { + if (this == o1) { + return true; + } + + RocketComponent that; + if (o1 instanceof net.sf.openrocket.gui.print.visitor.PartsAccumulator) { + that = ((net.sf.openrocket.gui.print.visitor.PartsAccumulator) o1).component; + } + else if (o1 instanceof RocketComponent) { + that = (RocketComponent) o1; + } + else { + return false; + } + + if (this.component.getClass().equals(that.getClass())) { + //If + if (that.getLength() == this.component.getLength()) { + if (that.getMass() == this.component.getMass()) { + return true; + } + } + if (this.component instanceof Coaxial && + that instanceof Coaxial) { + Coaxial cThis = (Coaxial) this.component; + Coaxial cThat = (Coaxial) that; + if (cThis.getInnerRadius() == cThat.getInnerRadius() && + cThis.getOuterRadius() == cThat.getOuterRadius()) { + return true; + } + } + return false; + } + return false; + } + + @Override + public int hashCode () { + return component.getComponentName().hashCode(); + } } \ No newline at end of file diff --git a/src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java deleted file mode 100644 index 00931b11..00000000 --- a/src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * StageVisitor.java - */ -package net.sf.openrocket.gui.print.visitor; - -import java.util.ArrayList; -import java.util.List; - -import net.sf.openrocket.rocketcomponent.BodyComponent; -import net.sf.openrocket.rocketcomponent.BodyTube; -import net.sf.openrocket.rocketcomponent.ComponentVisitor; -import net.sf.openrocket.rocketcomponent.ComponentVisitorStrategy; -import net.sf.openrocket.rocketcomponent.EllipticalFinSet; -import net.sf.openrocket.rocketcomponent.ExternalComponent; -import net.sf.openrocket.rocketcomponent.FreeformFinSet; -import net.sf.openrocket.rocketcomponent.InnerTube; -import net.sf.openrocket.rocketcomponent.LaunchLug; -import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.NoseCone; -import net.sf.openrocket.rocketcomponent.RadiusRingComponent; -import net.sf.openrocket.rocketcomponent.RingComponent; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.Stage; -import net.sf.openrocket.rocketcomponent.Transition; -import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; - -/** - * This visitor strategy accumulates Stage references in a Rocket hierarchy. - */ -public class StageVisitorStrategy implements ComponentVisitorStrategy { - - /** - * The collection of stages, accumulated during a visitation. - */ - private List stageComponents = new ArrayList(); - - private Double mass = 0d; - - /** - * The owning visitor. - */ - protected ComponentVisitor parent; - - /** - * Constructor. - */ - public StageVisitorStrategy() { - } - - /** - * Override the method that determines if the visiting should be going deep. - * - * @param stageNumber a stage number - * - * @return true, always - */ - public boolean shouldVisitStage(int stageNumber) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void setParent(final ComponentVisitor theParent) { - parent = theParent; - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Rocket visitable) { - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Stage visitable) { - - if (mass > 0d) { - stageComponents.add(mass); - } - mass = 0d; - List rc = visitable.getChildren(); - goDeep(rc); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RocketComponent visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * Recurse through the given rocket component. - * - * @param root the root component; all children will be visited recursively - */ - protected void goDeep(final RocketComponent root) { - List rc = root.getChildren(); - goDeep(rc); - } - - - /** - * Recurse through the given rocket component. - * - * @param theRc an array of rocket components; all children will be visited recursively - */ - protected void goDeep(final List theRc) { - for (RocketComponent rocketComponent : theRc) { - rocketComponent.accept(parent); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void visit(final ExternalComponent visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyComponent visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RingComponent visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final InnerTube visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final LaunchLug visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final Transition visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final RadiusRingComponent visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final MassObject visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final NoseCone visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final BodyTube visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final TrapezoidFinSet visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final EllipticalFinSet visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * {@inheritDoc} - */ - @Override - public void visit(final FreeformFinSet visitable) { - mass += visitable.getMass(); - goDeep(visitable); - } - - /** - * Get the list of stages, sort from Stage 1 .. Stage N. - * - * @return a sorted list of stages - */ - public List getStages() { - return stageComponents; - } - - /** - * Close by setting the last stage. - */ - @Override - public void close() { - if (mass > 0d) { - stageComponents.add(mass); - } - } - -} diff --git a/src/net/sf/openrocket/rocketcomponent/BodyComponent.java b/src/net/sf/openrocket/rocketcomponent/BodyComponent.java index 97a12406..2bc112fb 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyComponent.java @@ -3,17 +3,17 @@ package net.sf.openrocket.rocketcomponent; /** * Class to represent a body object. The object can be described as a function of - * the cylindrical coordinates x and angle theta as r = f(x,theta). The component + * the cylindrical coordinates x and angle theta as r = f(x,theta). The component * need not be symmetrical in any way (e.g. square tube, slanted cone etc). - * + * * It defines the methods getRadius(x,theta) and getInnerRadius(x,theta), as well * as get/setLength(). - * + * * @author Sampo Niskanen */ public abstract class BodyComponent extends ExternalComponent { - + /** * Default constructor. Sets the relative position to POSITION_RELATIVE_AFTER, * i.e. body components come after one another. @@ -21,32 +21,32 @@ public abstract class BodyComponent extends ExternalComponent { public BodyComponent() { super(RocketComponent.Position.AFTER); } - - + + /** * Get the outer radius of the component at cylindrical coordinate (x,theta). - * + * * Note that the return value may be negative for a slanted object. - * + * * @param x Distance in x direction * @param theta Angle about the x-axis * @return Distance to the outer edge of the object */ public abstract double getRadius(double x, double theta); - - + + /** * Get the inner radius of the component at cylindrical coordinate (x,theta). - * + * * Note that the return value may be negative for a slanted object. - * + * * @param x Distance in x direction * @param theta Angle about the x-axis * @return Distance to the inner edge of the object */ public abstract double getInnerRadius(double x, double theta); - - + + /** * Sets the length of the body component. @@ -57,21 +57,10 @@ public abstract class BodyComponent extends ExternalComponent { this.length = Math.max(length, 0); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override 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); - } - - + } diff --git a/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/src/net/sf/openrocket/rocketcomponent/BodyTube.java index f45f3526..adf0aa19 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -11,25 +11,25 @@ import java.util.HashMap; /** * Rocket body tube component. Has only two parameters, a radius and length. - * + * * @author Sampo Niskanen */ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial { - + private double radius = 0; private boolean autoRadius = false; // Radius chosen automatically based on parent component - + // When changing the inner radius, thickness is modified - + private boolean motorMount = false; private HashMap ejectionDelays = new HashMap(); private HashMap motors = new HashMap(); private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC; private double ignitionDelay = 0; private double overhang = 0; - - + + public BodyTube() { super(); @@ -37,31 +37,31 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial this.radius = DEFAULT_RADIUS; this.autoRadius = true; } - + public BodyTube(double length, double radius) { super(); this.radius = Math.max(radius, 0); this.length = Math.max(length, 0); } - - + + public BodyTube(double length, double radius, boolean filled) { this(length, radius); this.filled = filled; } - + public BodyTube(double length, double radius, double thickness) { this(length, radius); this.filled = false; this.thickness = thickness; } - - + + /************ Get/set component parameter methods ************/ - + /** * Return the outer radius of the body tube. - * + * * @return the outside radius of the tube */ @Override @@ -85,29 +85,29 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } return radius; } - - + + /** * 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 */ @Override public void setOuterRadius (double radius) { if ((this.radius == radius) && (autoRadius == false)) return; - + this.autoRadius = false; this.radius = Math.max(radius, 0); - + if (this.thickness > this.radius) this.thickness = this.radius; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + /** * Returns whether the radius is selected automatically or not. * Returns false also in case automatic radius selection is not possible. @@ -115,19 +115,19 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial public boolean isRadiusAutomatic() { return autoRadius; } - + /** - * Sets whether the radius is selected automatically or not. + * Sets whether the radius is selected automatically or not. */ public void setRadiusAutomatic(boolean auto) { if (autoRadius == auto) return; - + autoRadius = auto; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @Override public double getAftRadius() { return getOuterRadius(); } @Override @@ -136,13 +136,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial public boolean isAftRadiusAutomatic() { return isRadiusAutomatic(); } - + @Override public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); } - - + + @Override protected double getFrontAutoRadius() { @@ -157,7 +157,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } return getOuterRadius(); } - + @Override protected double getRearAutoRadius() { if (isRadiusAutomatic()) { @@ -171,27 +171,27 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial } return getOuterRadius(); } - - - - - - - @Override + + + + + + + @Override public double getInnerRadius() { if (filled) return 0; return Math.max(getOuterRadius()-thickness, 0); } - + @Override public void setInnerRadius(double r) { setThickness(getOuterRadius()-r); } - - + + /** @@ -202,19 +202,9 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial return "Body tube"; } - /** - * Accept a visitor to this BodyTube in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this BodyTube - */ - @Override - public void accept (final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - /************ Component calculations ***********/ - + // From SymmetricComponent /** * Returns the outer radius at the position x. This returns the same value as getOuterRadius(). @@ -223,7 +213,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial public double getRadius(double x) { return getOuterRadius(); } - + /** * Returns the inner radius at the position x. If the tube is filled, returns always zero. */ @@ -234,8 +224,8 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial else return Math.max(getOuterRadius()-thickness,0); } - - + + /** * Returns the body tube's center of gravity. */ @@ -243,7 +233,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial public Coordinate getComponentCG() { return new Coordinate(length / 2, 0, 0, getComponentMass()); } - + /** * Returns the body tube's volume. */ @@ -255,22 +245,22 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial else return getFilledVolume(r, length) - getFilledVolume(getInnerRadius(0), length); } - - + + @Override public double getLongitudinalUnitInertia() { // 1/12 * (3 * (r1^2 + r2^2) + h^2) 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(getOuterRadius()))/2; } - - + + /** @@ -279,12 +269,12 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial private static double getFilledVolume(double r, double l) { return Math.PI * r * r * l; } - - + + /** * Adds bounding coordinates to the given set. The body tube will fit within the * convex hull of the points. - * + * * Currently the points are simply a rectangular box around the body tube. */ @Override @@ -295,13 +285,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial 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. */ @@ -314,14 +304,14 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial return true; return false; } - + //////////////// Motor mount ///////////////// - + @Override public boolean isMotorMount() { return motorMount; } - + @Override public void setMotorMount(boolean mount) { if (motorMount == mount) @@ -329,22 +319,22 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial motorMount = mount; fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Override public Motor getMotor(String id) { if (id == null) return null; - + // Check whether the id is valid for the current rocket RocketComponent root = this.getRoot(); if (!(root instanceof Rocket)) return null; if (!((Rocket) root).isMotorConfigurationID(id)) return null; - + return motors.get(id); } - + @Override public void setMotor(String id, Motor motor) { if (id == null) { @@ -359,7 +349,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial motors.put(id, motor); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Override public double getMotorDelay(String id) { Double delay = ejectionDelays.get(id); @@ -367,28 +357,28 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial return Motor.PLUGGED; return delay; } - + @Override public void setMotorDelay(String id, double delay) { ejectionDelays.put(id, delay); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Override public int getMotorCount() { return 1; } - + @Override public double getMotorMountDiameter() { return getInnerRadius() * 2; } - + @Override public IgnitionEvent getIgnitionEvent() { return ignitionEvent; } - + @Override public void setIgnitionEvent(IgnitionEvent event) { if (ignitionEvent == event) @@ -396,13 +386,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial ignitionEvent = event; fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); } - - + + @Override public double getIgnitionDelay() { return ignitionDelay; } - + @Override public void setIgnitionDelay(double delay) { if (MathUtil.equals(delay, ignitionDelay)) @@ -410,13 +400,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial ignitionDelay = delay; fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); } - - + + @Override public double getMotorOverhang() { return overhang; } - + @Override public void setMotorOverhang(double overhang) { if (MathUtil.equals(this.overhang, overhang)) @@ -424,25 +414,25 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial this.overhang = overhang; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @Override public Coordinate getMotorPosition(String id) { Motor motor = motors.get(id); if (motor == null) { throw new IllegalArgumentException("No motor with id " + id + " defined."); } - + return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang()); } - - + + /* * (non-Javadoc) * Copy the motor and ejection delay HashMaps. - * + * * @see rocketcomponent.RocketComponent#copy() */ @SuppressWarnings("unchecked") diff --git a/src/net/sf/openrocket/rocketcomponent/CenteringRing.java b/src/net/sf/openrocket/rocketcomponent/CenteringRing.java index 3abcd44a..21b73e2a 100644 --- a/src/net/sf/openrocket/rocketcomponent/CenteringRing.java +++ b/src/net/sf/openrocket/rocketcomponent/CenteringRing.java @@ -4,14 +4,14 @@ import net.sf.openrocket.util.Coordinate; public class CenteringRing extends RadiusRingComponent { - + public CenteringRing() { setOuterRadiusAutomatic(true); setInnerRadiusAutomatic(true); setLength(0.002); } - - + + @Override public double getInnerRadius() { // Implement sibling inner radius automation @@ -26,55 +26,45 @@ public class CenteringRing extends RadiusRingComponent { */ if (!(sibling instanceof InnerTube)) // Excludes itself continue; - + double pos1 = this.toRelative(Coordinate.NUL, sibling)[0].x; double pos2 = this.toRelative(new Coordinate(getLength()), sibling)[0].x; if (pos2 < 0 || pos1 > sibling.getLength()) continue; - + innerRadius = Math.max(innerRadius, ((InnerTube) sibling).getOuterRadius()); } innerRadius = Math.min(innerRadius, getOuterRadius()); } } - + return super.getInnerRadius(); } - - + + @Override public void setOuterRadiusAutomatic(boolean auto) { super.setOuterRadiusAutomatic(auto); } - + @Override public void setInnerRadiusAutomatic(boolean auto) { super.setInnerRadiusAutomatic(auto); } - + @Override public String getComponentName() { return "Centering ring"; } - + @Override public boolean allowsChildren() { return false; } - + @Override public boolean isCompatible(Class type) { return false; } - - /** - * Accept a visitor to this CenteringRing in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this CenteringRing - */ - @Override - public void accept (final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } } diff --git a/src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java b/src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java deleted file mode 100644 index 5a69f751..00000000 --- a/src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * ComponentVisitor.java - */ -package net.sf.openrocket.rocketcomponent; - -/** - * This class implements a Visitor pattern to visit any/all components of a Rocket. - */ -public class ComponentVisitor implements Visitor { - - /** - * The delegate. - */ - private ComponentVisitorStrategy strategy; - - /** - * Constructor. - * - * @param aStrategy the object to delegate the visiting to - */ - public ComponentVisitor (ComponentVisitorStrategy aStrategy) { - strategy = aStrategy; - strategy.setParent(this); - } - - /** - * Visit a Rocket object. - * - * @param visitable the Rocket to visit - */ - public void visit (final Rocket visitable) { - strategy.visit(visitable); - } - - /** - * Visit a RocketComponent object. This is used to catch any RocketComponent subclass not explicity defined by a - * visit method in this strategy. - * - * @param visitable the RocketComponent to visit - */ - public void visit (final RocketComponent visitable) { - strategy.visit(visitable); - } - - /** - * Visit a Stage object. - * - * @param visitable the Stage to visit - */ - public void visit (final Stage visitable) { - strategy.visit(visitable); - } - - /** - * Visit an ExternalComponent object. - * - * @param visitable the ExternalComponent to visit - */ - public void visit (final ExternalComponent visitable) { - strategy.visit(visitable); - } - - /** - * Visit a BodyComponent object. - * - * @param visitable the BodyComponent to visit - */ - public void visit (final BodyComponent visitable) { - strategy.visit(visitable); - } - - /** - * Visit a RingComponent object. - * - * @param visitable the RingComponent to visit - */ - public void visit (final RingComponent visitable) { - strategy.visit(visitable); - } - - /** - * Visit an InnerTube object. - * - * @param visitable the InnerTube to visit - */ - public void visit (final InnerTube visitable) { - strategy.visit(visitable); - } - - /** - * Visit a LaunchLug object. - * - * @param visitable the LaunchLug to visit - */ - public void visit (final LaunchLug visitable) { - strategy.visit(visitable); - } - - /** - * Visit a Transition object. - * - * @param visitable the Transition to visit - */ - public void visit (final Transition visitable) { - strategy.visit(visitable); - } - - /** - * Visit a RadiusRingComponent object. - * - * @param visitable the RadiusRingComponent to visit - */ - public void visit (final RadiusRingComponent visitable) { - strategy.visit(visitable); - } - - /** - * Visit a MassObject object. - * - * @param visitable the MassObject to visit - */ - public void visit (final MassObject visitable) { - strategy.visit(visitable); - } - - /** - * Visit a NoseCone object. - * - * @param visitable the NoseCone to visit - */ - public void visit (final NoseCone visitable) { - strategy.visit(visitable); - } - - /** - * Visit a BodyTube object. - * - * @param visitable the BodyTube to visit - */ - public void visit (final BodyTube visitable) { - strategy.visit(visitable); - } - - /** - * Visit a Rocket object. - * - * @param visitable the Rocket to visit - */ - public void visit (final TrapezoidFinSet visitable) { - strategy.visit(visitable); - } - - /** - * Visit a Rocket object. - * - * @param visitable the Rocket to visit - */ - public void visit (final EllipticalFinSet visitable) { - strategy.visit(visitable); - } - - /** - * Visit a FreeformFinSet object. - * - * @param visitable the FreeformFinSet to visit - */ - public void visit (final FreeformFinSet visitable) { - strategy.visit(visitable); - } - - /** - * Perform any cleanup or finishing operations. - */ - public void close () { - strategy.close(); - } - -} diff --git a/src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java b/src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java deleted file mode 100644 index a4cc3348..00000000 --- a/src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * ComponentVisitorStrategy.java - */ -package net.sf.openrocket.rocketcomponent; - -/** - * This interface defines the methods used in a Rocket component visitor. By using a strategy, we can reuse one visitor - * definition and just instrument it with different strategies. - */ -public interface ComponentVisitorStrategy { - - /** - * Visit a Rocket object. - * - * @param visitable the Rocket to visit - */ - void visit (final Rocket visitable); - - /** - * Visit a RocketComponent object. This is used to catch any RocketComponent subclass not explicity defined by a - * visit method in this strategy. - * - * @param visitable the RocketComponent to visit - */ - void visit (final RocketComponent visitable); - - /** - * Visit a Stage object. - * - * @param visitable the Stage to visit - */ - void visit (final Stage visitable); - - /** - * Visit an ExternalComponent object. - * - * @param visitable the ExternalComponent to visit - */ - void visit (final ExternalComponent visitable); - - /** - * Visit a BodyComponent object. - * - * @param visitable the BodyComponent to visit - */ - void visit (final BodyComponent visitable); - - /** - * Visit a RingComponent object. - * - * @param visitable the RingComponent to visit - */ - void visit (final RingComponent visitable); - - /** - * Visit an InnerTube object. - * - * @param visitable the InnerTube to visit - */ - void visit (final InnerTube visitable); - - /** - * Visit a LaunchLug object. - * - * @param visitable the LaunchLug to visit - */ - void visit (final LaunchLug visitable); - - /** - * Visit a Transition object. - * - * @param visitable the Transition to visit - */ - void visit (final Transition visitable); - - /** - * Visit a RadiusRingComponent object. - * - * @param visitable the RadiusRingComponent to visit - */ - void visit (final RadiusRingComponent visitable); - - /** - * Visit a MassComponent object. - * - * @param visitable the MassComponent to visit - */ - void visit (final MassObject visitable); - - /** - * Visit a NoseCone object. - * - * @param visitable the NoseCone to visit - */ - void visit (final NoseCone visitable); - - /** - * Visit a BodyTube object. - * - * @param visitable the BodyTube to visit - */ - void visit (final BodyTube visitable); - - /** - * Visit a TrapezoidFinSet object. - * - * @param visitable the TrapezoidFinSet to visit - */ - void visit (final TrapezoidFinSet visitable); - - /** - * Visit an EllipticalFinSet object. - * - * @param visitable the EllipticalFinSet to visit - */ - void visit (final EllipticalFinSet visitable); - - /** - * Visit a FreeformFinSet object. - * - * @param visitable the FreeformFinSet to visit - */ - void visit (final FreeformFinSet visitable); - - /** - * Set the visitor that is using this strategy. - * - * @param parent the visitor - */ - void setParent (ComponentVisitor parent); - - /** - * Perform any cleanup or finishing operations. - */ - void close (); -} diff --git a/src/net/sf/openrocket/rocketcomponent/EllipticalFinSet.java b/src/net/sf/openrocket/rocketcomponent/EllipticalFinSet.java index 22d49bea..aaaf9284 100644 --- a/src/net/sf/openrocket/rocketcomponent/EllipticalFinSet.java +++ b/src/net/sf/openrocket/rocketcomponent/EllipticalFinSet.java @@ -5,7 +5,7 @@ import net.sf.openrocket.util.MathUtil; public class EllipticalFinSet extends FinSet { public static final int POINTS = 21; - + private static final double[] POINT_X = new double[POINTS]; private static final double[] POINT_Y = new double[POINTS]; static { @@ -19,15 +19,15 @@ public class EllipticalFinSet extends FinSet { POINT_X[POINTS-1] = 1; POINT_Y[POINTS-1] = 0; } - - + + private double height = 0.05; - + public EllipticalFinSet() { this.length = 0.05; } - + @Override public Coordinate[] getFinPoints() { Coordinate[] points = new Coordinate[POINTS]; @@ -46,20 +46,20 @@ public class EllipticalFinSet extends FinSet { public String getComponentName() { return "Elliptical fin set"; } - - + + public double getHeight() { return height; } - + public void setHeight(double height) { if (MathUtil.equals(this.height, height)) return; this.height = height; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + public void setLength(double length) { if (MathUtil.equals(this.length, length)) return; @@ -67,15 +67,5 @@ public class EllipticalFinSet extends FinSet { fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - /** - * Accept a visitor to this EllipticalFinSet in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this EllipticalFinSet - */ - @Override - public void accept(ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java b/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java index 6fa4a4c2..a7fdbcec 100644 --- a/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java @@ -1,55 +1,55 @@ package net.sf.openrocket.rocketcomponent; -import java.util.List; - import net.sf.openrocket.material.Material; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Prefs; +import java.util.List; + /** * Class of components with well-defined physical appearance and which have an effect on - * aerodynamic simulation. They have material defined for them, and the mass of the component + * aerodynamic simulation. They have material defined for them, and the mass of the component * is calculated using the component's volume. - * + * * @author Sampo Niskanen */ public abstract class ExternalComponent extends RocketComponent { - + public enum Finish { ROUGH("Rough", 500e-6), UNFINISHED("Unfinished", 150e-6), NORMAL("Regular paint", 60e-6), SMOOTH("Smooth paint", 20e-6), POLISHED("Polished", 2e-6); - + private final String name; private final double roughnessSize; - + Finish(String name, double roughness) { this.name = name; this.roughnessSize = roughness; } - + public double getRoughnessSize() { return roughnessSize; } - + @Override public String toString() { return name + " (" + UnitGroup.UNITS_ROUGHNESS.toStringUnit(roughnessSize) + ")"; } } - - + + /** * The material of the component. */ protected Material material = null; - + protected Finish finish = Finish.NORMAL; - - + + /** * Constructor that sets the relative position of the component. @@ -58,13 +58,13 @@ public abstract class ExternalComponent extends RocketComponent { super(relativePosition); this.material = Prefs.getDefaultComponentMaterial(this.getClass(), Material.Type.BULK); } - + /** * Returns the volume of the component. This value is used in calculating the mass * of the object. */ public abstract double getComponentVolume(); - + /** * Calculates the mass of the component as the product of the volume and interior density. */ @@ -72,7 +72,7 @@ public abstract class ExternalComponent extends RocketComponent { public double getComponentMass() { return material.getDensity() * getComponentVolume(); } - + /** * ExternalComponent has aerodynamic effect, so return true. */ @@ -80,7 +80,7 @@ public abstract class ExternalComponent extends RocketComponent { public boolean isAerodynamic() { return true; } - + /** * ExternalComponent has effect on the mass, so return true. */ @@ -88,36 +88,35 @@ public abstract class ExternalComponent extends RocketComponent { public boolean isMassive() { return true; } - - + + public Material getMaterial() { return material; } - + public void setMaterial(Material mat) { if (mat.getType() != Material.Type.BULK) { throw new IllegalArgumentException("ExternalComponent requires a bulk material" + " type=" + mat.getType()); } - + if (material.equals(mat)) return; material = mat; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public Finish getFinish() { return finish; } - + public void setFinish(Finish finish) { if (this.finish == finish) return; this.finish = finish; fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE); } - - + @Override protected List copyFrom(RocketComponent c) { @@ -126,15 +125,5 @@ public abstract class ExternalComponent extends RocketComponent { this.material = src.material; return super.copyFrom(c); } - - /** - * Accept a visitor to this ExternalComponent in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this ExternalComponent - */ - @Override - public void accept (final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index 24ca9811..792483d2 100644 --- a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -1,40 +1,40 @@ package net.sf.openrocket.rocketcomponent; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Coordinate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class FreeformFinSet extends FinSet { private static final LogHelper log = Application.getLogger(); - + private ArrayList points = new ArrayList(); - + public FreeformFinSet() { points.add(Coordinate.NUL); points.add(new Coordinate(0.025, 0.05)); points.add(new Coordinate(0.075, 0.05)); points.add(new Coordinate(0.05, 0)); - + this.length = 0.05; } - - + + public FreeformFinSet(Coordinate[] finpoints) throws IllegalFinPointException { setPoints(finpoints); } - + /* public FreeformFinSet(FinSet finset) { Coordinate[] finpoints = finset.getFinPoints(); this.copyFrom(finset); - + points.clear(); for (Coordinate c: finpoints) { points.add(c); @@ -50,7 +50,7 @@ public class FreeformFinSet extends FinSet { * inserted in its stead. *

* The specified fin set should not be used after the call! - * + * * @param finset the fin set to convert. * @return the new freeform fin set. */ @@ -59,12 +59,12 @@ public class FreeformFinSet extends FinSet { final RocketComponent root = finset.getRoot(); FreeformFinSet freeform; List toInvalidate = Collections.emptyList(); - + try { if (root instanceof Rocket) { ((Rocket) root).freeze(); } - + // Get fin set position and remove fin set final RocketComponent parent = finset.getParent(); final int position; @@ -74,7 +74,7 @@ public class FreeformFinSet extends FinSet { } else { position = -1; } - + // Create the freeform fin set Coordinate[] finpoints = finset.getFinPoints(); @@ -85,24 +85,24 @@ public class FreeformFinSet extends FinSet { "freeform fin, fin=" + finset + " points=" + Arrays.toString(finpoints), e); } - + // Copy component attributes toInvalidate = freeform.copyFrom(finset); - + // Set name final String componentTypeName = finset.getComponentName(); final String name = freeform.getName(); - + if (name.startsWith(componentTypeName)) { freeform.setName(freeform.getComponentName() + name.substring(componentTypeName.length())); } - + // Add freeform fin set to parent if (parent != null) { parent.addChild(freeform, position); } - + } finally { if (root instanceof Rocket) { ((Rocket) root).thaw(); @@ -114,34 +114,34 @@ public class FreeformFinSet extends FinSet { } return freeform; } - - + + /** * Add a fin point between indices index-1 and index. * The point is placed at the midpoint of the current segment. - * + * * @param index the fin point before which to add the new point. */ public void addPoint(int index) { double x0, y0, x1, y1; - + x0 = points.get(index - 1).x; y0 = points.get(index - 1).y; x1 = points.get(index).x; y1 = points.get(index).y; - + points.add(index, new Coordinate((x0 + x1) / 2, (y0 + y1) / 2)); // adding a point within the segment affects neither mass nor aerodynamics fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** * Remove the fin point with the given index. The first and last fin points * cannot be removed, and will cause an IllegalFinPointException * if attempted. - * + * * @param index the fin point index to remove * @throws IllegalFinPointException if removing would result in invalid fin planform */ @@ -149,20 +149,20 @@ public class FreeformFinSet extends FinSet { if (index == 0 || index == points.size() - 1) { throw new IllegalFinPointException("cannot remove first or last point"); } - + ArrayList copy = this.points.clone(); copy.remove(index); validate(copy); this.points = copy; - + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + public int getPointCount() { return points.size(); } - + public void setPoints(Coordinate[] points) throws IllegalFinPointException { ArrayList list = new ArrayList(points.length); for (Coordinate p : points) { @@ -170,23 +170,23 @@ public class FreeformFinSet extends FinSet { } validate(list); this.points = list; - + this.length = points[points.length - 1].x; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + /** * Set the point at position i to coordinates (x,y). *

* Note that this method enforces basic fin shape restrictions (non-negative y, - * first and last point locations) silently, but throws an + * first and last point locations) silently, but throws an * IllegalFinPointException if the point causes fin segments to * intersect. *

* Moving of the first point in the X-axis is allowed, but this actually moves * all of the other points the corresponding distance back. - * + * * @param index the point index to modify. * @param x the x-coordinate. * @param y the y-coordinate. @@ -196,11 +196,11 @@ public class FreeformFinSet extends FinSet { public void setPoint(int index, double x, double y) throws IllegalFinPointException { if (y < 0) y = 0; - + double x0, y0, x1, y1; - + if (index == 0) { - + // Restrict point x = Math.min(x, points.get(points.size() - 1).x); y = 0; @@ -208,9 +208,9 @@ public class FreeformFinSet extends FinSet { y0 = Double.NaN; x1 = points.get(1).x; y1 = points.get(1).y; - + } else if (index == points.size() - 1) { - + // Restrict point x = Math.max(x, 0); y = 0; @@ -218,16 +218,16 @@ public class FreeformFinSet extends FinSet { y0 = points.get(index - 1).y; x1 = Double.NaN; y1 = Double.NaN; - + } else { - + x0 = points.get(index - 1).x; y0 = points.get(index - 1).y; x1 = points.get(index + 1).x; y1 = points.get(index + 1).y; - + } - + // Check for intersecting @@ -237,7 +237,7 @@ public class FreeformFinSet extends FinSet { for (int i = 1; i < points.size(); i++) { px1 = points.get(i).x; py1 = points.get(i).y; - + if (i != index - 1 && i != index && i != index + 1) { if (intersects(x0, y0, x, y, px0, py0, px1, py1)) { throw new IllegalFinPointException("segments intersect"); @@ -248,49 +248,49 @@ public class FreeformFinSet extends FinSet { throw new IllegalFinPointException("segments intersect"); } } - + px0 = px1; py0 = py1; } - + if (index == 0) { - + System.out.println("Set point zero to x:" + x); for (int i = 1; i < points.size(); i++) { Coordinate c = points.get(i); points.set(i, c.setX(c.x - x)); } - + } else { - + points.set(index, new Coordinate(x, y)); - + } if (index == 0 || index == points.size() - 1) { this.length = points.get(points.size() - 1).x; } fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + private boolean intersects(double ax0, double ay0, double ax1, double ay1, double bx0, double by0, double bx1, double by1) { - + double d = ((by1 - by0) * (ax1 - ax0) - (bx1 - bx0) * (ay1 - ay0)); - + double ua = ((bx1 - bx0) * (ay0 - by0) - (by1 - by0) * (ax0 - bx0)) / d; double ub = ((ax1 - ax0) * (ay0 - by0) - (ay1 - ay0) * (ax0 - bx0)) / d; - + return (ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1); } - - + + @Override public Coordinate[] getFinPoints() { return points.toArray(new Coordinate[0]); } - + @Override public double getSpan() { double max = 0; @@ -300,30 +300,20 @@ public class FreeformFinSet extends FinSet { } return max; } - + @Override public String getComponentName() { return "Freeform fin set"; } - - + + @Override protected RocketComponent copyWithOriginalID() { RocketComponent c = super.copyWithOriginalID(); ((FreeformFinSet) c).points = this.points.clone(); return c; } - - /** - * Accept a visitor to this FreeformFinSet in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this FreeformFinSet - */ - @Override - public void accept(ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - + private void validate(ArrayList points) throws IllegalFinPointException { final int n = points.size(); if (points.get(0).x != 0 || points.get(0).y != 0 || @@ -342,5 +332,5 @@ public class FreeformFinSet extends FinSet { } } } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/src/net/sf/openrocket/rocketcomponent/InnerTube.java index 036daef6..a4d5af4d 100644 --- a/src/net/sf/openrocket/rocketcomponent/InnerTube.java +++ b/src/net/sf/openrocket/rocketcomponent/InnerTube.java @@ -13,16 +13,16 @@ import java.util.List; /** * This class defines an inner tube that can be used as a motor mount. The component * may also be clustered. - * + * * @author Sampo Niskanen */ public class InnerTube extends ThicknessRingComponent implements Clusterable, RadialParent, MotorMount { - + private ClusterConfiguration cluster = ClusterConfiguration.SINGLE; private double clusterScale = 1.0; private double clusterRotation = 0.0; - + private boolean motorMount = false; private HashMap ejectionDelays = new HashMap(); @@ -30,8 +30,8 @@ public class InnerTube extends ThicknessRingComponent private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC; private double ignitionDelay = 0; private double overhang = 0; - - + + /** * Main constructor. */ @@ -41,30 +41,30 @@ public class InnerTube extends ThicknessRingComponent this.setInnerRadius(0.018 / 2); this.setLength(0.070); } - - + + @Override public double getInnerRadius(double x) { return getInnerRadius(); } - - + + @Override public double getOuterRadius(double x) { return getOuterRadius(); } - - + + @Override public String getComponentName() { return "Inner Tube"; } - + @Override public boolean allowsChildren() { return true; } - + /** * Allow all InternalComponents to be added to this component. */ @@ -72,11 +72,11 @@ public class InnerTube extends ThicknessRingComponent public boolean isCompatible(Class type) { return InternalComponent.class.isAssignableFrom(type); } - - + + ///////////// Cluster methods ////////////// - + /** * Get the current cluster configuration. * @return The current cluster configuration. @@ -84,7 +84,7 @@ public class InnerTube extends ThicknessRingComponent public ClusterConfiguration getClusterConfiguration() { return cluster; } - + /** * Set the current cluster configuration. * @param cluster The cluster configuration. @@ -93,7 +93,7 @@ public class InnerTube extends ThicknessRingComponent this.cluster = cluster; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + /** * Return the number of tubes in the cluster. * @return Number of tubes in the current cluster. @@ -102,7 +102,7 @@ public class InnerTube extends ThicknessRingComponent public int getClusterCount() { return cluster.getClusterCount(); } - + /** * Get the cluster scaling. A value of 1.0 indicates that the tubes are packed * touching each other, larger values separate the tubes and smaller values @@ -111,7 +111,7 @@ public class InnerTube extends ThicknessRingComponent public double getClusterScale() { return clusterScale; } - + /** * Set the cluster scaling. * @see #getClusterScale() @@ -123,8 +123,8 @@ public class InnerTube extends ThicknessRingComponent clusterScale = scale; fireComponentChangeEvent(new ComponentChangeEvent(this, ComponentChangeEvent.MASS_CHANGE)); } - - + + /** * @return the clusterRotation @@ -132,8 +132,8 @@ public class InnerTube extends ThicknessRingComponent public double getClusterRotation() { return clusterRotation; } - - + + /** * @param rotation the clusterRotation to set */ @@ -144,8 +144,8 @@ public class InnerTube extends ThicknessRingComponent this.clusterRotation = rotation; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /** * Return the distance between the closest two cluster inner tube center points. * This is equivalent to the cluster scale multiplied by the tube diameter. @@ -154,8 +154,8 @@ public class InnerTube extends ThicknessRingComponent public double getClusterSeparation() { return 2 * getOuterRadius() * clusterScale; } - - + + public List getClusterPoints() { List list = new ArrayList(getClusterCount()); List points = cluster.getPoints(clusterRotation - getRadialDirection()); @@ -165,16 +165,16 @@ public class InnerTube extends ThicknessRingComponent } return list; } - - + + @Override public Coordinate[] shiftCoordinates(Coordinate[] array) { array = super.shiftCoordinates(array); - + int count = getClusterCount(); if (count == 1) return array; - + List points = getClusterPoints(); if (points.size() != count) { throw new BugException("Inconsistent cluster configuration, cluster count=" + count + @@ -186,20 +186,20 @@ public class InnerTube extends ThicknessRingComponent newArray[i * count + j] = array[i].add(points.get(j)); } } - + return newArray; } - - + + //////////////// Motor mount ///////////////// - + @Override public boolean isMotorMount() { return motorMount; } - + @Override public void setMotorMount(boolean mount) { if (motorMount == mount) @@ -207,22 +207,22 @@ public class InnerTube extends ThicknessRingComponent motorMount = mount; fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Override public Motor getMotor(String id) { if (id == null) return null; - + // Check whether the id is valid for the current rocket RocketComponent root = this.getRoot(); if (!(root instanceof Rocket)) return null; if (!((Rocket) root).isMotorConfigurationID(id)) return null; - + return motors.get(id); } - + @Override public void setMotor(String id, Motor motor) { if (id == null) { @@ -237,7 +237,7 @@ public class InnerTube extends ThicknessRingComponent motors.put(id, motor); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Override public double getMotorDelay(String id) { Double delay = ejectionDelays.get(id); @@ -245,29 +245,29 @@ public class InnerTube extends ThicknessRingComponent return Motor.PLUGGED; return delay; } - + @Override public void setMotorDelay(String id, double delay) { ejectionDelays.put(id, delay); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - + @Deprecated @Override public int getMotorCount() { return getClusterCount(); } - + @Override public double getMotorMountDiameter() { return getInnerRadius() * 2; } - + @Override public IgnitionEvent getIgnitionEvent() { return ignitionEvent; } - + @Override public void setIgnitionEvent(IgnitionEvent event) { if (ignitionEvent == event) @@ -275,13 +275,13 @@ public class InnerTube extends ThicknessRingComponent ignitionEvent = event; fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); } - - + + @Override public double getIgnitionDelay() { return ignitionDelay; } - + @Override public void setIgnitionDelay(double delay) { if (MathUtil.equals(delay, ignitionDelay)) @@ -289,13 +289,13 @@ public class InnerTube extends ThicknessRingComponent ignitionDelay = delay; fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); } - - + + @Override public double getMotorOverhang() { return overhang; } - + @Override public void setMotorOverhang(double overhang) { if (MathUtil.equals(this.overhang, overhang)) @@ -303,34 +303,22 @@ public class InnerTube extends ThicknessRingComponent this.overhang = overhang; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @Override public Coordinate getMotorPosition(String id) { Motor motor = motors.get(id); if (motor == null) { throw new IllegalArgumentException("No motor with id " + id + " defined."); } - + 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. - * + * * @see rocketcomponent.RocketComponent#copy() */ @SuppressWarnings("unchecked") @@ -341,5 +329,5 @@ public class InnerTube extends ThicknessRingComponent ((InnerTube) c).ejectionDelays = (HashMap) ejectionDelays.clone(); return c; } - + } \ No newline at end of file diff --git a/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index a51629a3..70f92a7f 100644 --- a/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -12,13 +12,13 @@ public class LaunchLug extends ExternalComponent implements Coaxial { private double radius; private double thickness; - + private double radialDirection = 0; - + /* These are calculated when the component is first attached to any Rocket */ private double shiftY, shiftZ; - - + + public LaunchLug() { super(Position.MIDDLE); @@ -26,12 +26,12 @@ public class LaunchLug extends ExternalComponent implements Coaxial { thickness = 0.001; length = 0.03; } - - + + public double getOuterRadius () { return radius; } - + public void setOuterRadius (double radius) { if (MathUtil.equals(this.radius, radius)) return; @@ -39,31 +39,31 @@ public class LaunchLug extends ExternalComponent implements Coaxial { this.thickness = Math.min(this.thickness, this.radius); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public double getInnerRadius() { return radius - thickness; } - + public void setInnerRadius(double innerRadius) { setOuterRadius(innerRadius + thickness); } - + public double getThickness() { return thickness; } - + public void setThickness(double thickness) { if (MathUtil.equals(this.thickness, thickness)) return; this.thickness = MathUtil.clamp(thickness, 0, radius); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + public double getRadialDirection() { return radialDirection; } - + public void setRadialDirection(double direction) { direction = MathUtil.reduce180(direction); if (MathUtil.equals(this.radialDirection, direction)) @@ -71,8 +71,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial { this.radialDirection = direction; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + public void setLength(double length) { if (MathUtil.equals(this.length, length)) @@ -80,8 +80,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial { this.length = length; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @@ -90,44 +90,44 @@ public class LaunchLug extends ExternalComponent implements Coaxial { super.setRelativePosition(position); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @Override public void setPositionValue(double value) { super.setPositionValue(value); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + @Override public Coordinate[] shiftCoordinates(Coordinate[] array) { array = super.shiftCoordinates(array); - + for (int i = 0; i < array.length; i++) { array[i] = array[i].add(0, shiftY, shiftZ); } - + return array; } - - + + @Override public void componentChanged(ComponentChangeEvent e) { super.componentChanged(e); - - /* + + /* * shiftY and shiftZ must be computed here since calculating them * in shiftCoordinates() would cause an infinite loop due to .toRelative */ RocketComponent body; double parentRadius; - + for (body = this.getParent(); body != null; body = body.getParent()) { if (body instanceof SymmetricComponent) break; } - + if (body == null) { parentRadius = 0; } else { @@ -139,21 +139,21 @@ public class LaunchLug extends ExternalComponent implements Coaxial { x2 = MathUtil.clamp(x2, 0, body.getLength()); parentRadius = Math.max(s.getRadius(x1), s.getRadius(x2)); } - + shiftY = Math.cos(radialDirection) * (parentRadius + radius); shiftZ = Math.sin(radialDirection) * (parentRadius + radius); - + // System.out.println("Computed shift: y="+shiftY+" z="+shiftZ); } - - + + @Override public double getComponentVolume() { return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness)); } - + @Override public Collection getComponentBounds() { ArrayList set = new ArrayList(); @@ -161,49 +161,39 @@ public class LaunchLug extends ExternalComponent implements Coaxial { addBound(set, length, radius); return set; } - + @Override public Coordinate getComponentCG() { return new Coordinate(length / 2, 0, 0, getComponentMass()); } - + @Override public String getComponentName() { return "Launch lug"; } - + @Override public double getLongitudinalUnitInertia() { // 1/12 * (3 * (r1^2 + r2^2) + h^2) 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(getOuterRadius()))/2; } - + @Override public boolean allowsChildren() { return false; } - + @Override public boolean isCompatible(Class 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); - } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/MassObject.java b/src/net/sf/openrocket/rocketcomponent/MassObject.java index bbd9789d..70d22ff3 100644 --- a/src/net/sf/openrocket/rocketcomponent/MassObject.java +++ b/src/net/sf/openrocket/rocketcomponent/MassObject.java @@ -1,12 +1,12 @@ package net.sf.openrocket.rocketcomponent; -import static net.sf.openrocket.util.MathUtil.pow2; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.MathUtil; import java.util.ArrayList; import java.util.Collection; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.MathUtil; +import static net.sf.openrocket.util.MathUtil.pow2; /** @@ -19,31 +19,31 @@ import net.sf.openrocket.util.MathUtil; * @author Sampo Niskanen */ public abstract class MassObject extends InternalComponent { - + private double radius; - + private double radialPosition; private double radialDirection; - + private double shiftY = 0; private double shiftZ = 0; - - + + public MassObject() { this(0.03, 0.015); } - + public MassObject(double length, double radius) { super(); - + this.length = length; this.radius = radius; - + this.setRelativePosition(Position.TOP); this.setPositionValue(0.0); } - - + + public void setLength(double length) { length = Math.max(length, 0); if (MathUtil.equals(this.length, length)) { @@ -52,13 +52,13 @@ public abstract class MassObject extends InternalComponent { this.length = length; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + public final double getRadius() { return radius; } - - + + public final void setRadius(double radius) { radius = Math.max(radius, 0); if (MathUtil.equals(this.radius, radius)) { @@ -67,13 +67,13 @@ public abstract class MassObject extends InternalComponent { this.radius = radius; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + public final double getRadialPosition() { return radialPosition; } - + public final void setRadialPosition(double radialPosition) { radialPosition = Math.max(radialPosition, 0); if (MathUtil.equals(this.radialPosition, radialPosition)) { @@ -84,11 +84,11 @@ public abstract class MassObject extends InternalComponent { shiftZ = radialPosition * Math.sin(radialDirection); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public final double getRadialDirection() { return radialDirection; } - + public final void setRadialDirection(double radialDirection) { radialDirection = MathUtil.reduce180(radialDirection); if (MathUtil.equals(this.radialDirection, radialDirection)) { @@ -99,8 +99,8 @@ public abstract class MassObject extends InternalComponent { shiftZ = radialPosition * Math.sin(radialDirection); fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /** * Shift the coordinates according to the radial position and direction. */ @@ -111,22 +111,22 @@ public abstract class MassObject extends InternalComponent { } return array; } - + @Override public final Coordinate getComponentCG() { return new Coordinate(length / 2, shiftY, shiftZ, getComponentMass()); } - + @Override public final double getLongitudinalUnitInertia() { return (3 * pow2(radius) + pow2(length)) / 12; } - + @Override public final double getRotationalUnitInertia() { return pow2(radius) / 2; } - + @Override public final Collection getComponentBounds() { Collection c = new ArrayList(); @@ -134,15 +134,5 @@ public abstract class MassObject extends InternalComponent { addBound(c, length, radius); return c; } - - /** - * Accept a visitor to this MassObject in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this MassObject - */ - @Override - public void accept(final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/NoseCone.java b/src/net/sf/openrocket/rocketcomponent/NoseCone.java index 1aa61a4c..229fcbf5 100644 --- a/src/net/sf/openrocket/rocketcomponent/NoseCone.java +++ b/src/net/sf/openrocket/rocketcomponent/NoseCone.java @@ -3,18 +3,18 @@ package net.sf.openrocket.rocketcomponent; /** * Rocket nose cones of various types. Implemented as a transition with the * fore radius == 0. - * + * * @author Sampo Niskanen */ public class NoseCone extends Transition { - - + + /********* Constructors **********/ public NoseCone() { this(Transition.Shape.OGIVE, 6*DEFAULT_RADIUS, DEFAULT_RADIUS); } - + public NoseCone(Transition.Shape type, double length, double radius) { super(); super.setType(type); @@ -27,9 +27,9 @@ public class NoseCone extends Transition { super.setThickness(0.002); super.setLength(length); super.setClipped(false); - + } - + /********** Get/set methods for component parameters **********/ @@ -37,7 +37,7 @@ public class NoseCone extends Transition { public double getForeRadius() { return 0; } - + @Override public void setForeRadius(double r) { // No-op @@ -47,7 +47,7 @@ public class NoseCone extends Transition { public boolean isForeRadiusAutomatic() { return false; } - + @Override public void setForeRadiusAutomatic(boolean b) { // No-op @@ -97,14 +97,14 @@ public class NoseCone extends Transition { public boolean isClipped() { return false; } - + @Override public void setClipped(boolean b) { // No-op } - - + + /********** RocketComponent methods **********/ /** @@ -114,14 +114,5 @@ public class NoseCone extends Transition { public String getComponentName() { return "Nose cone"; } - - /** - * Accept a visitor to this NoseCone in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this NoseCone - */ - @Override - public void accept(ComponentVisitor theVisitor) { - theVisitor.visit(this); - } + } diff --git a/src/net/sf/openrocket/rocketcomponent/RadiusRingComponent.java b/src/net/sf/openrocket/rocketcomponent/RadiusRingComponent.java index b3d86251..46a35143 100644 --- a/src/net/sf/openrocket/rocketcomponent/RadiusRingComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/RadiusRingComponent.java @@ -6,16 +6,16 @@ import net.sf.openrocket.util.MathUtil; /** * An inner component that consists of a hollow cylindrical component. This can be * an inner tube, tube coupler, centering ring, bulkhead etc. - * + * * The properties include the inner and outer radii, length and radial position. - * + * * @author Sampo Niskanen */ public abstract class RadiusRingComponent extends RingComponent implements Coaxial { protected double outerRadius = 0; protected double innerRadius = 0; - + @Override public double getOuterRadius() { if (outerRadiusAutomatic && getParent() instanceof RadialParent) { @@ -27,7 +27,7 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi outerRadius = Math.min(((RadialParent)parent).getInnerRadius(pos1), ((RadialParent)parent).getInnerRadius(pos2)); } - + return outerRadius; } @@ -36,7 +36,7 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi r = Math.max(r,0); if (MathUtil.equals(outerRadius, r) && !isOuterRadiusAutomatic()) return; - + outerRadius = r; outerRadiusAutomatic = false; if (getInnerRadius() > r) { @@ -46,8 +46,8 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + @Override public double getInnerRadius() { return innerRadius; @@ -57,18 +57,18 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi r = Math.max(r,0); if (MathUtil.equals(innerRadius, r)) return; - + innerRadius = r; innerRadiusAutomatic = false; if (getOuterRadius() < r) { outerRadius = r; outerRadiusAutomatic = false; } - + fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + @Override public double getThickness() { return Math.max(getOuterRadius() - getInnerRadius(), 0); @@ -76,19 +76,9 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi @Override public void setThickness(double thickness) { double outer = getOuterRadius(); - + thickness = MathUtil.clamp(thickness, 0, outer); setInnerRadius(outer - thickness); } - /** - * Accept a visitor to this RadiusRingComponent in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this RadiusRingComponent - */ - @Override - public void accept (final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - } diff --git a/src/net/sf/openrocket/rocketcomponent/RingComponent.java b/src/net/sf/openrocket/rocketcomponent/RingComponent.java index b6c6cec6..233457c2 100644 --- a/src/net/sf/openrocket/rocketcomponent/RingComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/RingComponent.java @@ -11,90 +11,90 @@ import java.util.List; /** * An inner component that consists of a hollow cylindrical component. This can be * an inner tube, tube coupler, centering ring, bulkhead etc. - * + * * The properties include the inner and outer radii, length and radial position. - * + * * @author Sampo Niskanen */ public abstract class RingComponent extends StructuralComponent implements Coaxial { protected boolean outerRadiusAutomatic = false; protected boolean innerRadiusAutomatic = false; - - + + private double radialDirection = 0; private double radialPosition = 0; - + private double shiftY = 0; private double shiftZ = 0; - - + + @Override public abstract double getOuterRadius(); @Override public abstract void setOuterRadius(double r); - + @Override - public abstract double getInnerRadius(); + public abstract double getInnerRadius(); @Override public abstract void setInnerRadius(double r); - + @Override public abstract double getThickness(); public abstract void setThickness(double thickness); - - + + public final boolean isOuterRadiusAutomatic() { return outerRadiusAutomatic; } - + protected void setOuterRadiusAutomatic(boolean auto) { if (auto == outerRadiusAutomatic) return; outerRadiusAutomatic = auto; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + public final boolean isInnerRadiusAutomatic() { return innerRadiusAutomatic; } - + protected void setInnerRadiusAutomatic(boolean auto) { if (auto == innerRadiusAutomatic) return; innerRadiusAutomatic = auto; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - - - + + + + public final void setLength(double length) { double l = Math.max(length,0); if (this.length == l) return; - + this.length = l; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + /** * Return the radial direction of displacement of the component. Direction 0 * is equivalent to the Y-direction. - * + * * @return the radial direction. */ public double getRadialDirection() { return radialDirection; } - + /** * Set the radial direction of displacement of the component. Direction 0 * is equivalent to the Y-direction. - * + * * @param dir the radial direction. */ public void setRadialDirection(double dir) { @@ -107,23 +107,23 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - - + + + /** * Return the radial position of the component. The position is the distance * of the center of the component from the center of the parent component. - * + * * @return the radial position. */ public double getRadialPosition() { return radialPosition; } - + /** * Set the radial position of the component. The position is the distance * of the center of the component from the center of the parent component. - * + * * @param pos the radial position. */ public void setRadialPosition(double pos) { @@ -136,25 +136,25 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getRadialShiftY() { return shiftY; } - + public double getRadialShiftZ() { return shiftZ; } - + public void setRadialShift(double y, double z) { radialPosition = Math.hypot(y, z); radialDirection = Math.atan2(z, y); - - // Re-calculate to ensure consistency + + // Re-calculate to ensure consistency shiftY = radialPosition * Math.cos(radialDirection); shiftZ = radialPosition * Math.sin(radialDirection); assert(MathUtil.equals(y, shiftY)); assert(MathUtil.equals(z, shiftZ)); - + fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } @@ -167,8 +167,8 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi return ((Clusterable)this).getClusterConfiguration().getClusterCount(); return 1; } - - + + /** * Shift the coordinates according to the radial position and direction. */ @@ -179,8 +179,8 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi } return array; } - - + + @Override public Collection getComponentBounds() { List bounds = new ArrayList(); @@ -188,9 +188,9 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi addBound(bounds,length,getOuterRadius()); return bounds; } - - + + @Override public Coordinate getComponentCG() { return new Coordinate(length/2, 0, 0, getComponentMass()); @@ -201,7 +201,7 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi return ringMass(getOuterRadius(), getInnerRadius(), getLength(), getMaterial().getDensity()) * getClusterCount(); } - + @Override public double getLongitudinalUnitInertia() { @@ -213,14 +213,4 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi return ringRotationalUnitInertia(getOuterRadius(), getInnerRadius()); } - /** - * Accept a visitor to this RingComponent in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this RingComponent - */ - @Override - public void accept (final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } - } diff --git a/src/net/sf/openrocket/rocketcomponent/Rocket.java b/src/net/sf/openrocket/rocketcomponent/Rocket.java index 3a284534..64c1f687 100644 --- a/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -1,25 +1,15 @@ package net.sf.openrocket.rocketcomponent; -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.ArrayList; -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 net.sf.openrocket.util.*; + +import javax.swing.event.ChangeListener; +import javax.swing.event.EventListenerList; +import java.util.*; /** @@ -28,46 +18,46 @@ import net.sf.openrocket.util.UniqueID; * (eg. the rocket listener lists) and the methods defined in RocketComponent call these. * It also defines some other methods that concern the whole rocket, and helper methods * that keep information about the program state. - * + * * @author Sampo Niskanen */ public class Rocket extends RocketComponent { private static final LogHelper log = Application.getLogger(); - + public static final double DEFAULT_REFERENCE_LENGTH = 0.01; - + /** * List of component change listeners. */ private EventListenerList listenerList = new EventListenerList(); - + /** * When freezeList != null, events are not dispatched but stored in the list. * When the structure is thawed, a single combined event will be fired. */ private List freezeList = null; - + private int modID; private int massModID; private int aeroModID; private int treeModID; private int functionalModID; - + private ReferenceType refType = ReferenceType.MAXIMUM; // Set in constructor private double customReferenceLength = DEFAULT_REFERENCE_LENGTH; - + // The default configuration used in dialogs private final Configuration defaultConfiguration; - + private String designer = ""; private String revision = ""; - + // Motor configuration list private ArrayList motorConfigurationIDs = new ArrayList(); @@ -75,15 +65,15 @@ public class Rocket extends RocketComponent { { motorConfigurationIDs.add(null); } - + // Does the rocket have a perfect finish (a notable amount of laminar flow) private boolean perfectFinish = false; - - + + ///////////// Constructor ///////////// - + public Rocket() { super(RocketComponent.Position.AFTER); modID = UniqueID.next(); @@ -93,150 +83,150 @@ public class Rocket extends RocketComponent { functionalModID = modID; defaultConfiguration = new Configuration(this); } - - + + public String getDesigner() { checkState(); return designer; } - + public void setDesigner(String s) { if (s == null) s = ""; designer = s; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + public String getRevision() { checkState(); return revision; } - + public void setRevision(String s) { if (s == null) s = ""; revision = s; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** * Return the number of stages in this rocket. - * + * * @return the number of stages in this rocket. */ public int getStageCount() { checkState(); return this.getChildCount(); } - - + + /** * Return the non-negative modification ID of this rocket. The ID is changed - * every time any change occurs in the rocket. This can be used to check + * every time any change occurs in the rocket. This can be used to check * whether it is necessary to void cached data in cases where listeners can not * or should not be used. *

* Three other modification IDs are also available, {@link #getMassModID()}, - * {@link #getAerodynamicModID()} {@link #getTreeModID()}, which change every time - * a mass change, aerodynamic change, or tree change occur. Even though the values - * of the different modification ID's may be equal, they should be treated totally + * {@link #getAerodynamicModID()} {@link #getTreeModID()}, which change every time + * a mass change, aerodynamic change, or tree change occur. Even though the values + * of the different modification ID's may be equal, they should be treated totally * separate. *

* Note that undo events restore the modification IDs that were in use at the * corresponding undo level. Subsequent modifications, however, produce modIDs * distinct from those already used. - * + * * @return a unique ID number for this modification state. */ public int getModID() { return modID; } - + /** * Return the non-negative mass modification ID of this rocket. See * {@link #getModID()} for details. - * + * * @return a unique ID number for this mass-modification state. */ public int getMassModID() { return massModID; } - + /** * Return the non-negative aerodynamic modification ID of this rocket. See * {@link #getModID()} for details. - * + * * @return a unique ID number for this aerodynamic-modification state. */ public int getAerodynamicModID() { return aeroModID; } - + /** * Return the non-negative tree modification ID of this rocket. See * {@link #getModID()} for details. - * + * * @return a unique ID number for this tree-modification state. */ public int getTreeModID() { return treeModID; } - + /** * Return the non-negative functional modificationID of this rocket. * This changes every time a functional change occurs. - * + * * @return a unique ID number for this functional modification state. */ public int getFunctionalModID() { return functionalModID; } - - + + public ReferenceType getReferenceType() { checkState(); return refType; } - + public void setReferenceType(ReferenceType type) { if (refType == type) return; refType = type; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + public double getCustomReferenceLength() { checkState(); return customReferenceLength; } - + public void setCustomReferenceLength(double length) { if (MathUtil.equals(customReferenceLength, length)) return; - + this.customReferenceLength = Math.max(length, 0.001); - + if (refType == ReferenceType.CUSTOM) { fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } } - - + + /** * Set whether the rocket has a perfect finish. This will affect whether the * boundary layer is assumed to be fully turbulent or not. - * + * * @param perfectFinish whether the finish is perfect. */ public void setPerfectFinish(boolean perfectFinish) { @@ -245,19 +235,19 @@ public class Rocket extends RocketComponent { this.perfectFinish = perfectFinish; fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE); } - - + + /** * Get whether the rocket has a perfect finish. - * + * * @return the perfectFinish */ public boolean isPerfectFinish() { return perfectFinish; } - - + + @@ -273,10 +263,10 @@ public class Rocket extends RocketComponent { copy.motorConfigurationNames = (HashMap) this.motorConfigurationNames.clone(); copy.resetListeners(); - + return copy; } - + /** * Load the rocket structure from the source. The method loads the fields of this * Rocket object and copies the references to siblings from the source. @@ -289,10 +279,10 @@ public class Rocket extends RocketComponent { */ @SuppressWarnings("unchecked") public void loadFrom(Rocket r) { - + // Store list of components to invalidate after event has been fired List toInvalidate = this.copyFrom(r); - + int type = ComponentChangeEvent.UNDO_CHANGE | ComponentChangeEvent.NONFUNCTIONAL_CHANGE; if (this.massModID != r.massModID) type |= ComponentChangeEvent.MASS_CHANGE; @@ -300,7 +290,7 @@ public class Rocket extends RocketComponent { type |= ComponentChangeEvent.AERODYNAMIC_CHANGE; // Loading a rocket is always a tree change since the component objects change type |= ComponentChangeEvent.TREE_CHANGE; - + this.modID = r.modID; this.massModID = r.massModID; this.aeroModID = r.aeroModID; @@ -308,31 +298,31 @@ public class Rocket extends RocketComponent { this.functionalModID = r.functionalModID; this.refType = r.refType; this.customReferenceLength = r.customReferenceLength; - + this.motorConfigurationIDs = r.motorConfigurationIDs.clone(); this.motorConfigurationNames = (HashMap) r.motorConfigurationNames.clone(); this.perfectFinish = r.perfectFinish; - + String id = defaultConfiguration.getMotorConfigurationID(); if (!this.motorConfigurationIDs.contains(id)) defaultConfiguration.setMotorConfigurationID(null); - + this.checkComponentStructure(); - + fireComponentChangeEvent(type); - + // Invalidate obsolete components after event for (RocketComponent c : toInvalidate) { c.invalidate(); } } - - + + /////// Implement the ComponentChangeListener lists - + /** * Creates a new EventListenerList for this component. This is necessary when cloning * the structure. @@ -341,15 +331,15 @@ public class Rocket extends RocketComponent { // System.out.println("RESETTING LISTENER LIST of Rocket "+this); listenerList = new EventListenerList(); } - - + + public void printListeners() { System.out.println("" + this + " has " + listenerList.getListenerCount() + " listeners:"); Object[] list = listenerList.getListenerList(); for (int i = 1; i < list.length; i += 2) System.out.println(" " + ((i + 1) / 2) + ": " + list[i]); } - + @Override public void addComponentChangeListener(ComponentChangeListener l) { checkState(); @@ -357,15 +347,15 @@ public class Rocket extends RocketComponent { log.verbose("Added ComponentChangeListener " + l + ", current number of listeners is " + listenerList.getListenerCount()); } - + @Override public void removeComponentChangeListener(ComponentChangeListener l) { listenerList.remove(ComponentChangeListener.class, l); log.verbose("Removed ComponentChangeListener " + l + ", current number of listeners is " + listenerList.getListenerCount()); } - - + + @Override public void addChangeListener(ChangeListener l) { checkState(); @@ -373,21 +363,21 @@ public class Rocket extends RocketComponent { log.verbose("Added ChangeListener " + l + ", current number of listeners is " + listenerList.getListenerCount()); } - + @Override public void removeChangeListener(ChangeListener l) { listenerList.remove(ChangeListener.class, l); log.verbose("Removed ChangeListener " + l + ", current number of listeners is " + listenerList.getListenerCount()); } - - + + @Override protected void fireComponentChangeEvent(ComponentChangeEvent e) { mutex.lock("fireComponentChangeEvent"); try { checkState(); - + // Update modification ID's only for normal (not undo/redo) events if (!e.isUndoChange()) { modID = UniqueID.next(); @@ -400,22 +390,22 @@ public class Rocket extends RocketComponent { if (e.getType() != ComponentChangeEvent.NONFUNCTIONAL_CHANGE) functionalModID = modID; } - + // Check whether frozen if (freezeList != null) { log.debug("Rocket is in frozen state, adding event " + e + " info freeze list"); freezeList.add(e); return; } - + log.debug("Firing rocket change event " + e); - + // Notify all components first Iterator iterator = this.iterator(true); while (iterator.hasNext()) { iterator.next().componentChanged(e); } - + // Notify all listeners Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { @@ -429,13 +419,13 @@ public class Rocket extends RocketComponent { mutex.unlock("fireComponentChangeEvent"); } } - - + + /** * Freezes the rocket structure from firing any events. This may be performed to * combine several actions on the structure into a single large action. * thaw() must always be called afterwards. - * + * * NOTE: Always use a try/finally to ensure thaw() is called: *

 	 *     Rocket r = c.getRocket();
@@ -446,7 +436,7 @@ public class Rocket extends RocketComponent {
 	 *         r.thaw();
 	 *     }
 	 * 
- * + * * @see #thaw() */ public void freeze() { @@ -459,7 +449,7 @@ public class Rocket extends RocketComponent { "freezeList=" + freezeList); } } - + /** * Thaws a frozen rocket structure and fires a combination of the events fired during * the freeze. The event type is a combination of those fired and the source is the @@ -478,9 +468,9 @@ public class Rocket extends RocketComponent { freezeList = null; return; } - + log.debug("Thawing rocket, freezeList=" + freezeList); - + int type = 0; Object c = null; for (ComponentChangeEvent e : freezeList) { @@ -488,44 +478,44 @@ public class Rocket extends RocketComponent { c = e.getSource(); } freezeList = null; - + fireComponentChangeEvent(new ComponentChangeEvent((RocketComponent) c, type)); } - - + + //////// Motor configurations //////// - + /** * Return the default configuration. This should be used in the user interface * to ensure a consistent rocket configuration between dialogs. It should NOT * be used in simulations not relating to the UI. - * + * * @return the default {@link Configuration}. */ public Configuration getDefaultConfiguration() { checkState(); return defaultConfiguration; } - - + + /** * Return an array of the motor configuration IDs. This array is guaranteed * to contain the null ID as the first element. - * + * * @return an array of the motor configuration IDs. */ public String[] getMotorConfigurationIDs() { checkState(); return motorConfigurationIDs.toArray(new String[0]); } - + /** * Add a new motor configuration ID to the motor configurations. The new ID * is returned. - * + * * @return the new motor configuration ID. */ public String newMotorConfigurationID() { @@ -535,10 +525,10 @@ public class Rocket extends RocketComponent { fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); return id; } - + /** * Add a specified motor configuration ID to the motor configurations. - * + * * @param id the motor configuration ID. * @return true if successful, false if the ID was already used. */ @@ -550,11 +540,11 @@ public class Rocket extends RocketComponent { fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); return true; } - + /** * Remove a motor configuration ID from the configuration IDs. The null * ID cannot be removed, and an attempt to remove it will be silently ignored. - * + * * @param id the motor configuration ID to remove */ public void removeMotorConfigurationID(String id) { @@ -564,11 +554,11 @@ public class Rocket extends RocketComponent { motorConfigurationIDs.remove(id); fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE); } - - + + /** * Check whether id is a valid motor configuration ID. - * + * * @param id the configuration ID. * @return whether a motor configuration with that ID exists. */ @@ -576,12 +566,12 @@ public class Rocket extends RocketComponent { checkState(); return motorConfigurationIDs.contains(id); } - - + + /** * Check whether the given motor configuration ID has motors defined for it. - * + * * @param id the motor configuration ID (may be invalid). * @return whether any motors are defined for it. */ @@ -589,11 +579,11 @@ public class Rocket extends RocketComponent { checkState(); if (id == null) return false; - + Iterator iterator = this.iterator(); while (iterator.hasNext()) { RocketComponent c = iterator.next(); - + if (c instanceof MotorMount) { MotorMount mount = (MotorMount) c; if (!mount.isMotorMount()) @@ -605,12 +595,12 @@ public class Rocket extends RocketComponent { } return false; } - - + + /** * Return the user-set name of the motor configuration. If no name has been set, * returns an empty string (not null). - * + * * @param id the motor configuration id * @return the configuration name */ @@ -623,12 +613,12 @@ public class Rocket extends RocketComponent { return ""; return s; } - - + + /** * Set the name of the motor configuration. A name can be unset by passing * null or an empty string. - * + * * @param id the motor configuration id * @param name the name for the motor configuration */ @@ -637,29 +627,29 @@ public class Rocket extends RocketComponent { motorConfigurationNames.put(id, name); fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** - * Return either the motor configuration name (if set) or its description. - * + * Return either the motor configuration name (if set) or its description. + * * @param id the motor configuration ID. * @return a textual representation of the configuration */ public String getMotorConfigurationNameOrDescription(String id) { checkState(); String name; - + name = getMotorConfigurationName(id); if (name != null && !name.equals("")) return name; - + return getMotorConfigurationDescription(id); } - + /** - * Return a description for the motor configuration, generated from the motor + * Return a description for the motor configuration, generated from the motor * designations of the components. - * + * * @param id the motor configuration ID. * @return a textual representation of the configuration */ @@ -668,59 +658,59 @@ public class Rocket extends RocketComponent { checkState(); String name; int motorCount = 0; - + // Generate the description - + // First iterate over each stage and store the designations of each motor List> list = new ArrayList>(); List currentList = null; - + Iterator iterator = this.iterator(); while (iterator.hasNext()) { RocketComponent c = iterator.next(); - + if (c instanceof Stage) { - + currentList = new ArrayList(); list.add(currentList); - + } else if (c instanceof MotorMount) { - + MotorMount mount = (MotorMount) c; Motor motor = mount.getMotor(id); - + if (mount.isMotorMount() && motor != null) { String designation = motor.getDesignation(mount.getMotorDelay(id)); - + for (int i = 0; i < mount.getMotorCount(); i++) { currentList.add(designation); motorCount++; } } - + } } - + if (motorCount == 0) { return "[No motors]"; } - - // Change multiple occurrences of a motor to n x motor + + // Change multiple occurrences of a motor to n x motor List stages = new ArrayList(); - + for (List stage : list) { String stageName = ""; String previous = null; int count = 0; - + Collections.sort(stage); for (String current : stage) { if (current.equals(previous)) { - + count++; - + } else { - + if (previous != null) { String s = ""; if (count > 1) { @@ -728,16 +718,16 @@ public class Rocket extends RocketComponent { } else { s = previous; } - + if (stageName.equals("")) stageName = s; else stageName = stageName + "," + s; } - + previous = current; count = 1; - + } } if (previous != null) { @@ -747,16 +737,16 @@ public class Rocket extends RocketComponent { } else { s = previous; } - + if (stageName.equals("")) stageName = s; else stageName = stageName + "," + s; } - + stages.add(stageName); } - + name = "["; for (int i = 0; i < stages.size(); i++) { String s = stages.get(i); @@ -770,57 +760,57 @@ public class Rocket extends RocketComponent { name += "]"; return name; } - - + + //////// Obligatory component information - + @Override public String getComponentName() { return "Rocket"; } - + @Override public Coordinate getComponentCG() { return new Coordinate(0, 0, 0, 0); } - + @Override public double getComponentMass() { return 0; } - + @Override public double getLongitudinalUnitInertia() { return 0; } - + @Override public double getRotationalUnitInertia() { return 0; } - + @Override public Collection getComponentBounds() { return Collections.emptyList(); } - + @Override public boolean isAerodynamic() { return false; } - + @Override public boolean isMassive() { return false; } - + @Override public boolean allowsChildren() { return true; } - + /** * Allows only Stage components to be added to the type Rocket. */ @@ -828,14 +818,5 @@ public class Rocket extends RocketComponent { public boolean isCompatible(Class type) { return (Stage.class.isAssignableFrom(type)); } - - /** - * Accept a visitor to this Rocket in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this Rocket - */ - @Override - public void accept(final ComponentVisitor theVisitor) { - theVisitor.visit(this); - } + } diff --git a/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 8573caa6..e109f6b3 100644 --- a/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1,32 +1,19 @@ package net.sf.openrocket.rocketcomponent; -import java.awt.Color; -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Deque; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -import javax.swing.event.ChangeListener; - import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.ArrayList; -import net.sf.openrocket.util.BugException; -import net.sf.openrocket.util.ChangeSource; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.Invalidator; -import net.sf.openrocket.util.LineStyle; -import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.SafetyMutex; -import net.sf.openrocket.util.UniqueID; - - -public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable, - Visitable { +import net.sf.openrocket.util.*; + +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.util.*; +import java.util.List; + + +public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable { private static final LogHelper log = Application.getLogger(); - + /* * Text is suitable to the form * Position relative to: @@ -42,38 +29,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab AFTER("After the parent component"), /** Specify an absolute X-coordinate position. */ ABSOLUTE("Tip of the nose cone"); - + private String title; - + Position(String title) { this.title = title; } - + @Override public String toString() { return title; } } - + /** * A safety mutex that can be used to prevent concurrent access to this component. */ protected SafetyMutex mutex = SafetyMutex.newInstance(); - + //////// Parent/child trees /** * Parent component of the current component, or null if none exists. */ private RocketComponent parent = null; - + /** * List of child components of this component. */ private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>(); - + //////// Parameters common to all components: - + /** * Characteristic length of the component. This is used in calculating the coordinate * transformations and positions of other components in reference to this component. @@ -81,50 +68,50 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * By default it is zero, i.e. no translation. */ protected double length = 0; - + /** * Positioning of this component relative to the parent component. */ protected Position relativePosition; - + /** * Offset of the position of this component relative to the normal position given by * relativePosition. By default zero, i.e. no position change. */ protected double position = 0; - + // Color of the component, null means to use the default color private Color color = null; private LineStyle lineStyle = null; - + // Override mass/CG private double overrideMass = 0; private boolean massOverriden = false; private double overrideCGX = 0; private boolean cgOverriden = false; - + private boolean overrideSubcomponents = false; - + // User-given name of the component private String name = null; - + // User-specified comment private String comment = ""; - + // Unique ID of the component private String id = null; - + /** * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}. */ private Invalidator invalidator = new Invalidator(this); - - + + //// NOTE !!! All fields must be copied in the method copyFrom()! //// - + /** @@ -137,74 +124,74 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.relativePosition = relativePosition; newID(); } - + //////////// Methods that must be implemented //////////// - + /** * Static component name. The name may not vary of the parameters, it must be static. */ public abstract String getComponentName(); // Static component type name - + /** * Return the component mass (regardless of mass overriding). */ public abstract double getComponentMass(); // Mass of non-overridden component - + /** * Return the component CG and mass (regardless of CG or mass overriding). */ public abstract Coordinate getComponentCG(); // CG of non-overridden component - + /** - * Return the longitudinal (around the y- or z-axis) unitary moment of inertia. + * Return the longitudinal (around the y- or z-axis) unitary moment of inertia. * The unitary moment of inertia is the moment of inertia with the assumption that * the mass of the component is one kilogram. The inertia is measured in * respect to the non-overridden CG. - * + * * @return the longitudinal unitary moment of inertia of this component. */ public abstract double getLongitudinalUnitInertia(); - - + + /** - * Return the rotational (around the x-axis) unitary moment of inertia. + * Return the rotational (around the x-axis) unitary moment of inertia. * The unitary moment of inertia is the moment of inertia with the assumption that * the mass of the component is one kilogram. The inertia is measured in * respect to the non-overridden CG. - * + * * @return the rotational unitary moment of inertia of this component. */ public abstract double getRotationalUnitInertia(); - - + + /** * Test whether this component allows any children components. This method must * return true if and only if {@link #isCompatible(Class)} returns true for any * rocket component class. - * + * * @return <code>true</code> if children can be attached to this component, <code>false</code> otherwise. */ public abstract boolean allowsChildren(); - + /** * Test whether the given component type can be added to this component. This type safety * is enforced by the <code>addChild()</code> methods. The return value of this method * may change to reflect the current state of this component (e.g. two components of some * type cannot be placed as children). - * + * * @param type The RocketComponent class type to add. * @return Whether such a component can be added. */ public abstract boolean isCompatible(Class<? extends RocketComponent> type); - - + + /* Non-abstract helper method */ /** * Test whether the given component can be added to this component. This is equivalent * to calling <code>isCompatible(c.getClass())</code>. - * + * * @param c Component to test. * @return Whether the component can be added. * @see #isCompatible(Class) @@ -213,33 +200,33 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return isCompatible(c.getClass()); } - - + + /** * Return a collection of bounding coordinates. The coordinates must be such that * the component is fully enclosed in their convex hull. - * + * * @return a collection of coordinates that bound the component. */ public abstract Collection<Coordinate> getComponentBounds(); - + /** * Return true if the component may have an aerodynamic effect on the rocket. */ public abstract boolean isAerodynamic(); - + /** * Return true if the component may have an effect on the rocket's mass. */ public abstract boolean isMassive(); - - + + //////////// Methods that may be overridden //////////// - + /** * Shift the coordinates in the array corresponding to radial movement. A component @@ -248,7 +235,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * coordinate for each cluster. * <p> * The default implementation simply returns the array, and thus produces no shift. - * + * * @param c an array of coordinates to shift. * @return an array of shifted coordinates. The method may modify the contents * of the passed array and return the array itself. @@ -257,28 +244,28 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); return c; } - - + + /** - * Called when any component in the tree fires a ComponentChangeEvent. This is by - * default a no-op, but subclasses may override this method to e.g. invalidate - * cached data. The overriding method *must* call + * Called when any component in the tree fires a ComponentChangeEvent. This is by + * default a no-op, but subclasses may override this method to e.g. invalidate + * cached data. The overriding method *must* call * <code>super.componentChanged(e)</code> at some point. - * + * * @param e The event fired */ protected void componentChanged(ComponentChangeEvent e) { // No-op checkState(); } - - + + /** * Return the user-provided name of the component, or the component base * name if the user-provided name is empty. This can be used in the UI. - * + * * @return A string describing the component. */ @Override @@ -289,8 +276,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab else return name; } - - + + /** * Create a string describing the basic component structure from this component downwards. * @return a string containing the rocket structure @@ -305,7 +292,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.unlock("toDebugString"); } } - + private void toDebugString(StringBuilder sb) { sb.append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this)); sb.append("[\"").append(this.getName()).append('"'); @@ -315,26 +302,26 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } sb.append(']'); } - - + + /** * Make a deep copy of the rocket component tree structure from this component * downwards for copying purposes. Each component in the copy will be assigned * a new component ID, making it a safe copy. This method does not fire any events. - * + * * @return A deep copy of the structure. */ public final RocketComponent copy() { RocketComponent clone = copyWithOriginalID(); - + Iterator<RocketComponent> iterator = clone.iterator(true); while (iterator.hasNext()) { iterator.next().newID(); } return clone; } - - + + /** * Make a deep copy of the rocket component tree structure from this component @@ -343,13 +330,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * undo/redo mechanism. This method should not be used for other purposes, * such as copy/paste. This method does not fire any events. * <p> - * This method must be overridden by any component that refers to mutable objects, + * This method must be overridden by any component that refers to mutable objects, * or if some fields should not be copied. This should be performed by * <code>RocketComponent c = super.copyWithOriginalID();</code> and then cloning/modifying * the appropriate fields. * <p> * This is not performed as serializing/deserializing for performance reasons. - * + * * @return A deep copy of the structure. */ protected RocketComponent copyWithOriginalID() { @@ -362,14 +349,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } catch (CloneNotSupportedException e) { throw new BugException("CloneNotSupportedException encountered, report a bug!", e); } - + // Reset the mutex clone.mutex = SafetyMutex.newInstance(); - + // Reset all parent/child information clone.parent = null; clone.children = new ArrayList<RocketComponent>(); - + // Add copied children to the structure without firing events. for (RocketComponent child : this.children) { RocketComponent childCopy = child.copyWithOriginalID(); @@ -377,33 +364,23 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab clone.children.add(childCopy); childCopy.parent = clone; } - + this.checkComponentStructure(); clone.checkComponentStructure(); - + return clone; } finally { mutex.unlock("copyWithOriginalID"); } } - - - /** - * 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 ////////// - + /** * Return the color of the object to use in 2D figures, or <code>null</code> * to use the default color. @@ -412,26 +389,26 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return color; } - + /** - * Set the color of the object to use in 2D figures. + * Set the color of the object to use in 2D figures. */ public final void setColor(Color c) { if ((color == null && c == null) || (color != null && color.equals(c))) return; - + checkState(); this.color = c; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + public final LineStyle getLineStyle() { mutex.verify(); return lineStyle; } - + public final void setLineStyle(LineStyle style) { if (this.lineStyle == style) return; @@ -439,25 +416,25 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.lineStyle = style; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** * Get the current override mass. The mass is not necessarily in use * at the moment. - * + * * @return the override mass */ public final double getOverrideMass() { mutex.verify(); return overrideMass; } - + /** * Set the current override mass. The mass is not set to use by this * method. - * + * * @param m the override mass */ public final void setOverrideMass(double m) { @@ -468,21 +445,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (massOverriden) fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + /** * Return whether mass override is active for this component. This does NOT * take into account whether a parent component is overriding the mass. - * + * * @return whether the mass is overridden */ public final boolean isMassOverridden() { mutex.verify(); return massOverriden; } - + /** * Set whether the mass is currently overridden. - * + * * @param o whether the mass is overridden */ public final void setMassOverridden(boolean o) { @@ -493,34 +470,34 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab massOverriden = o; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /** * Return the current override CG. The CG is not necessarily overridden. - * + * * @return the override CG */ public final Coordinate getOverrideCG() { mutex.verify(); return getComponentCG().setX(overrideCGX); } - + /** * Return the x-coordinate of the current override CG. - * + * * @return the x-coordinate of the override CG. */ public final double getOverrideCGX() { mutex.verify(); return overrideCGX; } - + /** * Set the current override CG to (x,0,0). - * + * * @param x the x-coordinate of the override CG to set. */ public final void setOverrideCGX(double x) { @@ -533,20 +510,20 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab else fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - + /** * Return whether the CG is currently overridden. - * + * * @return whether the CG is overridden */ public final boolean isCGOverridden() { mutex.verify(); return cgOverriden; } - + /** * Set whether the CG is currently overridden. - * + * * @param o whether the CG is overridden */ public final void setCGOverridden(boolean o) { @@ -557,8 +534,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab cgOverriden = o; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /** * Return whether the mass and/or CG override overrides all subcomponent values @@ -567,19 +544,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * always or never overrides subcomponents. In this case the subclass should * also override {@link #isOverrideSubcomponentsEnabled()} to return * <code>false</code>. - * + * * @return whether the current mass and/or CG override overrides subcomponents as well. */ public boolean getOverrideSubcomponents() { mutex.verify(); return overrideSubcomponents; } - - + + /** * Set whether the mass and/or CG override overrides all subcomponent values * as well. See {@link #getOverrideSubcomponents()} for details. - * + * * @param override whether the mass and/or CG override overrides all subcomponent. */ public void setOverrideSubcomponents(boolean override) { @@ -590,7 +567,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab overrideSubcomponents = override; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + /** * Return whether the option to override all subcomponents is enabled or not. * The default implementation returns <code>false</code> if neither mass nor @@ -598,15 +575,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * <p> * This method may be overridden if the setting of overriding subcomponents * cannot be set. - * + * * @return whether the option to override subcomponents is currently enabled. */ public boolean isOverrideSubcomponentsEnabled() { mutex.verify(); return isCGOverridden() || isMassOverridden(); } - - + + /** @@ -616,7 +593,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return name; } - + /** * Set the user-defined name of the component. If name==null, sets the name to * the default name, currently the component name. @@ -632,22 +609,22 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.name = name; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** * Return the comment of the component. The component may contain multiple lines * using \n as a newline separator. - * + * * @return the comment of the component. */ public final String getComment() { mutex.verify(); return comment; } - + /** * Set the comment of the component. - * + * * @param comment the comment of the component. */ public final void setComment(String comment) { @@ -660,18 +637,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.comment = comment; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - - + + /** * Returns the unique ID of the component. - * + * * @return the ID of the component. */ public final String getID() { return id; } - + /** * Generate a new ID for this component. */ @@ -679,15 +656,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); this.id = UniqueID.uuid(); } - - + + /** * Get the characteristic length of the component, for example the length of a body tube * of the length of the root chord of a fin. This is used in positioning the component * relative to its parent. - * + * * If the length of a component is settable, the class must define the setter method * itself. */ @@ -695,7 +672,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return length; } - + /** * Get the positioning of the component relative to its parent component. * This is one of the enums of {@link Position}. A setter method is not provided, @@ -705,8 +682,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.verify(); return relativePosition; } - - + + /** * Set the positioning of the component relative to its parent component. * The actual position of the component is maintained to the best ability. @@ -715,59 +692,59 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * do not support setting the relative position. A component that does support * it should override this with a public method that simply calls this * supermethod AND fire a suitable ComponentChangeEvent. - * + * * @param position the relative positioning. */ protected void setRelativePosition(RocketComponent.Position position) { if (this.relativePosition == position) return; checkState(); - + // Update position so as not to move the component if (this.parent != null) { double thisPos = this.toRelative(Coordinate.NUL, this.parent)[0].x; - + switch (position) { case ABSOLUTE: this.position = this.toAbsolute(Coordinate.NUL)[0].x; break; - + case TOP: this.position = thisPos; break; - + case MIDDLE: this.position = thisPos - (this.parent.length - this.length) / 2; break; - + case BOTTOM: this.position = thisPos - (this.parent.length - this.length); break; - + default: throw new BugException("Unknown position type: " + position); } } - + this.relativePosition = position; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + /** * Get the position value of the component. The exact meaning of the value is * dependent on the current relative positioning. - * + * * @return the positional value. */ public final double getPositionValue() { mutex.verify(); return position; } - - + + /** * Set the position value of the component. The exact meaning of the value * depends on the current relative positioning. @@ -776,7 +753,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * do not support setting the relative position. A component that does support * it should override this with a public method that simply calls this * supermethod AND fire a suitable ComponentChangeEvent. - * + * * @param value the position value of the component. */ public void setPositionValue(double value) { @@ -785,11 +762,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); this.position = value; } - - + + /////////// Coordinate changes /////////// - + /** * Returns coordinate c in absolute coordinates. Equivalent to toComponent(c,null). */ @@ -797,19 +774,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); return toRelative(c, null); } - - + + /** - * Return coordinate <code>c</code> described in the coordinate system of + * Return coordinate <code>c</code> described in the coordinate system of * <code>dest</code>. If <code>dest</code> is <code>null</code> returns * absolute coordinates. * <p> * This method returns an array of coordinates, each of which represents a * position of the coordinate in clustered cases. The array is guaranteed - * to contain at least one element. + * to contain at least one element. * <p> * The current implementation does not support rotating components. - * + * * @param c Coordinate in the component's coordinate system. * @param dest Destination component coordinate system. * @return an array of coordinates describing <code>c</code> in coordinates @@ -823,33 +800,33 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent search = dest; Coordinate[] array = new Coordinate[1]; array[0] = c; - + RocketComponent component = this; while ((component != search) && (component.parent != null)) { - + array = component.shiftCoordinates(array); - + switch (component.relativePosition) { case TOP: for (int i = 0; i < array.length; i++) { array[i] = array[i].add(component.position, 0, 0); } break; - + case MIDDLE: for (int i = 0; i < array.length; i++) { array[i] = array[i].add(component.position + (component.parent.length - component.length) / 2, 0, 0); } break; - + case BOTTOM: for (int i = 0; i < array.length; i++) { array[i] = array[i].add(component.position + (component.parent.length - component.length), 0, 0); } break; - + case AFTER: // Add length of all previous brother-components with POSITION_RELATIVE_AFTER int index = component.parent.children.indexOf(component); @@ -865,48 +842,48 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab array[i] = array[i].add(component.position + component.parent.length, 0, 0); } break; - + case ABSOLUTE: search = null; // Requires back-search if dest!=null if (Double.isNaN(absoluteX)) { absoluteX = component.position; } break; - + default: throw new BugException("Unknown relative positioning type of component" + component + ": " + component.relativePosition); } - + component = component.parent; // parent != null } - + if (!Double.isNaN(absoluteX)) { for (int i = 0; i < array.length; i++) { array[i] = array[i].setX(absoluteX + c.x); } } - + // Check whether destination has been found or whether to backtrack - // TODO: LOW: Backtracking into clustered components uses only one component + // TODO: LOW: Backtracking into clustered components uses only one component if ((dest != null) && (component != dest)) { Coordinate[] origin = dest.toAbsolute(Coordinate.NUL); for (int i = 0; i < array.length; i++) { array[i] = array[i].sub(origin[0]); } } - + return array; } finally { mutex.unlock("toRelative"); } } - - + + /** - * Recursively sum the lengths of all subcomponents that have position + * Recursively sum the lengths of all subcomponents that have position * Position.AFTER. - * + * * @return Sum of the lengths. */ private final double getTotalLength() { @@ -924,14 +901,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.unlock("getTotalLength"); } } - - + + /////////// Total mass and CG calculation //////////// - + /** * Return the (possibly overridden) mass of component. - * + * * @return The mass of the component or the given override mass. */ public final double getMass() { @@ -940,63 +917,63 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return overrideMass; return getComponentMass(); } - + /** * Return the (possibly overridden) center of gravity and mass. - * + * * Returns the CG with the weight of the coordinate set to the weight of the component. * Both CG and mass may be separately overridden. - * + * * @return The CG of the component or the given override CG. */ public final Coordinate getCG() { checkState(); if (cgOverriden) return getOverrideCG().setWeight(getMass()); - + if (massOverriden) return getComponentCG().setWeight(getMass()); - + return getComponentCG(); } - - + + /** * Return the longitudinal (around the y- or z-axis) moment of inertia of this component. * The moment of inertia is scaled in reference to the (possibly overridden) mass * and is relative to the non-overridden CG. - * + * * @return the longitudinal moment of inertia of this component. */ public final double getLongitudinalInertia() { checkState(); return getLongitudinalUnitInertia() * getMass(); } - + /** * Return the rotational (around the y- or z-axis) moment of inertia of this component. * The moment of inertia is scaled in reference to the (possibly overridden) mass * and is relative to the non-overridden CG. - * + * * @return the rotational moment of inertia of this component. */ public final double getRotationalInertia() { checkState(); return getRotationalUnitInertia() * getMass(); } - - + + /////////// Children handling /////////// - + /** * Adds a child to the rocket component tree. The component is added to the end - * of the component's child list. This is a helper method that calls + * of the component's child list. This is a helper method that calls * {@link #addChild(RocketComponent,int)}. - * + * * @param component The component to add. - * @throws IllegalArgumentException if the component is already part of some + * @throws IllegalArgumentException if the component is already part of some * component tree. * @see #addChild(RocketComponent,int) */ @@ -1004,18 +981,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); addChild(component, children.size()); } - - + + /** - * Adds a child to the rocket component tree. The component is added to + * Adds a child to the rocket component tree. The component is added to * the given position of the component's child list. * <p> - * This method may be overridden to enforce more strict component addition rules. + * This method may be overridden to enforce more strict component addition rules. * The tests should be performed first and then this method called. - * + * * @param component The component to add. * @param index Position to add component to. - * @throws IllegalArgumentException If the component is already part of + * @throws IllegalArgumentException If the component is already part of * some component tree. */ public void addChild(RocketComponent component, int index) { @@ -1028,20 +1005,20 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab throw new IllegalStateException("Component " + component.getComponentName() + " not currently compatible with component " + getComponentName()); } - + children.add(index, component); component.parent = this; - + this.checkComponentStructure(); component.checkComponentStructure(); - + fireAddRemoveEvent(component); } - - + + /** * Removes a child from the rocket component tree. - * + * * @param n remove the n'th child. * @throws IndexOutOfBoundsException if n is out of bounds */ @@ -1049,43 +1026,43 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); RocketComponent component = children.remove(n); component.parent = null; - + this.checkComponentStructure(); component.checkComponentStructure(); - + fireAddRemoveEvent(component); } - + /** * Removes a child from the rocket component tree. Does nothing if the component * is not present as a child. - * + * * @param component the component to remove * @return whether the component was a child */ public final boolean removeChild(RocketComponent component) { checkState(); - + component.checkComponentStructure(); - + if (children.remove(component)) { component.parent = null; - + this.checkComponentStructure(); component.checkComponentStructure(); - + fireAddRemoveEvent(component); return true; } return false; } - - + + /** * Move a child to another position. - * + * * @param component the component to move * @param index the component's new position * @throws IllegalArgumentException If an illegal placement was attempted. @@ -1094,15 +1071,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); if (children.remove(component)) { children.add(index, component); - + this.checkComponentStructure(); component.checkComponentStructure(); - + fireAddRemoveEvent(component); } } - - + + /** * Fires an AERODYNAMIC_CHANGE, MASS_CHANGE or OTHER_CHANGE event depending on the * type of component removed. @@ -1117,34 +1094,34 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (c.isMassive()) type |= ComponentChangeEvent.MASS_CHANGE; } - + fireComponentChangeEvent(type); } - - + + public final int getChildCount() { checkState(); this.checkComponentStructure(); return children.size(); } - + public final RocketComponent getChild(int n) { checkState(); this.checkComponentStructure(); return children.get(n); } - + public final List<RocketComponent> getChildren() { checkState(); this.checkComponentStructure(); return children.clone(); } - - + + /** * Returns the position of the child in this components child list, or -1 if the * component is not a child of this component. - * + * * @param child The child to search for. * @return Position in the list or -1 if not found. */ @@ -1153,21 +1130,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.checkComponentStructure(); return children.indexOf(child); } - + /** * Get the parent component of this component. Returns <code>null</code> if the component * has no parent. - * + * * @return The parent of this component or <code>null</code>. */ public final RocketComponent getParent() { checkState(); return parent; } - + /** * Get the root component of the component tree. - * + * * @return The root component of the component tree. */ public final RocketComponent getRoot() { @@ -1177,11 +1154,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab gp = gp.parent; return gp; } - + /** - * Returns the root Rocket component of this component tree. Throws an + * Returns the root Rocket component of this component tree. Throws an * IllegalStateException if the root component is not a Rocket. - * + * * @return The root Rocket component of the component tree. * @throws IllegalStateException If the root component is not a Rocket. */ @@ -1193,12 +1170,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab throw new IllegalStateException("getRocket() called with root component " + r.getComponentName()); } - - + + /** * Return the Stage component that this component belongs to. Throws an * IllegalStateException if a Stage is not in the parentage of this component. - * + * * @return The Stage component this component belongs to. * @throws IllegalStateException if a Stage component is not in the parentage. */ @@ -1212,11 +1189,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } throw new IllegalStateException("getStage() called without Stage as a parent."); } - + /** * Return the stage number of the stage this component belongs to. The stages * are numbered from zero upwards. - * + * * @return the stage number this component belongs to. */ public final int getStageNumber() { @@ -1224,7 +1201,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab if (parent == null) { throw new IllegalArgumentException("getStageNumber() called for root component"); } - + RocketComponent stage = this; while (!(stage instanceof Stage)) { stage = stage.parent; @@ -1235,13 +1212,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } return stage.parent.getChildPosition(stage); } - - + + /** * Find a component with the given ID. The component tree is searched from this component * down (including this component) for the ID and the corresponding component is returned, * or null if not found. - * + * * @param idToFind ID to search for. * @return The component with the ID, or null if not found. */ @@ -1255,8 +1232,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } return null; } - - + + // TODO: Move these methods elsewhere (used only in SymmetricComponent) public final RocketComponent getPreviousComponent() { checkState(); @@ -1287,52 +1264,52 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab c = c.getChild(c.getChildCount() - 1); return c; } - + // TODO: Move these methods elsewhere (used only in SymmetricComponent) public final RocketComponent getNextComponent() { checkState(); if (getChildCount() > 0) return getChild(0); - + RocketComponent current = this; RocketComponent nextParent = this.parent; - + while (nextParent != null) { int pos = nextParent.getChildPosition(current); if (pos < nextParent.getChildCount() - 1) return nextParent.getChild(pos + 1); - + current = nextParent; nextParent = current.parent; } return null; } - - + + /////////// Event handling ////////// // // Listener lists are provided by the root Rocket component, // a single listener list for the whole rocket. // - + /** * Adds a ComponentChangeListener to the rocket tree. The listener is added to the root * component, which must be of type Rocket (which overrides this method). Events of all * subcomponents are sent to all listeners. - * + * * @throws IllegalStateException - if the root component is not a Rocket */ public void addComponentChangeListener(ComponentChangeListener l) { checkState(); getRocket().addComponentChangeListener(l); } - + /** * Removes a ComponentChangeListener from the rocket tree. The listener is removed from * the root component, which must be of type Rocket (which overrides this method). * Does nothing if the root component is not a Rocket. (The asymmetry is so * that listeners can always be removed just in case.) - * + * * @param l Listener to remove */ public void removeComponentChangeListener(ComponentChangeListener l) { @@ -1340,15 +1317,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab getRoot().removeComponentChangeListener(l); } } - - + + /** - * Adds a <code>ChangeListener</code> to the rocket tree. This is identical to - * <code>addComponentChangeListener()</code> except that it uses a + * Adds a <code>ChangeListener</code> to the rocket tree. This is identical to + * <code>addComponentChangeListener()</code> except that it uses a * <code>ChangeListener</code>. The same events are dispatched to the - * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass + * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass * of <code>ChangeEvent</code>. - * + * * @throws IllegalStateException - if the root component is not a <code>Rocket</code> */ @Override @@ -1356,13 +1333,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); getRocket().addChangeListener(l); } - + /** * Removes a ChangeListener from the rocket tree. This is identical to * removeComponentChangeListener() except it uses a ChangeListener. * Does nothing if the root component is not a Rocket. (The asymmetry is so * that listeners can always be removed just in case.) - * + * * @param l Listener to remove */ @Override @@ -1371,17 +1348,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab getRoot().removeChangeListener(l); } } - - + + /** - * Fires a ComponentChangeEvent on the rocket structure. The call is passed to the + * Fires a ComponentChangeEvent on the rocket structure. The call is passed to the * root component, which must be of type Rocket (which overrides this method). * Events of all subcomponents are sent to all listeners. - * - * If the component tree root is not a Rocket, the event is ignored. This is the - * case when constructing components not in any Rocket tree. In this case it + * + * If the component tree root is not a Rocket, the event is ignored. This is the + * case when constructing components not in any Rocket tree. In this case it * would be impossible for the component to have listeners in any case. - * + * * @param e Event to send */ protected void fireComponentChangeEvent(ComponentChangeEvent e) { @@ -1393,34 +1370,34 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } getRoot().fireComponentChangeEvent(e); } - - + + /** * Fires a ComponentChangeEvent of the given type. The source of the event is set to * this component. - * + * * @param type Type of event * @see #fireComponentChangeEvent(ComponentChangeEvent) */ protected void fireComponentChangeEvent(int type) { fireComponentChangeEvent(new ComponentChangeEvent(this, type)); } - - + + /** * Checks whether this component has been invalidated and should no longer be used. * This is a safety check that in-place replaced components are no longer used. * All non-trivial methods (with the exception of methods simply getting a property) * should call this method before changing or computing anything. - * + * * @throws BugException if this component has been invalidated by {@link #copyFrom(RocketComponent)}. */ protected void checkState() { invalidator.check(true); mutex.verify(); } - - + + /** * Check that the local component structure is correct. This can be called after changing * the component structure in order to verify the integrity. @@ -1443,7 +1420,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } } - + // Check whether the list contains exactly the searched-for component (with == operator) private boolean containsExact(List<RocketComponent> haystack, RocketComponent needle) { for (RocketComponent c : haystack) { @@ -1453,10 +1430,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } return false; } - - + + /////////// Iterators ////////// - + /** * Returns an iterator that iterates over all children and sub-children. * <p> @@ -1466,10 +1443,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * <p> * If an iterator iterating over only the direct children of the component is required, * use <code>component.getChildren().iterator()</code>. - * + * * TODO: HIGH: Remove this after merges have been done - * - * @param returnSelf boolean value specifying whether the component itself should be + * + * @param returnSelf boolean value specifying whether the component itself should be * returned * @return An iterator for the children and sub-children. * @deprecated Use {@link #iterator(boolean)} instead @@ -1478,15 +1455,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab public final Iterator<RocketComponent> deepIterator(boolean returnSelf) { return iterator(returnSelf); } - - + + /** * Returns an iterator that iterates over all children and sub-children, including itself. * <p> * This method is equivalent to <code>deepIterator(true)</code>. - * + * * TODO: HIGH: Remove this after merges have been done - * + * * @return An iterator for this component, its children and sub-children. * @deprecated Use {@link #iterator()} instead */ @@ -1494,8 +1471,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab public final Iterator<RocketComponent> deepIterator() { return iterator(); } - - + + /** * Returns an iterator that iterates over all children and sub-children. @@ -1506,8 +1483,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * <p> * If an iterator iterating over only the direct children of the component is required, * use <code>component.getChildren().iterator()</code>. - * - * @param returnSelf boolean value specifying whether the component itself should be + * + * @param returnSelf boolean value specifying whether the component itself should be * returned * @return An iterator for the children and sub-children. */ @@ -1515,21 +1492,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab checkState(); return new RocketComponentIterator(this, returnSelf); } - - + + /** * Returns an iterator that iterates over this components, its children and sub-children. * <p> * This method is equivalent to <code>iterator(true)</code>. - * + * * @return An iterator for this component, its children and sub-children. */ @Override public final Iterator<RocketComponent> iterator() { return iterator(true); } - - + + @@ -1548,25 +1525,25 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab RocketComponent other = (RocketComponent) obj; return this.id.equals(other.id); } - - + + @Override public int hashCode() { return id.hashCode(); } - - + + //////////// Helper methods for subclasses - + /** * Helper method to add rotationally symmetric bounds at the specified coordinates. * The X-axis value is <code>x</code> and the radius at the specified position is - * <code>r</code>. + * <code>r</code>. */ protected static final void addBound(Collection<Coordinate> bounds, double x, double r) { bounds.add(new Coordinate(x, -r, -r)); @@ -1574,36 +1551,36 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab bounds.add(new Coordinate(x, r, r)); bounds.add(new Coordinate(x, -r, r)); } - - + + protected static final Coordinate ringCG(double outerRadius, double innerRadius, double x1, double x2, double density) { return new Coordinate((x1 + x2) / 2, 0, 0, ringMass(outerRadius, innerRadius, x2 - x1, density)); } - + protected static final double ringMass(double outerRadius, double innerRadius, double length, double density) { return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) * length * density; } - + protected static final double ringLongitudinalUnitInertia(double outerRadius, double innerRadius, double length) { // 1/12 * (3 * (r1^2 + r2^2) + h^2) return (3 * (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) + MathUtil.pow2(length)) / 12; } - + protected static final double ringRotationalUnitInertia(double outerRadius, double innerRadius) { // 1/2 * (r1^2 + r2^2) return (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) / 2; } - - + + //////////// OTHER - + /** * Loads the RocketComponent fields from the given component. This method is meant @@ -1619,40 +1596,40 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * to them have been removed (for example by firing appropriate events). The list contains * all children and sub-children of the current component and the entire component * tree of <code>src</code>. - * + * * @return a list of components that should not be used after this call. */ protected List<RocketComponent> copyFrom(RocketComponent src) { checkState(); List<RocketComponent> toInvalidate = new ArrayList<RocketComponent>(); - + if (this.parent != null) { throw new UnsupportedOperationException("copyFrom called for non-root component, parent=" + this.parent.toDebugString() + ", this=" + this.toDebugString()); } - + // Add current structure to be invalidated Iterator<RocketComponent> iterator = this.iterator(false); while (iterator.hasNext()) { toInvalidate.add(iterator.next()); } - + // Remove previous components for (RocketComponent child : this.children) { child.parent = null; } this.children.clear(); - + // Copy new children to this component for (RocketComponent c : src.children) { RocketComponent copy = c.copyWithOriginalID(); this.children.add(copy); copy.parent = this; } - + this.checkComponentStructure(); src.checkComponentStructure(); - + // Set all parameters this.length = src.length; this.relativePosition = src.relativePosition; @@ -1667,40 +1644,40 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.name = src.name; this.comment = src.comment; this.id = src.id; - + // Add source components to invalidation tree for (RocketComponent c : src) { toInvalidate.add(c); } - + return toInvalidate; } - + protected void invalidate() { invalidator.invalidate(); } - - + + ////////// Iterator implementation /////////// - + /** * Private inner class to implement the Iterator. - * + * * This iterator is fail-fast if the root of the structure is a Rocket. */ private static class RocketComponentIterator implements Iterator<RocketComponent> { // Stack holds iterators which still have some components left. private final Deque<Iterator<RocketComponent>> iteratorStack = new ArrayDeque<Iterator<RocketComponent>>(); - + private final Rocket root; private final int treeModID; - + private final RocketComponent original; private boolean returnSelf = false; - + // Construct iterator with component's child's iterator, if it has elements public RocketComponentIterator(RocketComponent c, boolean returnSelf) { - + RocketComponent gp = c.getRoot(); if (gp instanceof Rocket) { root = (Rocket) gp; @@ -1709,15 +1686,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab root = null; treeModID = -1; } - + Iterator<RocketComponent> i = c.children.iterator(); if (i.hasNext()) iteratorStack.push(i); - + this.original = c; this.returnSelf = returnSelf; } - + @Override public boolean hasNext() { checkID(); @@ -1725,38 +1702,38 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return true; return !iteratorStack.isEmpty(); // Elements remain if stack is not empty } - + @Override public RocketComponent next() { Iterator<RocketComponent> i; - + checkID(); - + // Return original component first if (returnSelf) { returnSelf = false; return original; } - + // Peek first iterator from stack, throw exception if empty i = iteratorStack.peek(); if (i == null) { throw new NoSuchElementException("No further elements in RocketComponent iterator"); } - + // Retrieve next component of the iterator, remove iterator from stack if empty RocketComponent c = i.next(); if (!i.hasNext()) iteratorStack.pop(); - + // Add iterator of component children to stack if it has children i = c.children.iterator(); if (i.hasNext()) iteratorStack.push(i); - + return c; } - + private void checkID() { if (root != null) { if (root.getTreeModID() != treeModID) { @@ -1764,12 +1741,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } } } - + @Override public void remove() { throw new UnsupportedOperationException("remove() not supported by " + "RocketComponent iterator"); } } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/Stage.java b/src/net/sf/openrocket/rocketcomponent/Stage.java index b16cd4b6..5acbe854 100644 --- a/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -1,20 +1,20 @@ package net.sf.openrocket.rocketcomponent; public class Stage extends ComponentAssembly { - + @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 + * 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. @@ -25,14 +25,4 @@ public class Stage extends ComponentAssembly { 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); - } } diff --git a/src/net/sf/openrocket/rocketcomponent/Transition.java b/src/net/sf/openrocket/rocketcomponent/Transition.java index 81140a66..0daa5f7f 100644 --- a/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -15,15 +15,15 @@ import static net.sf.openrocket.util.MathUtil.pow3; public class Transition extends SymmetricComponent { private static final double CLIP_PRECISION = 0.0001; - + private Shape type; private double shapeParameter; private boolean clipped; // Not to be read - use isClipped(), which may be overriden - + private double radius1, radius2; private boolean autoRadius1, autoRadius2; // Whether the start radius is automatic - + private double foreShoulderRadius; private double foreShoulderThickness; @@ -33,30 +33,30 @@ public class Transition extends SymmetricComponent { private double aftShoulderThickness; private double aftShoulderLength; private boolean aftShoulderCapped; - + // Used to cache the clip length private double clipLength = -1; - + public Transition() { super(); - + this.radius1 = DEFAULT_RADIUS; this.radius2 = DEFAULT_RADIUS; this.length = DEFAULT_RADIUS * 3; this.autoRadius1 = true; this.autoRadius2 = true; - + this.type = Shape.CONICAL; this.shapeParameter = 0; this.clipped = true; } - - + + //////// Fore radius //////// - + @Override public double getForeRadius() { @@ -73,35 +73,35 @@ public class Transition extends SymmetricComponent { } return radius1; } - + public void setForeRadius(double radius) { if ((this.radius1 == radius) && (autoRadius1 == false)) return; - + this.autoRadius1 = false; this.radius1 = Math.max(radius, 0); - + if (this.thickness > this.radius1 && this.thickness > this.radius2) this.thickness = Math.max(this.radius1, this.radius2); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isForeRadiusAutomatic() { return autoRadius1; } - + public void setForeRadiusAutomatic(boolean auto) { if (autoRadius1 == auto) return; - + autoRadius1 = auto; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + //////// Aft radius ///////// - + @Override public double getAftRadius() { if (isAftRadiusAutomatic()) { @@ -117,62 +117,62 @@ public class Transition extends SymmetricComponent { } return radius2; } - - + + public void setAftRadius(double radius) { if ((this.radius2 == radius) && (autoRadius2 == false)) return; - + this.autoRadius2 = false; this.radius2 = Math.max(radius, 0); - + if (this.thickness > this.radius1 && this.thickness > this.radius2) this.thickness = Math.max(this.radius1, this.radius2); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + @Override public boolean isAftRadiusAutomatic() { return autoRadius2; } - + public void setAftRadiusAutomatic(boolean auto) { if (autoRadius2 == auto) return; - + autoRadius2 = auto; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + + //// Radius automatics - + @Override protected double getFrontAutoRadius() { if (isAftRadiusAutomatic()) return -1; return getAftRadius(); } - - + + @Override protected double getRearAutoRadius() { if (isForeRadiusAutomatic()) return -1; return getForeRadius(); } - - + + //////// Type & shape ///////// - + public Shape getType() { return type; } - + public void setType(Shape type) { if (type == null) { throw new IllegalArgumentException("setType called with null argument"); @@ -184,142 +184,142 @@ public class Transition extends SymmetricComponent { this.shapeParameter = type.defaultParameter(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public double getShapeParameter() { return shapeParameter; } - + public void setShapeParameter(double n) { if (shapeParameter == n) return; this.shapeParameter = MathUtil.clamp(n, type.minParameter(), type.maxParameter()); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public boolean isClipped() { if (!type.isClippable()) return false; return clipped; } - + public void setClipped(boolean c) { if (clipped == c) return; clipped = c; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public boolean isClippedEnabled() { return type.isClippable(); } - + public double getShapeParameterMin() { return type.minParameter(); } - + public double getShapeParameterMax() { return type.maxParameter(); } - - + + //////// Shoulders //////// - + public double getForeShoulderRadius() { return foreShoulderRadius; } - + public void setForeShoulderRadius(double foreShoulderRadius) { if (MathUtil.equals(this.foreShoulderRadius, foreShoulderRadius)) return; this.foreShoulderRadius = foreShoulderRadius; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getForeShoulderThickness() { return foreShoulderThickness; } - + public void setForeShoulderThickness(double foreShoulderThickness) { if (MathUtil.equals(this.foreShoulderThickness, foreShoulderThickness)) return; this.foreShoulderThickness = foreShoulderThickness; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getForeShoulderLength() { return foreShoulderLength; } - + public void setForeShoulderLength(double foreShoulderLength) { if (MathUtil.equals(this.foreShoulderLength, foreShoulderLength)) return; this.foreShoulderLength = foreShoulderLength; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public boolean isForeShoulderCapped() { return foreShoulderCapped; } - + public void setForeShoulderCapped(boolean capped) { if (this.foreShoulderCapped == capped) return; this.foreShoulderCapped = capped; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + public double getAftShoulderRadius() { return aftShoulderRadius; } - + public void setAftShoulderRadius(double aftShoulderRadius) { if (MathUtil.equals(this.aftShoulderRadius, aftShoulderRadius)) return; this.aftShoulderRadius = aftShoulderRadius; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getAftShoulderThickness() { return aftShoulderThickness; } - + public void setAftShoulderThickness(double aftShoulderThickness) { if (MathUtil.equals(this.aftShoulderThickness, aftShoulderThickness)) return; this.aftShoulderThickness = aftShoulderThickness; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public double getAftShoulderLength() { return aftShoulderLength; } - + public void setAftShoulderLength(double aftShoulderLength) { if (MathUtil.equals(this.aftShoulderLength, aftShoulderLength)) return; this.aftShoulderLength = aftShoulderLength; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - + public boolean isAftShoulderCapped() { return aftShoulderCapped; } - + public void setAftShoulderCapped(boolean capped) { if (this.aftShoulderCapped == capped) return; this.aftShoulderCapped = capped; fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE); } - - + + /////////// Shape implementations //////////// - + /** @@ -329,20 +329,20 @@ public class Transition extends SymmetricComponent { public double getRadius(double x) { if (x < 0 || x > length) return 0; - + double r1 = getForeRadius(); double r2 = getAftRadius(); - + if (r1 == r2) return r1; - + if (r1 > r2) { x = length - x; double tmp = r1; r1 = r2; r2 = tmp; } - + if (isClipped()) { // Check clip calculation if (clipLength < 0) @@ -353,7 +353,7 @@ public class Transition extends SymmetricComponent { return r1 + type.getRadius(x, r2 - r1, length, shapeParameter); } } - + /** * Numerically solve clipLength from the equation * r1 == type.getRadius(clipLength,r2,clipLength+length) @@ -361,27 +361,27 @@ public class Transition extends SymmetricComponent { */ private void calculateClip(double r1, double r2) { double min = 0, max = length; - + if (r1 >= r2) { double tmp = r1; r1 = r2; r2 = tmp; } - + if (r1 == 0) { clipLength = 0; return; } - + if (length <= 0) { clipLength = 0; return; } - + // Required: // getR(min,min+length,r2) - r1 < 0 // getR(max,max+length,r2) - r1 > 0 - + int n = 0; while (type.getRadius(max, r2, max + length, shapeParameter) - r1 < 0) { min = max; @@ -390,7 +390,7 @@ public class Transition extends SymmetricComponent { if (n > 10) break; } - + while (true) { clipLength = (min + max) / 2; if ((max - min) < CLIP_PRECISION) @@ -403,14 +403,14 @@ public class Transition extends SymmetricComponent { } } } - - + + @Override public double getInnerRadius(double x) { return Math.max(getRadius(x) - thickness, 0); } - - + + @Override public Collection<Coordinate> getComponentBounds() { @@ -421,7 +421,7 @@ public class Transition extends SymmetricComponent { addBound(bounds, getLength() + aftShoulderLength, aftShoulderRadius); return bounds; } - + @Override public double getComponentMass() { double mass = super.getComponentMass(); @@ -434,7 +434,7 @@ public class Transition extends SymmetricComponent { final double ir = Math.max(getForeShoulderRadius() - getForeShoulderThickness(), 0); mass += ringMass(ir, 0, getForeShoulderThickness(), getMaterial().getDensity()); } - + if (getAftShoulderLength() > 0.001) { final double or = getAftShoulderRadius(); final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); @@ -444,10 +444,10 @@ public class Transition extends SymmetricComponent { final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); mass += ringMass(ir, 0, getAftShoulderThickness(), getMaterial().getDensity()); } - + return mass; } - + @Override public Coordinate getComponentCG() { Coordinate cg = super.getComponentCG(); @@ -462,7 +462,7 @@ public class Transition extends SymmetricComponent { getForeShoulderThickness() - getForeShoulderLength(), getMaterial().getDensity())); } - + if (getAftShoulderLength() > 0.001) { final double ir = Math.max(getAftShoulderRadius() - getAftShoulderThickness(), 0); cg = cg.average(ringCG(getAftShoulderRadius(), ir, getLength(), @@ -476,8 +476,8 @@ public class Transition extends SymmetricComponent { } return cg; } - - + + /* * The moments of inertia are not explicitly corrected for the shoulders. * However, since the mass is corrected, the inertia is automatically corrected @@ -493,27 +493,17 @@ public class Transition extends SymmetricComponent { public String getComponentName() { return "Transition"; } - + @Override protected void componentChanged(ComponentChangeEvent e) { super.componentChanged(e); 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. */ @@ -523,16 +513,16 @@ public class Transition extends SymmetricComponent { return true; return false; } - - + + /** * An enumeration listing the possible shapes of transitions. - * + * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public static enum Shape { - + /** * Conical shape. */ @@ -547,7 +537,7 @@ public class Transition extends SymmetricComponent { return radius * x / length; } }, - + /** * Ogive shape. The shape parameter is the portion of an extended tangent ogive * that will be used. That is, for param==1 a tangent ogive will be produced, and @@ -566,12 +556,12 @@ public class Transition extends SymmetricComponent { public boolean usesParameter() { return true; // Range 0...1 is default } - + @Override public double defaultParameter() { return 1.0; // Tangent ogive by default } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -579,17 +569,17 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 1; - + // Impossible to calculate ogive for length < radius, scale instead // TODO: LOW: secant ogive could be calculated lower if (length < radius) { x = x * radius / length; length = radius; } - + if (param < 0.001) return CONICAL.getRadius(x, radius, length, param); - + // Radius of circle is: double R = sqrt((pow2(length) + pow2(radius)) * (pow2((2 - param) * length) + pow2(param * radius)) / (4 * pow2(param * radius))); @@ -599,7 +589,7 @@ public class Transition extends SymmetricComponent { return sqrt(R * R - (L - x) * (L - x)) - y0; } }, - + /** * Ellipsoidal shape. */ @@ -619,7 +609,7 @@ public class Transition extends SymmetricComponent { return sqrt(2 * radius * x - x * x); // radius/length * sphere } }, - + POWER("Power series", "A power series nose cone has a profile of " + "<i>Radius</i> × (<i>x</i> / <i>Length</i>)" + @@ -637,12 +627,12 @@ public class Transition extends SymmetricComponent { public boolean usesParameter() { // Range 0...1 return true; } - + @Override public double defaultParameter() { return 0.5; } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -658,9 +648,9 @@ public class Transition extends SymmetricComponent { } return radius * Math.pow(x / length, param); } - + }, - + PARABOLIC("Parabolic series", "A parabolic series nose cone has a profile of a parabola. The shape " + "parameter defines the segment of the parabola to utilize. The shape " + @@ -672,20 +662,20 @@ public class Transition extends SymmetricComponent { "parameter 1.0 produces a <b>full parabola</b> which is tangent to the body " + "tube at the aft end, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a " + "<b>1/2 parabola</b> and 0 produces a <b>conical</b> transition.") { - + // In principle a parabolic transition is clippable, but the difference is // negligible. - + @Override public boolean usesParameter() { // Range 0...1 return true; } - + @Override public double defaultParameter() { return 1.0; } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -693,11 +683,11 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 1; - + return radius * ((2 * x / length - param * pow2(x / length)) / (2 - param)); } }, - + HAACK("Haack series", @@ -714,12 +704,12 @@ public class Transition extends SymmetricComponent { public boolean usesParameter() { return true; } - + @Override public double maxParameter() { return 1.0 / 3.0; // Range 0...1/3 } - + @Override public double getRadius(double x, double radius, double length, double param) { assert x >= 0; @@ -727,7 +717,7 @@ public class Transition extends SymmetricComponent { assert radius >= 0; assert param >= 0; assert param <= 2; - + double theta = Math.acos(1 - 2 * x / length); if (param == 0) { return radius * sqrt((theta - sin(2 * theta) / 2) / Math.PI); @@ -735,7 +725,7 @@ public class Transition extends SymmetricComponent { return radius * sqrt((theta - sin(2 * theta) / 2 + param * pow3(sin(theta))) / Math.PI); } }, - + // POLYNOMIAL("Smooth polynomial", // "A polynomial is fitted such that the nose cone profile is horizontal "+ // "at the aft end of the transition. The angle at the tip is defined by "+ @@ -767,18 +757,18 @@ public class Transition extends SymmetricComponent { // } // } ; - + // Privete fields of the shapes private final String name; private final String transitionDesc; private final String noseconeDesc; private final boolean canClip; - + // Non-clippable constructor Shape(String name, String noseconeDesc, String transitionDesc) { this(name, noseconeDesc, transitionDesc, false); } - + // Clippable constructor Shape(String name, String noseconeDesc, String transitionDesc, boolean canClip) { this.name = name; @@ -786,29 +776,29 @@ public class Transition extends SymmetricComponent { this.noseconeDesc = noseconeDesc; this.transitionDesc = transitionDesc; } - - + + /** * Return the name of the transition shape name. */ public String getName() { return name; } - + /** * Get a description of the Transition shape. */ public String getTransitionDescription() { return transitionDesc; } - + /** * Get a description of the NoseCone shape. */ public String getNoseConeDescription() { return noseconeDesc; } - + /** * Check whether the shape differs in clipped mode. The clipping should be * enabled by default if possible. @@ -816,41 +806,41 @@ public class Transition extends SymmetricComponent { public boolean isClippable() { return canClip; } - + /** * Return whether the shape uses the shape parameter. (Default false.) */ public boolean usesParameter() { return false; } - + /** * Return the minimum value of the shape parameter. (Default 0.) */ public double minParameter() { return 0.0; } - + /** * Return the maximum value of the shape parameter. (Default 1.) */ public double maxParameter() { return 1.0; } - + /** * Return the default value of the shape parameter. (Default 0.) */ public double defaultParameter() { return 0.0; } - + /** * Calculate the basic radius of a transition with the given radius, length and * shape parameter at the point x from the tip of the component. It is assumed * that the fore radius if zero and the aft radius is <code>radius >= 0</code>. * Boattails are achieved by reversing the component. - * + * * @param x Position from the tip of the component. * @param radius Aft end radius >= 0. * @param length Length of the transition >= 0. @@ -858,8 +848,8 @@ public class Transition extends SymmetricComponent { * @return The basic radius at the given position. */ public abstract double getRadius(double x, double radius, double length, double param); - - + + /** * Returns the name of the shape (same as getName()). */ diff --git a/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java b/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java index 6f8ef943..9c3076a3 100644 --- a/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java +++ b/src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java @@ -5,7 +5,7 @@ import net.sf.openrocket.util.Coordinate; /** * A set of trapezoidal fins. The root and tip chords are perpendicular to the rocket * base line, while the leading and aft edges may be slanted. - * + * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ @@ -16,37 +16,37 @@ public class TrapezoidFinSet extends FinSet { * sweep tipChord * | |___________ * | / | - * | / | + * | / | * | / | height * / | * __________/________________|_____________ * length * == rootChord */ - + // rootChord == length private double tipChord = 0; private double height = 0; private double sweep = 0; - - + + public TrapezoidFinSet() { this (3, 0.05, 0.05, 0.025, 0.05); } - + // TODO: HIGH: height=0 -> CP = NaN - public TrapezoidFinSet(int fins, double rootChord, double tipChord, double sweep, + public TrapezoidFinSet(int fins, double rootChord, double tipChord, double sweep, double height) { super(); - + this.setFinCount(fins); this.length = rootChord; this.tipChord = tipChord; this.sweep = sweep; this.height = height; } - - + + public void setFinShape(double rootChord, double tipChord, double sweep, double height, double thickness) { if (this.length==rootChord && this.tipChord==tipChord && this.sweep==sweep && @@ -69,7 +69,7 @@ public class TrapezoidFinSet extends FinSet { length = Math.max(r,0); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + public double getTipChord() { return tipChord; } @@ -95,7 +95,7 @@ public class TrapezoidFinSet extends FinSet { sweep = r; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + /** * Get the sweep angle. This is calculated from the true sweep and height, and is not * stored separetely. @@ -135,7 +135,7 @@ public class TrapezoidFinSet extends FinSet { fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - + /** * Returns the geometry of a trapezoidal fin. @@ -143,15 +143,15 @@ public class TrapezoidFinSet extends FinSet { @Override public Coordinate[] getFinPoints() { Coordinate[] c = new Coordinate[4]; - + c[0] = Coordinate.NUL; c[1] = new Coordinate(sweep,height); c[2] = new Coordinate(sweep+tipChord,height); c[3] = new Coordinate(length,0); - + return c; } - + /** * Returns the span of a trapezoidal fin. */ @@ -159,20 +159,11 @@ public class TrapezoidFinSet extends FinSet { public double getSpan() { return height; } - + @Override public String getComponentName() { return "Trapezoidal fin set"; } - /** - * Accept a visitor to this TrapezoidFinSet in the component hierarchy. - * - * @param theVisitor the visitor that will be called back with a reference to this TrapezoidFinSet - */ - @Override - public void accept(ComponentVisitor theVisitor) { - theVisitor.visit(this); - } } -- 2.47.2