Merge branch 'release-notes-b2' of https://github.com/neilweinstock/openrocket into release-notes-b2

This commit is contained in:
Neil Weinstock 2022-03-24 20:19:20 -04:00
commit 4ef5a02322
18 changed files with 230 additions and 130 deletions

View File

@ -96,6 +96,7 @@ FileHelper.ALL_DESIGNS_FILTER = All rocket designs (*.ork; *.rkt)
FileHelper.OPENROCKET_DESIGN_FILTER = OpenRocket designs (*.ork) FileHelper.OPENROCKET_DESIGN_FILTER = OpenRocket designs (*.ork)
FileHelper.ROCKSIM_DESIGN_FILTER = RockSim designs (*.rkt) FileHelper.ROCKSIM_DESIGN_FILTER = RockSim designs (*.rkt)
FileHelper.OPEN_ROCKET_COMPONENT_FILTER = OpenRocket presets (*.orc) FileHelper.OPEN_ROCKET_COMPONENT_FILTER = OpenRocket presets (*.orc)
FileHelper.PNG_FILTER = PNG image (*.png)
FileHelper.IMAGES = Image files FileHelper.IMAGES = Image files
@ -1273,6 +1274,7 @@ TCMotorSelPan.btn.close = Close
! PlotDialog ! PlotDialog
PlotDialog.CheckBox.Showdatapoints = Show data points 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.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.exportImage = Export Image
ComponentTree.ttip.massoverride = mass override ComponentTree.ttip.massoverride = mass override
ComponentTree.ttip.cgoverride = cg override ComponentTree.ttip.cgoverride = cg override

View File

@ -64,6 +64,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
final protected HashMap<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>(); final protected HashMap<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>();
final protected HashMap<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>(); final protected HashMap<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>();
final private Collection<MotorConfiguration> activeMotors = new ArrayList<MotorConfiguration>(); final private Collection<MotorConfiguration> activeMotors = new ArrayList<MotorConfiguration>();
final private InstanceMap activeInstances = new InstanceMap();
private int boundsModID = -1; private int boundsModID = -1;
private BoundingBox cachedBounds = new BoundingBox(); private BoundingBox cachedBounds = new BoundingBox();
@ -101,6 +102,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
updateStages(); updateStages();
updateMotors(); updateMotors();
updateActiveInstances();
} }
public Rocket getRocket() { public Rocket getRocket() {
@ -121,12 +123,14 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
cur.active = _active; cur.active = _active;
} }
updateMotors(); updateMotors();
updateActiveInstances();
} }
public void copyStages(FlightConfiguration other) { public void copyStages(FlightConfiguration other) {
for (StageFlags cur : other.stages.values()) for (StageFlags cur : other.stages.values())
stages.put(cur.stageNumber, new StageFlags(cur.stageNumber, cur.active)); stages.put(cur.stageNumber, new StageFlags(cur.stageNumber, cur.active));
updateMotors(); updateMotors();
updateActiveInstances();
} }
/** /**
@ -137,6 +141,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
public void clearStage(final int stageNumber) { public void clearStage(final int stageNumber) {
_setStageActive( stageNumber, false ); _setStageActive( stageNumber, false );
updateMotors(); updateMotors();
updateActiveInstances();
} }
/** /**
@ -163,6 +168,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
_setStageActive(i, true); _setStageActive(i, true);
} }
updateMotors(); updateMotors();
updateActiveInstances();
} }
/** /**
@ -174,6 +180,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
_setAllStages(false); _setAllStages(false);
_setStageActive(stageNumber, true); _setStageActive(stageNumber, true);
updateMotors(); updateMotors();
updateActiveInstances();
} }
/** /**
@ -196,9 +203,11 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if ((0 <= stageNumber) && (stages.containsKey(stageNumber))) { if ((0 <= stageNumber) && (stages.containsKey(stageNumber))) {
StageFlags flags = stages.get(stageNumber); StageFlags flags = stages.get(stageNumber);
flags.active = !flags.active; flags.active = !flags.active;
updateMotors();
updateActiveInstances();
return; return;
} }
updateMotors();
log.error("error: attempt to retrieve via a bad stage number: " + stageNumber); log.error("error: attempt to retrieve via a bad stage number: " + stageNumber);
} }
@ -286,21 +295,22 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return toReturn; return toReturn;
} }
public InstanceMap getActiveInstances() {
return activeInstances;
}
/* /*
* Generates a read-only, instance-aware collection of the components for this rocket & configuration * Generates a read-only, instance-aware collection of the components for this rocket & configuration
* *
* TODO: swap in this function for the 'getActiveComponents() function, above; ONLY WHEN READY / MATURE! * TODO: swap in this function for the 'getActiveComponents() function, above; ONLY WHEN READY / MATURE!
*/ */
public InstanceMap getActiveInstances() { private void updateActiveInstances() {
InstanceMap contexts = new InstanceMap(); activeInstances.clear();
getActiveContextListAt( this.rocket, contexts, Transformation.IDENTITY); getActiveContextListAt( this.rocket, activeInstances, Transformation.IDENTITY);
return contexts;
} }
private InstanceMap getActiveContextListAt(final RocketComponent component, final InstanceMap results, final Transformation parentTransform ){ private InstanceMap getActiveContextListAt(final RocketComponent component, final InstanceMap results, final Transformation parentTransform ){
final boolean active = this.isComponentActive(component);
if (!active)
return results;
final int instanceCount = component.getInstanceCount(); final int instanceCount = component.getInstanceCount();
final Coordinate[] allOffsets = component.getInstanceOffsets(); final Coordinate[] allOffsets = component.getInstanceOffsets();
final double[] allAngles = component.getInstanceAngles(); final double[] allAngles = component.getInstanceAngles();
@ -314,9 +324,11 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
final Transformation angleTransform = Transformation.getAxialRotation(allAngles[currentInstanceNumber]); final Transformation angleTransform = Transformation.getAxialRotation(allAngles[currentInstanceNumber]);
final Transformation currentTransform = componentTransform.applyTransformation(offsetTransform) final Transformation currentTransform = componentTransform.applyTransformation(offsetTransform)
.applyTransformation(angleTransform); .applyTransformation(angleTransform);
// constructs entry in-place // constructs entry in-place if this component is active
results.emplace(component, active, currentInstanceNumber, currentTransform); if (this.isComponentActive(component)) {
results.emplace(component, this.isComponentActive(component), currentInstanceNumber, currentTransform);
}
for(RocketComponent child : component.getChildren()) { for(RocketComponent child : component.getChildren()) {
getActiveContextListAt(child, results, currentTransform); getActiveContextListAt(child, results, currentTransform);
@ -401,6 +413,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
updateStages(); updateStages();
updateMotors(); updateMotors();
updateActiveInstances();
} }
private void updateStages() { private void updateStages() {
@ -531,6 +544,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
public void update(){ public void update(){
updateStages(); updateStages();
updateMotors(); updateMotors();
updateActiveInstances();
} }
/////////////// Helper methods /////////////// /////////////// Helper methods ///////////////
@ -604,35 +618,12 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
} }
for (InstanceContext context : contexts) { 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) {
// break out of per-instance loop.
break;
}
componentBounds.update(instanceBounds.transform(context.transform)); componentBounds.update(instanceBounds.transform(context.transform));
} }
} else { } else {
// Legacy Case: These components do not implement the BoxBounded Interface. // Legacy Case: These components do not implement the BoxBounded Interface.
Collection<Coordinate> instanceCoordinates = component.getComponentBounds(); Collection<Coordinate> instanceCoordinates = component.getComponentBounds();
for (InstanceContext context : contexts) { 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<Coordinate> transformedCoords = new ArrayList<>(instanceCoordinates); Collection<Coordinate> transformedCoords = new ArrayList<>(instanceCoordinates);
// mutating. Transforms coordinates in place. // mutating. Transforms coordinates in place.
context.transform.transform(instanceCoordinates); context.transform.transform(instanceCoordinates);

View File

@ -296,7 +296,8 @@ public class FreeformFinSet extends FinSet {
if (c.y > max) if (c.y > max)
max = c.y; max = c.y;
} }
return max;
return max - Math.min(points.get(points.size() - 1).y, 0);
} }
@Override @Override

View File

@ -26,9 +26,8 @@ public class InstanceContext {
return component.hashCode(); 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; component = _component;
active = _active;
instanceNumber = _instanceNumber; instanceNumber = _instanceNumber;
transform = _transform; transform = _transform;
@ -48,7 +47,6 @@ public class InstanceContext {
// ==== public ==== // ==== public ====
final public RocketComponent component; final public RocketComponent component;
final public boolean active;
final public int instanceNumber; final public int instanceNumber;
final public Transformation transform; final public Transformation transform;

View File

@ -34,7 +34,7 @@ public class InstanceMap extends HashMap<RocketComponent, ArrayList<InstanceCont
put(key, new ArrayList<InstanceContext>()); put(key, new ArrayList<InstanceContext>());
} }
final InstanceContext context = new InstanceContext(component, active, number, xform); final InstanceContext context = new InstanceContext(component, number, xform);
get(key).add(context); get(key).add(context);
} }

View File

@ -98,7 +98,7 @@ public class StageSeparationConfiguration implements FlightConfigurableParameter
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private SeparationEvent separationEvent = SeparationEvent.UPPER_IGNITION; private SeparationEvent separationEvent = SeparationEvent.EJECTION;
private double separationDelay = 0; private double separationDelay = 0;
private final List<StageSeparationConfiguration> configListeners = new LinkedList<>(); private final List<StageSeparationConfiguration> configListeners = new LinkedList<>();

View File

@ -425,24 +425,28 @@ public class BasicEventSimulationEngine implements SimulationEngine {
} }
case STAGE_SEPARATION: { case STAGE_SEPARATION: {
// Record the event.
currentStatus.getFlightData().addEvent(event);
RocketComponent boosterStage = event.getSource(); RocketComponent boosterStage = event.getSource();
final int stageNumber = boosterStage.getStageNumber(); final int stageNumber = boosterStage.getStageNumber();
// Mark the status as having dropped the booster if (currentStatus.getConfiguration().isStageActive(stageNumber-1)) {
currentStatus.getConfiguration().clearStage( stageNumber); // Record the event.
currentStatus.getFlightData().addEvent(event);
// Prepare the simulation branch
SimulationStatus boosterStatus = new SimulationStatus(currentStatus); // Mark the status as having dropped the booster
boosterStatus.setFlightData(new FlightDataBranch(boosterStage.getName(), FlightDataType.TYPE_TIME)); currentStatus.getConfiguration().clearStage( stageNumber);
// Mark the booster status as only having the booster.
boosterStatus.getConfiguration().setOnlyStage(stageNumber); // Prepare the simulation branch
toSimulate.push(boosterStatus); SimulationStatus boosterStatus = new SimulationStatus(currentStatus);
log.info(String.format("==>> @ %g; from Branch: %s ---- Branching: %s ---- \n", boosterStatus.setFlightData(new FlightDataBranch(boosterStage.getName(), FlightDataType.TYPE_TIME));
currentStatus.getSimulationTime(), // Mark the booster status as only having the booster.
currentStatus.getFlightData().getBranchName(), boosterStatus.getFlightData().getBranchName())); 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; break;
} }

View File

@ -66,7 +66,6 @@ import net.sf.openrocket.simulation.listeners.SimulationListener;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
public class TestRockets { public class TestRockets {
public final static FlightConfigurationId TEST_FCID_0 = new FlightConfigurationId("d010716e-ce0e-469d-ae46-190f3653ebbf"); public final static FlightConfigurationId TEST_FCID_0 = new FlightConfigurationId("d010716e-ce0e-469d-ae46-190f3653ebbf");
public final static FlightConfigurationId TEST_FCID_1 = new FlightConfigurationId("f41bee5b-ebb8-4d92-bce7-53001577a313"); public final static FlightConfigurationId TEST_FCID_1 = new FlightConfigurationId("f41bee5b-ebb8-4d92-bce7-53001577a313");
public final static FlightConfigurationId TEST_FCID_2 = new FlightConfigurationId("3e8d1280-53c2-4234-89a7-de215ef5cd69"); public final static FlightConfigurationId TEST_FCID_2 = new FlightConfigurationId("3e8d1280-53c2-4234-89a7-de215ef5cd69");
@ -1705,30 +1704,32 @@ public class TestRockets {
// find the body and fins // find the body and fins
final InstanceMap imap = rocket.getSelectedConfiguration().getActiveInstances(); final InstanceMap imap = rocket.getSelectedConfiguration().getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) { RocketComponent c = null;
RocketComponent c = entry.getKey(); for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
c = entry.getKey();
if (c instanceof TrapezoidFinSet) { if (c instanceof TrapezoidFinSet) {
final TrapezoidFinSet fins = (TrapezoidFinSet) c; break;
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);
} }
} }
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; return rocket;
} }

@ -1 +1 @@
Subproject commit 52e1e2c0800ebf62fd0e9cecd69aaaed6cddf80e Subproject commit 8304c0fd3dc80d65c40af4da83268ec1b32931d6

View File

@ -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<Transition.Shape>
implements ComboBoxModel<Transition.Shape>, 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);
}
}
}

View File

@ -16,6 +16,7 @@ import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel; 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.BasicSlider;
import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
@ -52,18 +53,15 @@ public class NoseConeConfig extends RocketComponentConfig {
{//// Nose cone shape: {//// Nose cone shape:
panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Noseconeshape"))); panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Noseconeshape")));
Transition.Shape selected = ((NoseCone) component).getType(); final JComboBox<Transition.Shape> typeBox = new JComboBox<>(new TransitionShapeModel(c));
Transition.Shape[] typeList = Transition.Shape.values();
final JComboBox<Transition.Shape> typeBox = new JComboBox<Transition.Shape>(typeList);
typeBox.setEditable(false); typeBox.setEditable(false);
typeBox.setSelectedItem(selected);
typeBox.addActionListener(new ActionListener() { typeBox.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem(); Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem();
((NoseCone) component).setType(s); if (s != null) {
description.setText(PREDESC + s.getNoseConeDescription()); description.setText(PREDESC + s.getNoseConeDescription());
}
updateEnabled(); updateEnabled();
} }
}); });

View File

@ -15,6 +15,7 @@ import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel; 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.BasicSlider;
import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.components.UnitSelector;
@ -54,18 +55,15 @@ public class TransitionConfig extends RocketComponentConfig {
//// Transition shape: //// Transition shape:
panel.add(new JLabel(trans.get("TransitionCfg.lbl.Transitionshape"))); panel.add(new JLabel(trans.get("TransitionCfg.lbl.Transitionshape")));
Transition.Shape selected = ((Transition) component).getType(); typeBox = new JComboBox<>(new TransitionShapeModel(c));
Transition.Shape[] typeList = Transition.Shape.values();
typeBox = new JComboBox<Transition.Shape>(typeList);
typeBox.setEditable(false); typeBox.setEditable(false);
typeBox.setSelectedItem(selected);
typeBox.addActionListener(new ActionListener() { typeBox.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem(); Transition.Shape s = (Transition.Shape) typeBox.getSelectedItem();
((Transition) component).setType(s); if (s != null) {
description.setText(PREDESC + s.getTransitionDescription()); description.setText(PREDESC + s.getTransitionDescription());
}
updateEnabled(); updateEnabled();
} }
}); });

View File

@ -85,21 +85,19 @@ public abstract class RocketRenderer {
if (ignore != null && ignore.contains(comp)) if (ignore != null && ignore.contains(comp))
continue; continue;
if( geom.active ) { final int hashCode = comp.hashCode();
final int hashCode = comp.hashCode();
selectionMap.put(hashCode, comp);
selectionMap.put(hashCode, comp);
gl.glColor4ub((byte) ((hashCode >> 24) & 0xFF), // red channel (LSB)
gl.glColor4ub((byte) ((hashCode >> 24) & 0xFF), // red channel (LSB) (byte) ((hashCode >> 16) & 0xFF), // green channel
(byte) ((hashCode >> 16) & 0xFF), // green channel (byte) ((hashCode >> 8) & 0xFF), // blue channel
(byte) ((hashCode >> 8) & 0xFF), // blue channel (byte) ((hashCode) & 0xFF)); // alpha channel (MSB)
(byte) ((hashCode) & 0xFF)); // alpha channel (MSB)
if (isDrawnTransparent(comp)) {
if (isDrawnTransparent(comp)) { geom.render(gl, Surface.INSIDE);
geom.render(gl, Surface.INSIDE); } else {
} else { geom.render(gl, Surface.ALL);
geom.render(gl, Surface.ALL);
}
} }
} }
@ -186,7 +184,6 @@ public abstract class RocketRenderer {
for(InstanceContext context: contextList ) { for(InstanceContext context: contextList ) {
Geometry instanceGeometry = cr.getComponentGeometry( comp, context.transform ); Geometry instanceGeometry = cr.getComponentGeometry( comp, context.transform );
instanceGeometry.active = context.active;
treeGeometry.add( instanceGeometry ); treeGeometry.add( instanceGeometry );
} }
} }
@ -196,19 +193,15 @@ public abstract class RocketRenderer {
private void renderTree( GL2 gl, final Collection<Geometry> geometryList){ private void renderTree( GL2 gl, final Collection<Geometry> geometryList){
//cycle through opaque components first, then transparent to preserve proper depth testing //cycle through opaque components first, then transparent to preserve proper depth testing
for(Geometry geom: geometryList ) { for(Geometry geom: geometryList ) {
if( geom.active ) { //if not transparent
//if not transparent if( !isDrawnTransparent( (RocketComponent)geom.obj) ){
if( !isDrawnTransparent( (RocketComponent)geom.obj) ){ renderComponent(gl, geom, 1.0f);
renderComponent(gl, geom, 1.0f);
}
} }
} }
for(Geometry geom: geometryList ) { for(Geometry geom: geometryList ) {
if( geom.active ) { if( isDrawnTransparent( (RocketComponent)geom.obj) ){
if( isDrawnTransparent( (RocketComponent)geom.obj) ){ // Draw T&T front faces blended, without depth test
// Draw T&T front faces blended, without depth test renderComponent(gl, geom, 0.2f);
renderComponent(gl, geom, 0.2f);
}
} }
} }
} }

View File

@ -25,8 +25,6 @@ public abstract class Geometry {
public final Object obj; public final Object obj;
public final Transformation transform; public final Transformation transform;
public boolean active;
public abstract void render(GL2 gl, Surface which ); public abstract void render(GL2 gl, Surface which );
private Geometry() { private Geometry() {

View File

@ -3,29 +3,37 @@ package net.sf.openrocket.gui.plot;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.components.StyledLabel; 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.GUIUtil;
import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.gui.widgets.SelectColorButton;
import org.jfree.chart.ChartPanel; 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. * Dialog that shows a plot of a simulation results based on user options.
@ -50,7 +58,14 @@ public class SimulationPlotDialog extends JDialog {
this.add(panel); this.add(panel);
final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart()); final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart());
final JFreeChart jChart = myPlot.getJFreeChart();
panel.add(chartPanel, "grow, wrap 20lp"); 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 //// Description text
JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2);
@ -108,6 +123,16 @@ public class SimulationPlotDialog extends JDialog {
} }
}); });
panel.add(button, "gapleft rel"); panel.add(button, "gapleft rel");
//// Print chart button
button = new SelectColorButton(trans.get("PlotDialog.btn.exportImage"));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doPngExport(chartPanel,jChart);
}
});
panel.add(button, "gapleft rel");
//// Add series selection box //// Add series selection box
ArrayList<String> stages = new ArrayList<String>(); ArrayList<String> stages = new ArrayList<String>();
@ -141,16 +166,43 @@ public class SimulationPlotDialog extends JDialog {
} }
}); });
panel.add(button, "right"); panel.add(button, "right");
this.setLocationByPlatform(true); this.setLocationByPlatform(true);
this.pack(); this.pack();
GUIUtil.setDisposableDialogOptions(this, button); GUIUtil.setDisposableDialogOptions(this, button);
GUIUtil.rememberWindowSize(this); GUIUtil.rememberWindowSize(this);
} }
private boolean doPngExport(ChartPanel chartPanel, JFreeChart chart){
JFileChooser chooser = new JFileChooser();
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileFilter(FileHelper.PNG_FILTER);
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, chartPanel.getWidth(), chartPanel.getHeight());
} catch(Exception e){
return false;
}
return true;
}
/** /**
* Static method that shows a plot with the specified parameters. * Static method that shows a plot with the specified parameters.
* *

View File

@ -378,11 +378,7 @@ public class RocketFigure extends AbstractScaleFigure {
for(InstanceContext context: contextList ) { for(InstanceContext context: contextList ) {
final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform); final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform);
allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform);
// generate shape for this component, if active
if( context.active ) {
allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform);
}
} }
} }
} }

View File

@ -56,6 +56,10 @@ public final class FileHelper {
public static final FileFilter CSV_FILTER = public static final FileFilter CSV_FILTER =
new SimpleFileFilter(trans.get("FileHelper.CSV_FILTER"), ".csv"); 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");