+2011-11-24 Sampo Niskanen
+
+ * Released version 1.1.9
+
2011-11-18 Doug Pedrick
- * Printable Fin Marking Guides, Transitions, and Nose Cones (simple projection only)
+ * Printable Fin Marking Guides, Transitions, and Nose Cones
+
+2011-10-20 Sampo Niskanen
+
+ * [BUG] NPE if plot data type is not present
2011-10-11 Sampo Niskanen
OpenRocket - an Open Source model rocket simulator
--------------------------------------------------
-Copyright (C) 2007-2010 Sampo Niskanen
+Copyright (C) 2007-2011 Sampo Niskanen
For license information see the file LICENSE.TXT.
--------------------------------
Sampo Niskanen, main developer
-Doug Pedrick, support for reading RockSim designs
+Doug Pedrick, support for reading RockSim designs, printing
+Richard Graham, geodetic computations
+Boris du Reau, internationalization
+Tripoli France, Tripoli Spain, Stefan Lobas / ERIG, translations
+OpenRocket 1.1.9 (2011-11-24):
+-------------------------------
+
+This release calculates rocket flight in real-world coordinates and
+takes into account geodetic effects (including coriolis effect) thanks
+to work by Richard Graham. Printing of transitions, nose cone
+profiles and fin marking guides is available thanks to Doug Pedrick.
+It also contains some usability features and bug fixes.
+
+
OpenRocket 1.1.8 (2011-08-25):
-------------------------------
# The OpenRocket build version
-build.version=1.1.9pre
+build.version=1.1.9
# The source of the package. When building a package for a specific
"<font size=\"+1\"><b>OpenRocket has been developed by:</b></font><br><br>" +
"Sampo Niskanen (main developer)<br>" +
"Doug Pedrick (RockSim file format, printing)<br>" +
- "Boris du Reau (internationalization, translation lead)<br><br>" +
+ "Boris du Reau (internationalization, translation lead)<br>" +
+ "Richard Graham (geodetic computations)<br><br>" +
"<b>Translations by:</b><br><br>" +
"Tripoli France (French)<br>" +
"Stefan Lobas / ERIG e.V. (German)<br>" +
private static final Translator trans = Application.getTranslator();
- // FIXME: NPE if FlightDataType has disappeared
-
public SimulationEditDialog(Window parent, Simulation s) {
this(parent, s, 0);
}
//// Simulation time:
panel.add(new JLabel(trans.get("SimuRunDlg.lbl.Simutime") + " "), "gapright para");
timeLabel = new JLabel("");
- panel.add(timeLabel, "growx, wrap rel");
+ panel.add(timeLabel, "growx, wmin 200lp, wrap rel");
//// Altitude:
panel.add(new JLabel(trans.get("SimuRunDlg.lbl.Altitude") + " "));
}
}
- // FIXME: Bugs when expected branch is not present
-
configurationSelector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
JScrollPane scroll = new JScrollPane(typeSelectorPanel);
- this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
+ this.add(scroll, "spany 2, height 10px, wmin 400lp, grow 100, gapright para");
//// Flight events
private JComboBox axisSelector;
- public PlotTypeSelector(int index, FlightDataType type) {
- this(index, type, null, -1);
- }
-
public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
super(new MigLayout("ins 0"));
import java.awt.Graphics2D;
import java.io.IOException;
import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN);
Rocket rocket = rocketDocument.getRocket();
- final Configuration configuration = rocket.getDefaultConfiguration();
+ final Configuration configuration = rocket.getDefaultConfiguration().clone();
configuration.setAllStages();
PdfContentByte canvas = writer.getDirectContent();
String[] motorIds = rocket.getMotorConfigurationIDs();
- List<Double> stageMasses = getStageMasses(rocket);
-
for (int j = 0; j < motorIds.length; j++) {
String motorId = motorIds[j];
if (motorId != null) {
}
}
- /**
- * 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<Motor> getMotorList(final Rocket theRocket, final String theMid) {
- Iterator<RocketComponent> components = theRocket.iterator();
- final List<Motor> motorList = new ArrayList<Motor>();
- 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;
- }
/**
* Paint a diagram of the rocket into the PDF document.
return target.replace("]", "");
}
-
- /**
- * Return a list of cumulative stage masses. The latter masses include the mass
- * of the upper stages as well.
- *
- * @param rocket the rocket
- * @return a list containing the cumulative stage masses
- */
- private List<Double> getStageMasses(final Rocket rocket) {
- List<Double> masses = new ArrayList<Double>();
- int stages = rocket.getStageCount();
-
- Configuration config = new Configuration(rocket);
- MassCalculator calc = new BasicMassCalculator();
-
- for (int i = 0; i < stages; i++) {
- config.setToStage(i);
- masses.add(calc.getCG(config, MassCalcType.NO_MOTORS).weight);
- }
-
- return masses;
- }
-
}
* to fit in the width of the chosen page size.
*/
public class PrintFigure extends RocketFigure {
-
- /**
- * Constructor.
- *
- * @param configuration the configuration
- */
- public PrintFigure (final Configuration configuration) {
- super(configuration);
- }
-
- protected double computeTy (int heightPx) {
- super.computeTy(heightPx);
- return 0;
- }
-
- public void setScale (final double theScale) {
- this.scale = theScale; //dpi/0.0254*scaling;
- updateFigure();
- }
+
+ /**
+ * Constructor.
+ *
+ * @param configuration the configuration
+ */
+ public PrintFigure(final Configuration configuration) {
+ super(configuration);
+ }
+
+ @Override
+ protected double computeTy(int heightPx) {
+ super.computeTy(heightPx);
+ return 0;
+ }
+
+ public void setScale(final double theScale) {
+ this.scale = theScale; //dpi/0.0254*scaling;
+ updateFigure();
+ }
}
*/
package net.sf.openrocket.gui.print;
-import java.util.*;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
/**
* Instances of this class are meant to keep track of what the user has selected to be printed.
*/
public class PrintableContext implements Comparable<PrintableContext>, Iterable<PrintableContext> {
-
- /**
- * The stage number. May be null for printables that have no stage meaning.
- */
- private Set<Integer> stageNumber;
-
- /**
- * The type of thing to be printed.
- */
- 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<OpenRocketPrintable, Set<Integer>> previous = new TreeMap<OpenRocketPrintable, Set<Integer>>();
-
- /**
- * Constructor.
- */
- public PrintableContext () {
- }
-
- /**
- * Constructor.
- *
- * @param theStageNumber the stage number of the printable; may be null if not applicable
- * @param thePrintable the type of the thing to be printed
- *
- * @throws IllegalArgumentException thrown if thePrintable.isStageSpecific
- */
- private PrintableContext (final Set<Integer> theStageNumber, final OpenRocketPrintable thePrintable)
- throws IllegalArgumentException {
- if (thePrintable.isStageSpecific() && theStageNumber == null) {
- throw new IllegalArgumentException("A stage number must be provided when a printable is stage specific.");
- }
- stageNumber = theStageNumber;
- printable = thePrintable;
- }
-
- /**
- * 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<Integer> stages = previous.get(thePrintable);
- if (stages == null) {
- stages = new TreeSet<Integer>();
- previous.put(thePrintable, stages);
- }
- if (theStageNumber != null) {
- stages.add(theStageNumber);
- }
- }
-
- /** PrintableContext iterator. */
- public Iterator<PrintableContext> iterator () {
- return new Iterator<PrintableContext>() {
-
- Iterator<OpenRocketPrintable> keyIter = previous.keySet().iterator();
-
- @Override
- public boolean hasNext () {
- return keyIter.hasNext();
- }
-
- @Override
- public PrintableContext next () {
- final OpenRocketPrintable key = keyIter.next();
- return new PrintableContext(previous.get(key), key);
- }
-
- @Override
- public void remove () {
- }
- };
-
- }
-
- /**
- * Get the stage number, if it's applicable to the printable.
- *
- * @return the stage number
- */
- public Set<Integer> getStageNumber () {
- return stageNumber;
- }
-
- /**
- * Get the printable.
- *
- * @return the printable
- */
- public OpenRocketPrintable getPrintable () {
- return printable;
- }
-
- @Override
- public int compareTo (final PrintableContext other) {
- return this.printable.getPrintOrder() - other.printable.getPrintOrder();
- }
-
+
+ /**
+ * The stage number. May be null for printables that have no stage meaning.
+ */
+ private Set<Integer> stageNumber;
+
+ /**
+ * The type of thing to be printed.
+ */
+ 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<OpenRocketPrintable, Set<Integer>> previous = new TreeMap<OpenRocketPrintable, Set<Integer>>();
+
+ /**
+ * Constructor.
+ */
+ public PrintableContext() {
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param theStageNumber the stage number of the printable; may be null if not applicable
+ * @param thePrintable the type of the thing to be printed
+ *
+ * @throws IllegalArgumentException thrown if thePrintable.isStageSpecific
+ */
+ private PrintableContext(final Set<Integer> theStageNumber, final OpenRocketPrintable thePrintable)
+ throws IllegalArgumentException {
+ if (thePrintable.isStageSpecific() && theStageNumber == null) {
+ throw new IllegalArgumentException("A stage number must be provided when a printable is stage specific.");
+ }
+ stageNumber = theStageNumber;
+ printable = thePrintable;
+ }
+
+ /**
+ * 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<Integer> stages = previous.get(thePrintable);
+ if (stages == null) {
+ stages = new TreeSet<Integer>();
+ previous.put(thePrintable, stages);
+ }
+ if (theStageNumber != null) {
+ stages.add(theStageNumber);
+ }
+ }
+
+ /** PrintableContext iterator. */
+ @Override
+ public Iterator<PrintableContext> iterator() {
+ return new Iterator<PrintableContext>() {
+
+ Iterator<OpenRocketPrintable> keyIter = previous.keySet().iterator();
+
+ @Override
+ public boolean hasNext() {
+ return keyIter.hasNext();
+ }
+
+ @Override
+ public PrintableContext next() {
+ final OpenRocketPrintable key = keyIter.next();
+ return new PrintableContext(previous.get(key), key);
+ }
+
+ @Override
+ public void remove() {
+ }
+ };
+
+ }
+
+ /**
+ * Get the stage number, if it's applicable to the printable.
+ *
+ * @return the stage number
+ */
+ public Set<Integer> getStageNumber() {
+ return stageNumber;
+ }
+
+ /**
+ * Get the printable.
+ *
+ * @return the printable
+ */
+ public OpenRocketPrintable getPrintable() {
+ return printable;
+ }
+
+ @Override
+ public int compareTo(final PrintableContext other) {
+ return this.printable.getPrintOrder() - other.printable.getPrintOrder();
+ }
+
}
package net.sf.openrocket.gui.print;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
import net.sf.openrocket.gui.rocketfigure.TransitionShapes;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.util.Transformation;
-import java.awt.*;
-
public class PrintableNoseCone extends AbstractPrintableTransition {
-
- /**
- * If the component to be drawn is a nose cone, save a reference to it.
- */
- private NoseCone target;
-
- /**
- * Construct a printable nose cone.
- *
- * @param noseCone the component to print
- */
- public PrintableNoseCone(Transition noseCone) {
- super(false, noseCone);
- }
-
- @Override
- protected void init(Transition component) {
-
- target = (NoseCone) component;
- double radius = target.getForeRadius();
- if (radius < target.getAftRadius()) {
- radius = target.getAftRadius();
- }
- setSize((int) PrintUnit.METERS.toPoints(2 * radius) + marginX,
- (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + marginY);
- }
-
- /**
- * Draw a nose cone.
- *
- * @param g2 the graphics context
- */
- protected void draw(Graphics2D g2) {
- Shape[] shapes = TransitionShapes.getShapesSide(target, Transformation.rotate_x(0d), PrintUnit.METERS.toPoints(1));
-
- if (shapes != null && shapes.length > 0) {
- Rectangle r = shapes[0].getBounds();
- g2.translate(marginX + r.getHeight() / 2, marginY);
- g2.rotate(Math.PI / 2);
- for (Shape shape : shapes) {
- g2.draw(shape);
- }
- g2.rotate(-Math.PI / 2);
- }
- }
+
+ /**
+ * If the component to be drawn is a nose cone, save a reference to it.
+ */
+ private NoseCone target;
+
+ /**
+ * Construct a printable nose cone.
+ *
+ * @param noseCone the component to print
+ */
+ public PrintableNoseCone(Transition noseCone) {
+ super(false, noseCone);
+ }
+
+ @Override
+ protected void init(Transition component) {
+
+ target = (NoseCone) component;
+ double radius = target.getForeRadius();
+ if (radius < target.getAftRadius()) {
+ radius = target.getAftRadius();
+ }
+ setSize((int) PrintUnit.METERS.toPoints(2 * radius) + marginX,
+ (int) PrintUnit.METERS.toPoints(target.getLength() + target.getAftShoulderLength()) + marginY);
+ }
+
+ /**
+ * Draw a nose cone.
+ *
+ * @param g2 the graphics context
+ */
+ @Override
+ protected void draw(Graphics2D g2) {
+ Shape[] shapes = TransitionShapes.getShapesSide(target, Transformation.rotate_x(0d), PrintUnit.METERS.toPoints(1));
+
+ if (shapes != null && shapes.length > 0) {
+ Rectangle r = shapes[0].getBounds();
+ g2.translate(marginX + r.getHeight() / 2, marginY);
+ g2.rotate(Math.PI / 2);
+ for (Shape shape : shapes) {
+ g2.draw(shape);
+ }
+ g2.rotate(-Math.PI / 2);
+ }
+ }
}
package net.sf.openrocket.gui.print;
-import net.sf.openrocket.rocketcomponent.Transition;
-
-import java.awt.*;
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
+import net.sf.openrocket.rocketcomponent.Transition;
+
/**
* This class allows for a Transition to be printable. It does so by decorating an existing transition (which will not be
* modified) and rendering it within a JPanel. The JPanel is not actually visualized on a display, but instead renders
* may be to draw a myriahedral projection that can be cut out and bent to form the shape.
*/
public class PrintableTransition extends AbstractPrintableTransition {
-
- /**
- * Dashed array value.
- */
- private final static float dash1[] = {4.0f};
- /**
- * The dashed stroke for glue tab.
- */
- private final static BasicStroke dashed = new BasicStroke(1.0f,
- BasicStroke.CAP_BUTT,
- BasicStroke.JOIN_MITER,
- 10.0f, dash1, 0.0f);
-
- /**
- * The layout is an outer arc, an inner arc, and two lines one either endpoints that connect the arcs.
- * Most of the math involves transposing geometric cartesian coordinates to the Java AWT coordinate system.
- */
- private Path2D gp;
-
- /**
- * The glue tab.
- */
- private Path2D glueTab1;
-
- /**
- * The alignment marks.
- */
- private Line2D tick1, tick2;
-
- /**
- * The x coordinates for the two ticks drawn at theta degrees.
- */
- private int tick3X, tick4X;
-
- /**
- * The angle, in degrees.
- */
- private float theta;
-
- /**
- * The x,y coordinates for where the virtual circle center is located.
- */
- private int circleCenterX, circleCenterY;
-
- /**
- * Constructor.
- *
- * @param transition the transition to print
- */
- public PrintableTransition(Transition transition) {
- super(false, transition);
- }
-
- @Override
- protected void init(Transition component) {
-
- double r1 = component.getAftRadius();
- double r2 = component.getForeRadius();
-
- //Regardless of orientation, we have the convention of R1 as the smaller radius. Flip if different.
- if (r1 > r2) {
- r1 = r2;
- r2 = component.getAftRadius();
- }
- double len = component.getLength();
- double v = r2 - r1;
- double tmp = Math.sqrt(v * v + len * len);
- double factor = tmp / v;
-
- theta = (float) (360d * v / tmp);
-
- int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
- int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
-
- int x = marginX;
- int tabOffset = 35;
- int y = tabOffset + marginY;
-
- Arc2D.Double outerArc = new Arc2D.Double();
- Arc2D.Double innerArc = new Arc2D.Double();
-
- //If the arcs are more than 3/4 of a circle, then assume the height (y) is the same as the radius of the bigger arc.
- if (theta >= 270) {
- y += r2InPoints;
- }
- //If the arc is between 1/2 and 3/4 of a circle, then compute the actual height based upon the angle and radius
- //of the bigger arc.
- else if (theta >= 180) {
- double thetaRads = Math.toRadians(theta - 180);
- y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
- }
-
- circleCenterY = y;
- circleCenterX = r2InPoints + x;
-
- //Create the larger arc.
- outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
-
- //Create the smaller arc.
- innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
-
- //Create the line between the start of the larger arc and the start of the smaller arc.
- Path2D.Double line = new Path2D.Double();
- line.setWindingRule(Path2D.WIND_NON_ZERO);
- line.moveTo(x, y);
- final int width = r2InPoints - r1InPoints;
- line.lineTo(width + x, y);
-
- //Create the line between the endpoint of the larger arc and the endpoint of the smaller arc.
- Path2D.Double closingLine = new Path2D.Double();
- closingLine.setWindingRule(Path2D.WIND_NON_ZERO);
- Point2D innerArcEndPoint = innerArc.getEndPoint();
- closingLine.moveTo(innerArcEndPoint.getX(), innerArcEndPoint.getY());
- Point2D outerArcEndPoint = outerArc.getEndPoint();
- closingLine.lineTo(outerArcEndPoint.getX(), outerArcEndPoint.getY());
-
- //Add all shapes to the polygon path.
- gp = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
- gp.append(line, false);
- gp.append(outerArc, false);
- gp.append(closingLine, false);
- gp.append(innerArc, false);
-
- //Create the glue tab.
- glueTab1 = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
- glueTab1.moveTo(x, y);
- glueTab1.lineTo(x + tabOffset, y - tabOffset);
- glueTab1.lineTo(width + x - tabOffset, y - tabOffset);
- glueTab1.lineTo(width + x, y);
-
- //Create tick marks for alignment, 1/4 of the width in from either edge
- int fromEdge = width / 4;
- final int tickLength = 8;
- //Upper left
- tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
- //Upper right
- tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
-
- tick3X = r2InPoints - fromEdge;
- tick4X = r1InPoints + fromEdge;
-
- setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
- }
-
- /**
- * Draw alignment marks on an angle.
- *
- * @param g2 the graphics context
- * @param x the center of the circle's x coordinate
- * @param y the center of the circle's y
- * @param line the line to draw
- * @param theta the angle
- */
- private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
- g2.translate(x, y);
- g2.rotate(Math.toRadians(-theta));
- g2.draw(line);
- g2.rotate(Math.toRadians(theta));
- g2.translate(-x, -y);
- }
-
- /**
- * Draw a transition.
- *
- * @param g2 the graphics context
- */
- protected void draw(Graphics2D g2) {
- //Render it.
- g2.draw(gp);
- g2.draw(tick1);
- g2.draw(tick2);
- drawAlignmentMarks(g2, circleCenterX,
- circleCenterY,
- new Line2D.Float(-tick3X, 0, -tick3X, -8),
- theta);
- drawAlignmentMarks(g2, circleCenterX,
- circleCenterY,
- new Line2D.Float(-tick4X, 0, -tick4X, -8),
- theta);
-
- g2.setStroke(dashed);
- g2.draw(glueTab1);
- }
-
+
+ /**
+ * Dashed array value.
+ */
+ private final static float dash1[] = { 4.0f };
+ /**
+ * The dashed stroke for glue tab.
+ */
+ private final static BasicStroke dashed = new BasicStroke(1.0f,
+ BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_MITER,
+ 10.0f, dash1, 0.0f);
+
+ /**
+ * The layout is an outer arc, an inner arc, and two lines one either endpoints that connect the arcs.
+ * Most of the math involves transposing geometric cartesian coordinates to the Java AWT coordinate system.
+ */
+ private Path2D gp;
+
+ /**
+ * The glue tab.
+ */
+ private Path2D glueTab1;
+
+ /**
+ * The alignment marks.
+ */
+ private Line2D tick1, tick2;
+
+ /**
+ * The x coordinates for the two ticks drawn at theta degrees.
+ */
+ private int tick3X, tick4X;
+
+ /**
+ * The angle, in degrees.
+ */
+ private float theta;
+
+ /**
+ * The x,y coordinates for where the virtual circle center is located.
+ */
+ private int circleCenterX, circleCenterY;
+
+ /**
+ * Constructor.
+ *
+ * @param transition the transition to print
+ */
+ public PrintableTransition(Transition transition) {
+ super(false, transition);
+ }
+
+ @Override
+ protected void init(Transition component) {
+
+ double r1 = component.getAftRadius();
+ double r2 = component.getForeRadius();
+
+ //Regardless of orientation, we have the convention of R1 as the smaller radius. Flip if different.
+ if (r1 > r2) {
+ r1 = r2;
+ r2 = component.getAftRadius();
+ }
+ double len = component.getLength();
+ double v = r2 - r1;
+ double tmp = Math.sqrt(v * v + len * len);
+ double factor = tmp / v;
+
+ theta = (float) (360d * v / tmp);
+
+ int r1InPoints = (int) PrintUnit.METERS.toPoints(r1 * factor);
+ int r2InPoints = (int) PrintUnit.METERS.toPoints(r2 * factor);
+
+ int x = marginX;
+ int tabOffset = 35;
+ int y = tabOffset + marginY;
+
+ Arc2D.Double outerArc = new Arc2D.Double();
+ Arc2D.Double innerArc = new Arc2D.Double();
+
+ //If the arcs are more than 3/4 of a circle, then assume the height (y) is the same as the radius of the bigger arc.
+ if (theta >= 270) {
+ y += r2InPoints;
+ }
+ //If the arc is between 1/2 and 3/4 of a circle, then compute the actual height based upon the angle and radius
+ //of the bigger arc.
+ else if (theta >= 180) {
+ double thetaRads = Math.toRadians(theta - 180);
+ y += (int) ((Math.cos(thetaRads) * r2InPoints) * Math.tan(thetaRads));
+ }
+
+ circleCenterY = y;
+ circleCenterX = r2InPoints + x;
+
+ //Create the larger arc.
+ outerArc.setArcByCenter(circleCenterX, circleCenterY, r2InPoints, 180, theta, Arc2D.OPEN);
+
+ //Create the smaller arc.
+ innerArc.setArcByCenter(circleCenterX, circleCenterY, r1InPoints, 180, theta, Arc2D.OPEN);
+
+ //Create the line between the start of the larger arc and the start of the smaller arc.
+ Path2D.Double line = new Path2D.Double();
+ line.setWindingRule(Path2D.WIND_NON_ZERO);
+ line.moveTo(x, y);
+ final int width = r2InPoints - r1InPoints;
+ line.lineTo(width + x, y);
+
+ //Create the line between the endpoint of the larger arc and the endpoint of the smaller arc.
+ Path2D.Double closingLine = new Path2D.Double();
+ closingLine.setWindingRule(Path2D.WIND_NON_ZERO);
+ Point2D innerArcEndPoint = innerArc.getEndPoint();
+ closingLine.moveTo(innerArcEndPoint.getX(), innerArcEndPoint.getY());
+ Point2D outerArcEndPoint = outerArc.getEndPoint();
+ closingLine.lineTo(outerArcEndPoint.getX(), outerArcEndPoint.getY());
+
+ //Add all shapes to the polygon path.
+ gp = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
+ gp.append(line, false);
+ gp.append(outerArc, false);
+ gp.append(closingLine, false);
+ gp.append(innerArc, false);
+
+ //Create the glue tab.
+ glueTab1 = new Path2D.Float(GeneralPath.WIND_EVEN_ODD, 4);
+ glueTab1.moveTo(x, y);
+ glueTab1.lineTo(x + tabOffset, y - tabOffset);
+ glueTab1.lineTo(width + x - tabOffset, y - tabOffset);
+ glueTab1.lineTo(width + x, y);
+
+ //Create tick marks for alignment, 1/4 of the width in from either edge
+ int fromEdge = width / 4;
+ final int tickLength = 8;
+ //Upper left
+ tick1 = new Line2D.Float(x + fromEdge, y, x + fromEdge, y + tickLength);
+ //Upper right
+ tick2 = new Line2D.Float(x + width - fromEdge, y, x + width - fromEdge, y + tickLength);
+
+ tick3X = r2InPoints - fromEdge;
+ tick4X = r1InPoints + fromEdge;
+
+ setSize(gp.getBounds().width, gp.getBounds().height + tabOffset);
+ }
+
+ /**
+ * Draw alignment marks on an angle.
+ *
+ * @param g2 the graphics context
+ * @param x the center of the circle's x coordinate
+ * @param y the center of the circle's y
+ * @param line the line to draw
+ * @param theta the angle
+ */
+ private void drawAlignmentMarks(Graphics2D g2, int x, int y, Line2D.Float line, float theta) {
+ g2.translate(x, y);
+ g2.rotate(Math.toRadians(-theta));
+ g2.draw(line);
+ g2.rotate(Math.toRadians(theta));
+ g2.translate(-x, -y);
+ }
+
+ /**
+ * Draw a transition.
+ *
+ * @param g2 the graphics context
+ */
+ @Override
+ protected void draw(Graphics2D g2) {
+ //Render it.
+ g2.draw(gp);
+ g2.draw(tick1);
+ g2.draw(tick2);
+ drawAlignmentMarks(g2, circleCenterX,
+ circleCenterY,
+ new Line2D.Float(-tick3X, 0, -tick3X, -8),
+ theta);
+ drawAlignmentMarks(g2, circleCenterX,
+ circleCenterY,
+ new Line2D.Float(-tick4X, 0, -tick4X, -8),
+ theta);
+
+ g2.setStroke(dashed);
+ g2.draw(glueTab1);
+ }
+
}
private SimulationWorker backgroundSimulationWorker = null;
- private boolean dirty = false;
private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
package net.sf.openrocket.models.gravity;
-//import net.sf.openrocket.util.Monitorable;
import net.sf.openrocket.util.Monitorable;
import net.sf.openrocket.util.WorldCoordinate;
/**
- * An interface to modelling gravitational acceleration.
+ * An interface for modeling gravitational acceleration.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public interface GravityModel extends Monitorable {
/**
- * Compute the gravity at a given world coordinate
+ * Compute the gravitational acceleration at a given world coordinate
+ *
* @param wc the world coordinate location
* @return gravitational acceleration in m/s/s
*/
import net.sf.openrocket.util.WorldCoordinate;
/**
- * A gravity model based on the WGS84 elipsoid.
+ * A gravity model based on the WGS84 ellipsoid.
*
* @author Richard Graham <richard@rdg.cc>
*/
// Apply correction due to altitude. Note this assumes a spherical earth, but it is a small correction
// so it probably doesn't really matter. Also does not take into account gravity of the atmosphere, again
// correction could be done but not really necessary.
- double g_alt = g_0 * Math.pow(WorldCoordinate.REARTH / (WorldCoordinate.REARTH + wc.getAltitude()), 2);
+ double g_alt = g_0 * MathUtil.pow2(WorldCoordinate.REARTH / (WorldCoordinate.REARTH + wc.getAltitude()));
return g_alt;
}
}
// Compute conditions
- //double altitude = status.getRocketPosition().z + status.getSimulationConditions().getLaunchAltitude();
- //gravity = status.getSimulationConditions().getGravityModel().getGravity(altitude);
gravity = status.getSimulationConditions().getGravityModel().getGravity(status.getRocketWorldPosition());
// Call post-listener
double newAlt = location.getAltitude() + delta.z;
// bearing (in radians, clockwise from north);
- // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
+ // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth's radius
double d = MathUtil.hypot(delta.x, delta.y);
// Check for zero movement before computing bearing
double newAlt = location.getAltitude() + delta.z;
// bearing (in radians, clockwise from north);
- // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
+ // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth's radius
double d = MathUtil.hypot(delta.x, delta.y);
// Check for zero movement before computing bearing
DASHDOT("LineStyle.Dash-dotted", new float[] { 8f, 3f, 2f, 3f });
private static final Translator trans = Application.getTranslator();
- static {
- System.out.println("*** LineStyle initialized trans:" + trans + " ***");
- System.err.println("*** LineStyle initialized ***");
- }
private final String name;
private final float[] dashes;
--- /dev/null
+package net.sf.openrocket.models.gravity;
+
+import static org.junit.Assert.assertEquals;
+import net.sf.openrocket.util.WorldCoordinate;
+
+import org.junit.Test;
+
+
+public class WGSGravityModelTest {
+
+ private WGSGravityModel model = new WGSGravityModel();
+
+ @Test
+ public void testSurfaceGravity() {
+ // Equator
+ test(0, 0, 0, 9.780);
+ // Mid-latitude
+ test(45, 0, 0, 9.806);
+ // Mid-latitude
+ test(45, 99, 0, 9.806);
+ // South pole
+ test(-90, 0, 0, 9.832);
+ }
+
+ @Test
+ public void testAltitudeEffect() {
+ test(45, 0, -100, 9.806);
+ test(45, 0, 0, 9.806);
+ test(45, 0, 10, 9.806);
+ test(45, 0, 100, 9.806);
+ test(45, 0, 1000, 9.803);
+ test(45, 0, 10000, 9.775);
+ test(45, 0, 100000, 9.505);
+ }
+
+ private void test(double lat, double lon, double alt, double g) {
+ WorldCoordinate wc = new WorldCoordinate(lat, lon, alt);
+ assertEquals(g, model.getGravity(wc), 0.001);
+ assertEquals(g, model.getGravity(wc), 0.001);
+ }
+
+}
// Test zero movement
+ System.out.println("\nTesting zero movement");
testAddCoordinate(50.0, 20.0, 0, 123, 50.0, 20.0, false);
// Long distance NE over England, crosses Greenwich meridian
// 50 03N 005 42W to 58 38N 003 04E is 1109km at 027 16'07"
+ System.out.println("\nTesting 1109km NE over England");
testAddCoordinate(50 + 3 * min, -5 - 42 * min, 1109000, 27 + 16 * min + 7 * sec, 58 + 38 * min, 3 + 4 * min, false);
// SW over Brazil
// -10N -60E to -11N -61E is 155.9km at 224 25'34"
+ System.out.println("\nTesting 155km SW over Brazil");
testAddCoordinate(-10, -60, 155900, 224 + 25 * min + 34 * sec, -11, -61, true);
// NW over the 180 meridian
// 63N -179E to 63 01N 179E is 100.9km at 271 56'34"
+ System.out.println("\nTesting 100km NW over 180 meridian");
testAddCoordinate(63, -179, 100900, 271 + 56 * min + 34 * sec, 63 + 1 * min, 179, true);
// NE near the north pole
// 89 50N 0E to 89 45N 175E is 46.29 km at 003 00'01"
+ System.out.println("\nTesting 46km NE near north pole");
testAddCoordinate(89 + 50 * min, 0, 46290, 3 + 0 * min + 1 * sec, 89 + 45 * min, 175, false);
// S directly over south pole
// -89 50N 12E to -89 45N 192E is 46.33km at 180 00'00"
+ System.out.println("\nTesting 46km directly over south pole ");
testAddCoordinate(-89 - 50 * min, 12, 46330, 180, -89 - 45 * min, -168, false);
}
// Test WGS84
/*
- * TODO: Since the example values are computed using a spherical earth approximation,
- * the WGS84 method will have significantly larger errors. The tolerance should be
- * increased correspondingly.
+ * Note: Since the example values are computed using a spherical earth approximation,
+ * the WGS84 method will have significantly larger errors. A tolerance of 1% accommodates
+ * all cases except the NE flight near the north pole, where the ellipsoidal effect is
+ * the greatest.
*/
- //tolerance = ...
+ tolerance = 0.04 * distance / 111325;
System.out.println("\nWGS84 tolerance: " + tolerance);
- result = GeodeticComputationStrategy.WGS84.addCoordinate(result, coord);
+ result = GeodeticComputationStrategy.WGS84.addCoordinate(wc, coord);
System.out.println("Difference Lat: " + Math.abs(finalLatitude - result.getLatitudeDeg()));
System.out.println("Difference Lon: " + Math.abs(finalLongitude - result.getLongitudeDeg()));
- // FIXME: Re-enable these when they function
- // assertEquals(finalLatitude, result.getLatitudeDeg(), tolerance);
- // assertEquals(finalLongitude, result.getLongitudeDeg(), tolerance);
- // assertEquals(1000.0, result.getAltitude(), 0.0);
+ assertEquals(finalLatitude, result.getLatitudeDeg(), tolerance);
+ assertEquals(finalLongitude, result.getLongitudeDeg(), tolerance);
+ assertEquals(1000.0, result.getAltitude(), 0.0);
// Test FLAT
$version = $_GET["version"];
$updates = "";
-$unstable = "1.1.8";
+$unstable = "1.1.9";
$stable = "1.0.0";
-if (preg_match("/^1\.1\.7/", $version)) {
+if (preg_match("/^1\.1\.8/", $version)) {
$updates = "Version: " . $unstable . "\n" .
+ "6: Additional template printing\n" .
+ "5: Geodetic computations\n" .
+ "4: Bug fixes\n" .
+ "";
+} else if (preg_match("/^1\.1\.7/", $version)) {
+ $updates = "Version: " . $unstable . "\n" .
+ "6: Additional template printing\n" .
+ "5: Geodetic computations\n" .
"4: Bug fixes\n" .
"";
} else if (preg_match("/^1\.1\.6/", $version)) {
$updates = "Version: " . $unstable . "\n" .
"8: Automatic rocket design optimization\n" .
+ "6: Additional template printing\n" .
+ "5: Geodetic computations\n" .
"";
} else if (preg_match("/^1\.1\.5/", $version)) {
$updates = "Version: " . $unstable . "\n" .
"8: Automatic rocket design optimization\n" .
"6: Initial localization support\n" .
+ "6: Additional template printing\n" .
+ "5: Geodetic computations\n" .
"5: Scaling support\n" .
"4: Bug fixes\n" .
"";
<div class="content">
<div class="news">
<h2>Recent news:</h2>
+ <p><span class="date">24.11.2011:</span> Version 1.1.9 is
+ <a href="download.html">released</a>!</p>
+ <p>For this version Richard Graham has implemented geodetic
+ computation methods, which take into account the curvature of the
+ Earth and the coriolis effect. The computation method is selected
+ by the <em>Geodetic calculations</em> option in the simulation
+ options. It's not <em>(yet)</em> a full spherical computation model, but
+ should be accurate enough for almost all sub-orbital needs.</p>
+ <p>Doug Pedrick has also enhanced the printing system with the
+ ability to print fin positioning guides, transition templates and
+ nose cone profiles. Other smaller enhancements and bug fixes are
+ also included.</p>
<p><span class="date">25.8.2011:</span> Version 1.1.8 is
<a href="download.html">released</a>!</p>
<p>This release contains bug fixes to the optimization methods.
easy to optimize against particulars of the simulation methods,
instead of true physical phenomena. Always keep common sense at
hand and take the results with a grain of salt.</p>
- <p><span class="date">22.7.2011:</span> Version 1.1.6 is
- <a href="download.html">released</a>!</p>
- <p>This release includes initial localization support and
- translations to French, German and Spanish. This is thanks to the
- great work of Boris du Reau, and the teams from Tripoli France,
- Tripoli Spain and ERIG e.V. If you prefer to use some other
- language than the system default, you can select the language on
- the "Options" tab of the preferences dialog.</p>
- <p>The release also includes design scaling support and numerous bug
- fixes.</p>
</div>
<div class="contentholder">
<h2>Ready packages</h2>
<a href="http://sourceforge.net/donate/index.php?group_id=260357"><img src="project-support.jpg" width="88" height="32" alt="Support This Project" /></a>
</div>
<div class="downloadbox">
- <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.8/OpenRocket-1.1.8.jar/download">
+ <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.9/OpenRocket-1.1.9.jar/download">
<strong>Download now!</strong>
- <span>OpenRocket-1.1.8.jar</span>
+ <span>OpenRocket-1.1.9.jar</span>
</a>
<span class="alternative">
- <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.8/ReleaseNotes/view">Release notes</a> |
- <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.8/OpenRocket-1.1.8-src.zip/download">Source code</a>
+ <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.9/ReleaseNotes/view">Release notes</a> |
+ <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.9/OpenRocket-1.1.9-src.zip/download">Source code</a>
</span>
</div>
<h3>Stable release</h3>
Windows) by double-clicking the package icon. No installation is
required.</p>
<p>From the command line OpenRocket can be started by
- <span class="command">java -jar OpenRocket-1.1.8.jar</span></p>
+ <span class="command">java -jar OpenRocket-1.1.9.jar</span></p>
</div>
<div class="clear"></div>
<h2>Introduction</h2>
<div class="rightpane">
<div class="downloadbox">
- <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.8/OpenRocket-1.1.8.jar/download">
+ <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.9/OpenRocket-1.1.9.jar/download">
<strong>Download now!</strong>
- <span>OpenRocket-1.1.8.jar</span>
+ <span>OpenRocket-1.1.9.jar</span>
</a>
<span class="alternative">
- <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.8/ReleaseNotes/view">Release notes</a> |
+ <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.9/ReleaseNotes/view">Release notes</a> |
<a href="download.html">Other versions</a>
</span>
</div>
<a href="http://sourceforge.net/donate/index.php?group_id=260357"><img src="project-support.jpg" width="88" height="32" alt="Support This Project" /> </a>
</div>
</div>
- <p><strong>OpenRocket</strong> is an free, fully featured model
+ <p><strong>OpenRocket</strong> is a free, fully featured model
rocket simulator that allows you to design and simulate your
rockets before actually building and flying them.</p>
<p>The main features include:</p>
<div class="clear"></div>
<div class="news">
<h2>News</h2>
+ <p><span class="date">24.11.2011:</span> Version 1.1.9 is
+ <a href="download.html">released</a>!</p>
+ <p>For this version Richard Graham has implemented geodetic
+ computation methods, which take into account the curvature of the
+ Earth and the coriolis effect. The computation method is selected
+ by the <em>Geodetic calculations</em> option in the simulation
+ options. It's not <em>(yet)</em> a full spherical computation model, but
+ should be accurate enough for almost all sub-orbital needs.</p>
+ <p>Doug Pedrick has also enhanced the printing system with the
+ ability to print fin positioning guides, transition templates and
+ nose cone profiles. Other smaller enhancements and bug fixes are
+ also included.</p>
<p><span class="date">25.8.2011:</span> Version 1.1.8 is
<a href="download.html">released</a>!</p>
<p>This release contains bug fixes to the optimization methods.
<set stableversion="1.0.0">
-<set developmentversion="1.1.8">
+<set developmentversion="1.1.9">
<set version="${developmentversion}">
<def name="downloadbox">
<!--- Remember to move the position of "onlyrecent" below! --->
+ <p><span class="date">24.11.2011:</span> Version 1.1.9 is
+ <a href="download.html">released</a>!</p>
+
+ <p>For this version Richard Graham has implemented geodetic
+ computation methods, which take into account the curvature of the
+ Earth and the coriolis effect. The computation method is selected
+ by the <em>Geodetic calculations</em> option in the simulation
+ options. It's not <em>(yet)</em> a full spherical computation model, but
+ should be accurate enough for almost all sub-orbital needs.</p>
+
+ <p>Doug Pedrick has also enhanced the printing system with the
+ ability to print fin positioning guides, transition templates and
+ nose cone profiles. Other smaller enhancements and bug fixes are
+ also included.</p>
+
<p><span class="date">25.8.2011:</span> Version 1.1.8 is
<a href="download.html">released</a>!</p>
instead of true physical phenomena. Always keep common sense at
hand and take the results with a grain of salt.</p>
+
+ <if not onlyrecent><!--- Older items not shown on download page: --->
+
+
<p><span class="date">22.7.2011:</span> Version 1.1.6 is
<a href="download.html">released</a>!</p>
<p>The release also includes design scaling support and numerous bug
fixes.</p>
-
- <if not onlyrecent><!--- Older items not shown on download page: --->
-
-
<p><span class="date">10.6.2011:</span> Version 1.1.5 is
<a href="download.html">released</a>!</p>