From 19c847d44a7a8c88d343ab193e63466a6427fa1e Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Tue, 8 Mar 2022 08:35:29 -0700 Subject: [PATCH 01/16] Change default stage separation from next stage ignition to current stage motor burnout. For parallel stages, this is much more likely to be the user's intent. The previous behavior caused stage separation the moment of ignition, so the booster set essentially flew as a second, independent rocket. With this change, the parallel stage stays with the center stack until the ejection charge files (note that OR treats a rocket with a -0 ejection delay as having an ejection charge at motor burnout). Note that this change to the default behavior is also applied to axial stages, but is also appropriate in that case. For the vast majority of low power rockets, the booster burnout ignites the next stage, so "next stage ignition" and "current stage ejection charge" are simultaneous. For high power rockets, the next stage ignition has to be customized anyway. --- .../rocketcomponent/StageSeparationConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java index 31ee0c893..d7ef82824 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/StageSeparationConfiguration.java @@ -98,7 +98,7 @@ public class StageSeparationConfiguration implements FlightConfigurableParameter private static final Translator trans = Application.getTranslator(); - private SeparationEvent separationEvent = SeparationEvent.UPPER_IGNITION; + private SeparationEvent separationEvent = SeparationEvent.EJECTION; private double separationDelay = 0; private final List configListeners = new LinkedList<>(); From 5ac829ea78f920e6bf332c3b1fc1d5b34c7a4090 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 9 Mar 2022 11:01:18 -0700 Subject: [PATCH 02/16] Check for active upper stage before dropping booster If there are multiple independent motor clusters in a stage, each of them can attempt a separation event. When the second event happens, there is no longer an active upper stage, so the separation results in a simulation branch with no active stages. This causes a NaN exception as the mass is 0. --- .../BasicEventSimulationEngine.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index e128cae5f..69fdf3d3d 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -425,24 +425,28 @@ public class BasicEventSimulationEngine implements SimulationEngine { } case STAGE_SEPARATION: { - // Record the event. - currentStatus.getFlightData().addEvent(event); - RocketComponent boosterStage = event.getSource(); final int stageNumber = boosterStage.getStageNumber(); - - // Mark the status as having dropped the booster - currentStatus.getConfiguration().clearStage( stageNumber); - - // Prepare the simulation branch - SimulationStatus boosterStatus = new SimulationStatus(currentStatus); - boosterStatus.setFlightData(new FlightDataBranch(boosterStage.getName(), FlightDataType.TYPE_TIME)); - // Mark the booster status as only having the booster. - boosterStatus.getConfiguration().setOnlyStage(stageNumber); - toSimulate.push(boosterStatus); - log.info(String.format("==>> @ %g; from Branch: %s ---- Branching: %s ---- \n", - currentStatus.getSimulationTime(), - currentStatus.getFlightData().getBranchName(), boosterStatus.getFlightData().getBranchName())); + + if (currentStatus.getConfiguration().isStageActive(stageNumber-1)) { + // Record the event. + currentStatus.getFlightData().addEvent(event); + + // Mark the status as having dropped the booster + currentStatus.getConfiguration().clearStage( stageNumber); + + // Prepare the simulation branch + SimulationStatus boosterStatus = new SimulationStatus(currentStatus); + boosterStatus.setFlightData(new FlightDataBranch(boosterStage.getName(), FlightDataType.TYPE_TIME)); + // Mark the booster status as only having the booster. + boosterStatus.getConfiguration().setOnlyStage(stageNumber); + toSimulate.push(boosterStatus); + log.info(String.format("==>> @ %g; from Branch: %s ---- Branching: %s ---- \n", + currentStatus.getSimulationTime(), + currentStatus.getFlightData().getBranchName(), boosterStatus.getFlightData().getBranchName())); + } else { + log.debug("upper stage is not active; not performing separation"); + } break; } From d290525099838082e345c139945802f0c0d0b57f Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 11 Mar 2022 11:24:02 -0700 Subject: [PATCH 03/16] Process entire part tree when finding active contexts instead of bailing out early when an inactive component is found. --- .../rocketcomponent/FlightConfiguration.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 70f645ab1..99992072c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -298,9 +298,7 @@ public class FlightConfiguration implements FlightConfigurableParameter Date: Fri, 11 Mar 2022 21:49:53 -0700 Subject: [PATCH 04/16] Remove 'active' field from InstanceContext The original plan was that there would be an InstanceContext for every instance of every RocketComponent, with some active and some not (just as RocketComponents may be active or not). The implementation has resulted in InstanceContexts only being created for active RocketComponents, so the active field is superfluous. --- .../rocketcomponent/FlightConfiguration.java | 23 ------------------- .../rocketcomponent/InstanceContext.java | 4 +--- .../rocketcomponent/InstanceMap.java | 2 +- .../gui/figure3d/RocketRenderer.java | 2 +- .../gui/scalefigure/RocketFigure.java | 6 +---- 5 files changed, 4 insertions(+), 33 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 99992072c..5d3ba0e07 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -604,35 +604,12 @@ public class FlightConfiguration implements FlightConfigurableParameter instanceCoordinates = component.getComponentBounds(); for (InstanceContext context : contexts) { - /* - * If the instance is not active in the current context, then - * skip the bound calculations. This is mildly confusing since - * getActiveInstances() implies that it will only return the - * instances that are active, but it returns all instances and - * the context indicates if it is active or not. - */ - if (!context.active) { - continue; - } - Collection transformedCoords = new ArrayList<>(instanceCoordinates); // mutating. Transforms coordinates in place. context.transform.transform(instanceCoordinates); diff --git a/core/src/net/sf/openrocket/rocketcomponent/InstanceContext.java b/core/src/net/sf/openrocket/rocketcomponent/InstanceContext.java index 2e7da00f9..e781182c8 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InstanceContext.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InstanceContext.java @@ -26,9 +26,8 @@ public class InstanceContext { return component.hashCode(); } - public InstanceContext(final RocketComponent _component, final boolean _active, final int _instanceNumber, final Transformation _transform) { + public InstanceContext(final RocketComponent _component, final int _instanceNumber, final Transformation _transform) { component = _component; - active = _active; instanceNumber = _instanceNumber; transform = _transform; @@ -48,7 +47,6 @@ public class InstanceContext { // ==== public ==== final public RocketComponent component; - final public boolean active; final public int instanceNumber; final public Transformation transform; diff --git a/core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java b/core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java index 36cc79e8a..1538e69aa 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java @@ -34,7 +34,7 @@ public class InstanceMap extends HashMap()); } - final InstanceContext context = new InstanceContext(component, active, number, xform); + final InstanceContext context = new InstanceContext(component, number, xform); get(key).add(context); } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index c6aef258e..f4b6662ae 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -186,7 +186,7 @@ public abstract class RocketRenderer { for(InstanceContext context: contextList ) { Geometry instanceGeometry = cr.getComponentGeometry( comp, context.transform ); - instanceGeometry.active = context.active; + instanceGeometry.active = true; treeGeometry.add( instanceGeometry ); } } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index ab3330a0b..5275d8ad0 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -378,11 +378,7 @@ public class RocketFigure extends AbstractScaleFigure { for(InstanceContext context: contextList ) { final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform); - - // generate shape for this component, if active - if( context.active ) { - allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform); - } + allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform); } } } From 584353463bde42f4236e085a85da41bee14ec718 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Mon, 14 Mar 2022 12:35:59 -0600 Subject: [PATCH 05/16] Update FlightConfiguration active instances whenever active components change, not every time get getActiveInstances() is called --- .../rocketcomponent/FlightConfiguration.java | 24 +++++++--- .../net/sf/openrocket/util/TestRockets.java | 45 ++++++++++--------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 5d3ba0e07..8e65f29c7 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -64,6 +64,7 @@ public class FlightConfiguration implements FlightConfigurableParameter stages = new HashMap(); final protected HashMap motors = new HashMap(); final private Collection activeMotors = new ArrayList(); + final private InstanceMap activeInstances = new InstanceMap(); private int boundsModID = -1; private BoundingBox cachedBounds = new BoundingBox(); @@ -101,6 +102,7 @@ public class FlightConfiguration implements FlightConfigurableParameter> entry: imap.entrySet() ) { - RocketComponent c = entry.getKey(); + RocketComponent c = null; + for(Map.Entry> entry: imap.entrySet() ) { + c = entry.getKey(); if (c instanceof TrapezoidFinSet) { - final TrapezoidFinSet fins = (TrapezoidFinSet) c; - final BodyTube body = (BodyTube) fins.getParent(); - body.removeChild(fins); - - // create a PodSet to hook the fins to - PodSet podset = new PodSet(); - podset.setInstanceCount(fins.getFinCount()); - - body.addChild(podset); - - // put a phantom body tube on the pods - BodyTube podBody = new BodyTube(fins.getRootChord(), 0); - podBody.setName("Pod Body"); - podset.addChild(podBody); - - // change the number of fins to 1 and put the revised - // finset on the podbody - fins.setFinCount(1); - podBody.addChild(fins); + break; } } + final TrapezoidFinSet fins = (TrapezoidFinSet) c; + final BodyTube body = (BodyTube) fins.getParent(); + body.removeChild(fins); + + // create a PodSet to hook the fins to + PodSet podset = new PodSet(); + podset.setInstanceCount(fins.getFinCount()); + + body.addChild(podset); + + // put a phantom body tube on the pods + BodyTube podBody = new BodyTube(fins.getRootChord(), 0); + podBody.setName("Pod Body"); + podset.addChild(podBody); + + // change the number of fins to 1 and put the revised + // finset on the podbody + fins.setFinCount(1); + podBody.addChild(fins); return rocket; } From 13fe3e104698572fc6f9d7906850dcdc9cfa4264 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Mon, 14 Mar 2022 12:47:21 -0600 Subject: [PATCH 06/16] Geometry active flag was also never false; eliminated it --- .../gui/figure3d/RocketRenderer.java | 45 ++++++++----------- .../gui/figure3d/geometry/Geometry.java | 2 - 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index f4b6662ae..d144ef2c5 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -85,21 +85,19 @@ public abstract class RocketRenderer { if (ignore != null && ignore.contains(comp)) continue; - if( geom.active ) { - final int hashCode = comp.hashCode(); - - selectionMap.put(hashCode, comp); - - gl.glColor4ub((byte) ((hashCode >> 24) & 0xFF), // red channel (LSB) - (byte) ((hashCode >> 16) & 0xFF), // green channel - (byte) ((hashCode >> 8) & 0xFF), // blue channel - (byte) ((hashCode) & 0xFF)); // alpha channel (MSB) - - if (isDrawnTransparent(comp)) { - geom.render(gl, Surface.INSIDE); - } else { - geom.render(gl, Surface.ALL); - } + final int hashCode = comp.hashCode(); + + selectionMap.put(hashCode, comp); + + gl.glColor4ub((byte) ((hashCode >> 24) & 0xFF), // red channel (LSB) + (byte) ((hashCode >> 16) & 0xFF), // green channel + (byte) ((hashCode >> 8) & 0xFF), // blue channel + (byte) ((hashCode) & 0xFF)); // alpha channel (MSB) + + if (isDrawnTransparent(comp)) { + geom.render(gl, Surface.INSIDE); + } else { + geom.render(gl, Surface.ALL); } } @@ -186,7 +184,6 @@ public abstract class RocketRenderer { for(InstanceContext context: contextList ) { Geometry instanceGeometry = cr.getComponentGeometry( comp, context.transform ); - instanceGeometry.active = true; treeGeometry.add( instanceGeometry ); } } @@ -196,19 +193,15 @@ public abstract class RocketRenderer { private void renderTree( GL2 gl, final Collection geometryList){ //cycle through opaque components first, then transparent to preserve proper depth testing for(Geometry geom: geometryList ) { - if( geom.active ) { - //if not transparent - if( !isDrawnTransparent( (RocketComponent)geom.obj) ){ - renderComponent(gl, geom, 1.0f); - } + //if not transparent + if( !isDrawnTransparent( (RocketComponent)geom.obj) ){ + renderComponent(gl, geom, 1.0f); } } for(Geometry geom: geometryList ) { - if( geom.active ) { - if( isDrawnTransparent( (RocketComponent)geom.obj) ){ - // Draw T&T front faces blended, without depth test - renderComponent(gl, geom, 0.2f); - } + if( isDrawnTransparent( (RocketComponent)geom.obj) ){ + // Draw T&T front faces blended, without depth test + renderComponent(gl, geom, 0.2f); } } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java index 4c4b9df62..41554e575 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java @@ -25,8 +25,6 @@ public abstract class Geometry { public final Object obj; public final Transformation transform; - public boolean active; - public abstract void render(GL2 gl, Surface which ); private Geometry() { From 6a826d54b96e1b34fc128e0d3641229aa9cce515 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 16 Mar 2022 08:10:01 -0600 Subject: [PATCH 07/16] Get correct fin span when last point in shape is loewr than first point. Note that the lowest point on the fin is guaranteed to be either the first point which is at (0, 0) or the last point, which will have a y coordinate of 0 if on a body tube, greater than 0 if on an increasing transition, or less than 0 on a decreasing transition (boattail). --- core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index 284f70550..b0fe74cea 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -296,7 +296,8 @@ public class FreeformFinSet extends FinSet { if (c.y > max) max = c.y; } - return max; + + return max - Math.min(points.get(points.size() - 1).y, 0); } @Override From 04351e8d5e97e72e198be3821f5f8671de917f21 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 17 Mar 2022 03:33:32 +0100 Subject: [PATCH 08/16] [#1246] Use TransitionShapeModel for Transition type ComboBox This fixes the issue of the type not updating when a preset changes. --- .../gui/adaptors/TransitionShapeModel.java | 64 +++++++++++++++++++ .../gui/configdialog/NoseConeConfig.java | 12 ++-- .../gui/configdialog/TransitionConfig.java | 12 ++-- 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java diff --git a/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java b/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java new file mode 100644 index 000000000..51b02201e --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java @@ -0,0 +1,64 @@ +package net.sf.openrocket.gui.adaptors; + +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.ComponentChangeListener; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Transition; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; + +public class TransitionShapeModel extends AbstractListModel + implements ComboBoxModel, ComponentChangeListener { + private final RocketComponent component; + private final Transition.Shape[] typeList = Transition.Shape.values(); + private Transition.Shape previousType; + + public TransitionShapeModel(RocketComponent component) { + this.component = component; + if (component instanceof Transition) { + previousType = ((Transition) component).getType(); + setSelectedItem(previousType); + component.addComponentChangeListener(this); + } + } + + @Override + public void setSelectedItem(Object item) { + if (!(component instanceof Transition) || !(item instanceof Transition.Shape)) { + return; + } + + ((Transition) component).setType((Transition.Shape) item); + } + + @Override + public Object getSelectedItem() { + if (component instanceof Transition) { + return ((Transition) component).getType(); + } + return null; + } + + @Override + public int getSize() { + return typeList.length; + } + + @Override + public Transition.Shape getElementAt(int index) { + return typeList[index]; + } + + @Override + public void componentChanged(ComponentChangeEvent e) { + if (!(component instanceof Transition)) { + return; + } + + if (previousType != ((Transition) component).getType()) { + previousType = ((Transition) component).getType(); + fireContentsChanged(this, 0, 0); + } + } +} diff --git a/swing/src/net/sf/openrocket/gui/configdialog/NoseConeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/NoseConeConfig.java index 6be83867e..d7c2975cb 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/NoseConeConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/NoseConeConfig.java @@ -16,6 +16,7 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.TransitionShapeModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.UnitSelector; @@ -52,18 +53,15 @@ public class NoseConeConfig extends RocketComponentConfig { {//// Nose cone shape: panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Noseconeshape"))); - Transition.Shape selected = ((NoseCone) component).getType(); - Transition.Shape[] typeList = Transition.Shape.values(); - - final JComboBox typeBox = new JComboBox(typeList); + final JComboBox typeBox = new JComboBox<>(new TransitionShapeModel(c)); typeBox.setEditable(false); - typeBox.setSelectedItem(selected); typeBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem(); - ((NoseCone) component).setType(s); - description.setText(PREDESC + s.getNoseConeDescription()); + if (s != null) { + description.setText(PREDESC + s.getNoseConeDescription()); + } updateEnabled(); } }); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/TransitionConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/TransitionConfig.java index d41945b5c..35a44bcdf 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/TransitionConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/TransitionConfig.java @@ -15,6 +15,7 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.TransitionShapeModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.UnitSelector; @@ -54,18 +55,15 @@ public class TransitionConfig extends RocketComponentConfig { //// Transition shape: panel.add(new JLabel(trans.get("TransitionCfg.lbl.Transitionshape"))); - Transition.Shape selected = ((Transition) component).getType(); - Transition.Shape[] typeList = Transition.Shape.values(); - - typeBox = new JComboBox(typeList); + typeBox = new JComboBox<>(new TransitionShapeModel(c)); typeBox.setEditable(false); - typeBox.setSelectedItem(selected); typeBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem(); - ((Transition) component).setType(s); - description.setText(PREDESC + s.getTransitionDescription()); + if (s != null) { + description.setText(PREDESC + s.getTransitionDescription()); + } updateEnabled(); } }); From d8460941511264fdcaf4a767e3922a7aa0820076 Mon Sep 17 00:00:00 2001 From: Remington Holder Date: Sat, 19 Mar 2022 22:15:12 -0400 Subject: [PATCH 09/16] Adds Export PNG Button To Sim Chart Page --- .../gui/plot/SimulationPlotDialog.java | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index d2d322a8b..cc6ed951d 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -6,26 +6,32 @@ import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.io.File; import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; +import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.gui.widgets.SelectColorButton; import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartUtilities; +import org.jfree.chart.JFreeChart; /** * Dialog that shows a plot of a simulation results based on user options. @@ -50,6 +56,7 @@ public class SimulationPlotDialog extends JDialog { this.add(panel); final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart()); + final JFreeChart jChart = myPlot.getJFreeChart(); panel.add(chartPanel, "grow, wrap 20lp"); //// Description text @@ -108,6 +115,16 @@ public class SimulationPlotDialog extends JDialog { } }); panel.add(button, "gapleft rel"); + + //// Print chart button + button = new SelectColorButton(Icons.FILE_PRINT); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doPngExport(jChart); + } + }); + panel.add(button, "gapleft rel"); //// Add series selection box ArrayList stages = new ArrayList(); @@ -141,16 +158,42 @@ public class SimulationPlotDialog extends JDialog { } }); panel.add(button, "right"); - this.setLocationByPlatform(true); this.pack(); GUIUtil.setDisposableDialogOptions(this, button); GUIUtil.rememberWindowSize(this); } - - - + + private boolean doPngExport(JFreeChart chart){ + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(FileHelper.getImageFileFilter()); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + + /* Ensures No Problems When Choosing File */ + if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) + return false; + + File file = chooser.getSelectedFile(); + if (file == null) + return false; + + file = FileHelper.forceExtension(file, "png"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } + + /* Uses JFreeChart Built In PNG Export Method */ + try{ + ChartUtilities.saveChartAsPNG(file, chart, 1000, 500); + } catch(Exception e){ + return false; + } + + return true; + } + + /** * Static method that shows a plot with the specified parameters. * From c73a41071a262542dbb5c44a79893f50e472870a Mon Sep 17 00:00:00 2001 From: Remington Holder Date: Sun, 20 Mar 2022 20:38:20 -0400 Subject: [PATCH 10/16] Various Small Updates To PNG Chart Export - Creates PNG file filter - Adds new file filter to translation file - Makes Exported Chart's Width & Height as it is displayed - Changes Button From Print Icon to "Save As Image" text --- core/resources/l10n/messages.properties | 1 + .../sf/openrocket/gui/plot/SimulationPlotDialog.java | 11 ++++++----- swing/src/net/sf/openrocket/gui/util/FileHelper.java | 4 ++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 9e4a2782b..49a7e3769 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -96,6 +96,7 @@ FileHelper.ALL_DESIGNS_FILTER = All rocket designs (*.ork; *.rkt) FileHelper.OPENROCKET_DESIGN_FILTER = OpenRocket designs (*.ork) FileHelper.ROCKSIM_DESIGN_FILTER = RockSim designs (*.rkt) FileHelper.OPEN_ROCKET_COMPONENT_FILTER = OpenRocket presets (*.orc) +FileHelper.PNG_FILTER = PNG image (*.png) FileHelper.IMAGES = Image files diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index cc6ed951d..a3dd2a462 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -117,11 +117,11 @@ public class SimulationPlotDialog extends JDialog { panel.add(button, "gapleft rel"); //// Print chart button - button = new SelectColorButton(Icons.FILE_PRINT); + button = new SelectColorButton("Save As Image"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - doPngExport(jChart); + doPngExport(chartPanel,jChart); } }); panel.add(button, "gapleft rel"); @@ -165,9 +165,10 @@ public class SimulationPlotDialog extends JDialog { GUIUtil.rememberWindowSize(this); } - private boolean doPngExport(JFreeChart chart){ + private boolean doPngExport(ChartPanel chartPanel, JFreeChart chart){ JFileChooser chooser = new JFileChooser(); - chooser.setFileFilter(FileHelper.getImageFileFilter()); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileFilter(FileHelper.PNG_FILTER); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); /* Ensures No Problems When Choosing File */ @@ -185,7 +186,7 @@ public class SimulationPlotDialog extends JDialog { /* Uses JFreeChart Built In PNG Export Method */ try{ - ChartUtilities.saveChartAsPNG(file, chart, 1000, 500); + ChartUtilities.saveChartAsPNG(file, chart, chartPanel.getWidth(), chartPanel.getHeight()); } catch(Exception e){ return false; } diff --git a/swing/src/net/sf/openrocket/gui/util/FileHelper.java b/swing/src/net/sf/openrocket/gui/util/FileHelper.java index c88b99a58..96d8234f4 100644 --- a/swing/src/net/sf/openrocket/gui/util/FileHelper.java +++ b/swing/src/net/sf/openrocket/gui/util/FileHelper.java @@ -56,6 +56,10 @@ public final class FileHelper { public static final FileFilter CSV_FILTER = new SimpleFileFilter(trans.get("FileHelper.CSV_FILTER"), ".csv"); + /** File filter for PNG files (*.png) */ + public static final FileFilter PNG_FILTER = + new SimpleFileFilter(trans.get("FileHelper.PNG_FILTER"), ".png"); + From 670455fb2d8ee3c833dd84b05cc7e30efb54867a Mon Sep 17 00:00:00 2001 From: Remington Holder Date: Sun, 20 Mar 2022 21:39:26 -0400 Subject: [PATCH 11/16] Formats Comments & Makes "Save As Image" Translatable --- core/resources/l10n/messages.properties | 2 +- .../net/sf/openrocket/gui/plot/SimulationPlotDialog.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 49a7e3769..4f194e109 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1274,7 +1274,7 @@ TCMotorSelPan.btn.close = Close ! PlotDialog PlotDialog.CheckBox.Showdatapoints = Show data points PlotDialog.lbl.Chart = left click drag to zoom area. mouse wheel to zoom. ctrl-mouse wheel to zoom x axis only. ctrl-left click drag to pan. right click drag to zoom dynamically. - +PlotDialog.btn.saveAsImage = Save As Image ComponentTree.ttip.massoverride = mass override ComponentTree.ttip.cgoverride = cg override ! "main" prefix is used for the main application dialog diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index a3dd2a462..1848c753a 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -117,7 +117,7 @@ public class SimulationPlotDialog extends JDialog { panel.add(button, "gapleft rel"); //// Print chart button - button = new SelectColorButton("Save As Image"); + button = new SelectColorButton(trans.get("PlotDialog.btn.saveAsImage")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -171,7 +171,7 @@ public class SimulationPlotDialog extends JDialog { chooser.setFileFilter(FileHelper.PNG_FILTER); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); - /* Ensures No Problems When Choosing File */ + //// Ensures No Problems When Choosing File if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return false; @@ -184,7 +184,7 @@ public class SimulationPlotDialog extends JDialog { return false; } - /* Uses JFreeChart Built In PNG Export Method */ + //// Uses JFreeChart Built In PNG Export Method try{ ChartUtilities.saveChartAsPNG(file, chart, chartPanel.getWidth(), chartPanel.getHeight()); } catch(Exception e){ From 0a45df51670b5ec07a11d7316402c8f7136e7904 Mon Sep 17 00:00:00 2001 From: Remington Holder Date: Sun, 20 Mar 2022 21:49:39 -0400 Subject: [PATCH 12/16] (Minor) Adds Newline For Code Readability --- core/resources/l10n/messages.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 4f194e109..a1019a12f 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1275,6 +1275,7 @@ TCMotorSelPan.btn.close = Close PlotDialog.CheckBox.Showdatapoints = Show data points PlotDialog.lbl.Chart = left click drag to zoom area. mouse wheel to zoom. ctrl-mouse wheel to zoom x axis only. ctrl-left click drag to pan. right click drag to zoom dynamically. PlotDialog.btn.saveAsImage = Save As Image + ComponentTree.ttip.massoverride = mass override ComponentTree.ttip.cgoverride = cg override ! "main" prefix is used for the main application dialog From 2d5d1b3eca87b40dc66f41e65f5ea49f9b0e01b0 Mon Sep 17 00:00:00 2001 From: Remington Holder Date: Sun, 20 Mar 2022 22:08:03 -0400 Subject: [PATCH 13/16] Changes "Save As PNG" to "Export Image" - Change improves application uniformity --- core/resources/l10n/messages.properties | 2 +- swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index a1019a12f..117978079 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1274,7 +1274,7 @@ TCMotorSelPan.btn.close = Close ! PlotDialog PlotDialog.CheckBox.Showdatapoints = Show data points PlotDialog.lbl.Chart = left click drag to zoom area. mouse wheel to zoom. ctrl-mouse wheel to zoom x axis only. ctrl-left click drag to pan. right click drag to zoom dynamically. -PlotDialog.btn.saveAsImage = Save As Image +PlotDialog.btn.exportImage = Export Image ComponentTree.ttip.massoverride = mass override ComponentTree.ttip.cgoverride = cg override diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index 1848c753a..22165524c 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -117,7 +117,7 @@ public class SimulationPlotDialog extends JDialog { panel.add(button, "gapleft rel"); //// Print chart button - button = new SelectColorButton(trans.get("PlotDialog.btn.saveAsImage")); + button = new SelectColorButton(trans.get("PlotDialog.btn.exportImage")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { From a5ab27e0550847623d63513ff8da5fed7f8493ec Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 22 Mar 2022 04:05:14 +0100 Subject: [PATCH 14/16] [#1254] Fix simulation plot element stretching The elements stretched because the panel dimensions were greater than the chartPanel maximum draw dimensions. --- .../net/sf/openrocket/gui/plot/SimulationPlotDialog.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index 22165524c..ffcaec72f 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -3,6 +3,8 @@ package net.sf.openrocket.gui.plot; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; @@ -58,6 +60,12 @@ public class SimulationPlotDialog extends JDialog { final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart()); final JFreeChart jChart = myPlot.getJFreeChart(); panel.add(chartPanel, "grow, wrap 20lp"); + + // Ensures normal aspect-ratio of chart elements when resizing the panel + chartPanel.setMinimumDrawWidth(0); + chartPanel.setMaximumDrawWidth(Integer.MAX_VALUE); + chartPanel.setMinimumDrawHeight(0); + chartPanel.setMaximumDrawHeight(Integer.MAX_VALUE); //// Description text JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); From 524a94b7225db8f14e32ee78ac2bf6cec6f7de57 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 22 Mar 2022 12:52:00 +0100 Subject: [PATCH 15/16] Update parts database --- swing/resources-src/datafiles/components | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swing/resources-src/datafiles/components b/swing/resources-src/datafiles/components index 52e1e2c08..8304c0fd3 160000 --- a/swing/resources-src/datafiles/components +++ b/swing/resources-src/datafiles/components @@ -1 +1 @@ -Subproject commit 52e1e2c0800ebf62fd0e9cecd69aaaed6cddf80e +Subproject commit 8304c0fd3dc80d65c40af4da83268ec1b32931d6 From f595b4f1c61b77cce5784b5a6c13fdba34d50693 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 24 Mar 2022 12:45:05 +0100 Subject: [PATCH 16/16] Update motors --- .../datafiles/thrustcurves/thrustcurves.ser | Bin 3132338 -> 3137320 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/resources/datafiles/thrustcurves/thrustcurves.ser b/core/resources/datafiles/thrustcurves/thrustcurves.ser index 6509ed02d0f9c06d7c107477f30c0b4bfb5c521d..7be885d29af1b36a8e896692f0fb36cdbcad03eb 100644 GIT binary patch delta 25193 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