Merge remote-tracking branch 'origin/unstable' into add-open-airframe-warning
This commit is contained in:
commit
b3d2b92edd
@ -720,6 +720,11 @@ simplotpanel.RIGHT_NAME = Right
|
|||||||
simplotpanel.CUSTOM = Custom
|
simplotpanel.CUSTOM = Custom
|
||||||
SimulationPlotPanel.error.noPlotSelected = Please add one or more variables to plot on the Y-axis.
|
SimulationPlotPanel.error.noPlotSelected = Please add one or more variables to plot on the Y-axis.
|
||||||
SimulationPlotPanel.error.noPlotSelected.title = Nothing to plot
|
SimulationPlotPanel.error.noPlotSelected.title = Nothing to plot
|
||||||
|
simplotpanel.MarkerStyle.lbl.MarkerStyle = Marker style:
|
||||||
|
simplotpanel.MarkerStyle.lbl.MarkerStyle.ttip = Style of the flight event marker (how it's drawn in the simulation plot)
|
||||||
|
simplotpanel.MarkerStyle.btn.VerticalMarker = Vertical line
|
||||||
|
simplotpanel.MarkerStyle.btn.Icon = Icon
|
||||||
|
simplotpanel.MarkerStyle.OnlyInTime = Only available for time domain, other domains only support icon markers
|
||||||
|
|
||||||
! Component add buttons
|
! Component add buttons
|
||||||
compaddbuttons.AxialStage = Stage
|
compaddbuttons.AxialStage = Stage
|
||||||
@ -1150,6 +1155,8 @@ NoseConeCfg.tab.General = General
|
|||||||
NoseConeCfg.tab.ttip.General = General properties
|
NoseConeCfg.tab.ttip.General = General properties
|
||||||
NoseConeCfg.tab.Shoulder = Shoulder
|
NoseConeCfg.tab.Shoulder = Shoulder
|
||||||
NoseConeCfg.tab.ttip.Shoulder = Shoulder properties
|
NoseConeCfg.tab.ttip.Shoulder = Shoulder properties
|
||||||
|
NoseConeCfg.checkbox.Flip = Flip to tail cone
|
||||||
|
NoseConeCfg.checkbox.Flip.ttip = Flips the nose cone direction to a tail cone.
|
||||||
|
|
||||||
! ParachuteConfig
|
! ParachuteConfig
|
||||||
Parachute.Parachute = Parachute
|
Parachute.Parachute = Parachute
|
||||||
|
@ -76,6 +76,7 @@ public abstract class Preferences implements ChangeSource {
|
|||||||
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
|
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
|
||||||
private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN";
|
private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN";
|
||||||
private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB";
|
private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB";
|
||||||
|
public static final String MARKER_STYLE_ICON = "MARKER_STYLE_ICON";
|
||||||
private static final String SHOW_MARKERS = "SHOW_MARKERS";
|
private static final String SHOW_MARKERS = "SHOW_MARKERS";
|
||||||
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
|
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ public class LinearInterpolator implements Cloneable {
|
|||||||
|
|
||||||
if ( y1 != null ) {
|
if ( y1 != null ) {
|
||||||
// Wow, x was a key in the map. Such luck.
|
// Wow, x was a key in the map. Such luck.
|
||||||
return y1.doubleValue();
|
return y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we now know that x is not in the map, so we need to find the lower and higher keys.
|
// we now know that x is not in the map, so we need to find the lower and higher keys.
|
||||||
@ -96,16 +96,16 @@ public class LinearInterpolator implements Cloneable {
|
|||||||
Double firstKey = sortMap.firstKey();
|
Double firstKey = sortMap.firstKey();
|
||||||
|
|
||||||
// x is smaller than the first entry in the map.
|
// x is smaller than the first entry in the map.
|
||||||
if ( x < firstKey.doubleValue() ) {
|
if ( x < firstKey) {
|
||||||
y1 = sortMap.get(firstKey);
|
y1 = sortMap.get(firstKey);
|
||||||
return y1.doubleValue();
|
return y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// floor key is the largest key smaller than x - since we have at least one key,
|
// floor key is the largest key smaller than x - since we have at least one key,
|
||||||
// and x>=firstKey, we know that floorKey != null.
|
// and x>=firstKey, we know that floorKey != null.
|
||||||
Double floorKey = sortMap.subMap(firstKey, x).lastKey();
|
Double floorKey = sortMap.subMap(firstKey, x).lastKey();
|
||||||
|
|
||||||
x1 = floorKey.doubleValue();
|
x1 = floorKey;
|
||||||
y1 = sortMap.get(floorKey);
|
y1 = sortMap.get(floorKey);
|
||||||
|
|
||||||
// Now we need to find the key that is greater or equal to x
|
// Now we need to find the key that is greater or equal to x
|
||||||
@ -113,16 +113,16 @@ public class LinearInterpolator implements Cloneable {
|
|||||||
|
|
||||||
// Check if x is bigger than all the entries.
|
// Check if x is bigger than all the entries.
|
||||||
if ( tailMap.isEmpty() ) {
|
if ( tailMap.isEmpty() ) {
|
||||||
return y1.doubleValue();
|
return y1;
|
||||||
}
|
}
|
||||||
Double ceilKey = tailMap.firstKey();
|
Double ceilKey = tailMap.firstKey();
|
||||||
|
|
||||||
// Check if x is bigger than all the entries.
|
// Check if x is bigger than all the entries.
|
||||||
if ( ceilKey == null ) {
|
if ( ceilKey == null ) {
|
||||||
return y1.doubleValue();
|
return y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
x2 = ceilKey.doubleValue();
|
x2 = ceilKey;
|
||||||
y2 = sortMap.get(ceilKey);
|
y2 = sortMap.get(ceilKey);
|
||||||
|
|
||||||
return (x - x1)/(x2-x1) * (y2-y1) + y1;
|
return (x - x1)/(x2-x1) * (y2-y1) + y1;
|
||||||
|
@ -24,6 +24,8 @@ import static org.junit.Assert.assertSame;
|
|||||||
* Tests to verify that simulations contain all the expected flight events.
|
* Tests to verify that simulations contain all the expected flight events.
|
||||||
*/
|
*/
|
||||||
public class FlightEventsTest extends BaseTestCase {
|
public class FlightEventsTest extends BaseTestCase {
|
||||||
|
private static final double EPSILON = 0.005;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for a single stage design.
|
* Tests for a single stage design.
|
||||||
*/
|
*/
|
||||||
@ -66,7 +68,7 @@ public class FlightEventsTest extends BaseTestCase {
|
|||||||
// Test that the event times are correct
|
// Test that the event times are correct
|
||||||
for (int i = 0; i < expectedEventTimes.length; i++) {
|
for (int i = 0; i < expectedEventTimes.length; i++) {
|
||||||
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
|
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
|
||||||
expectedEventTimes[i], eventList.get(i).getTime(), 0.002);
|
expectedEventTimes[i], eventList.get(i).getTime(), EPSILON);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ public class FlightEventsTest extends BaseTestCase {
|
|||||||
// Test that the event times are correct
|
// Test that the event times are correct
|
||||||
for (int i = 0; i < expectedEventTimes.length; i++) {
|
for (int i = 0; i < expectedEventTimes.length; i++) {
|
||||||
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
|
assertEquals(" Flight type " + expectedEventTypes[i] + " has wrong time",
|
||||||
expectedEventTimes[i], eventList.get(i).getTime(), 0.002);
|
expectedEventTimes[i], eventList.get(i).getTime(), EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the event sources are correct
|
// Test that the event sources are correct
|
||||||
|
Binary file not shown.
BIN
swing/resources/datafiles/examples/Airstart timing.ork
Normal file
BIN
swing/resources/datafiles/examples/Airstart timing.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Chute release.ork
Normal file
BIN
swing/resources/datafiles/examples/Chute release.ork
Normal file
Binary file not shown.
BIN
swing/resources/datafiles/examples/Clustered motors.ork
Normal file
BIN
swing/resources/datafiles/examples/Clustered motors.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Dual parachute deployment.ork
Normal file
BIN
swing/resources/datafiles/examples/Dual parachute deployment.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Parallel booster staging.ork
Normal file
BIN
swing/resources/datafiles/examples/Parallel booster staging.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Presets.ork
Normal file
BIN
swing/resources/datafiles/examples/Presets.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Simulation extensions.ork
Normal file
BIN
swing/resources/datafiles/examples/Simulation extensions.ork
Normal file
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/TARC payload rocket.ork
Normal file
BIN
swing/resources/datafiles/examples/TARC payload rocket.ork
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
swing/resources/datafiles/examples/Tube fin rocket.ork
Normal file
BIN
swing/resources/datafiles/examples/Tube fin rocket.ork
Normal file
Binary file not shown.
BIN
swing/resources/datafiles/examples/Two-stage rocket.ork
Normal file
BIN
swing/resources/datafiles/examples/Two-stage rocket.ork
Normal file
Binary file not shown.
@ -1,6 +1,8 @@
|
|||||||
package net.sf.openrocket.gui.main;
|
package net.sf.openrocket.gui.main;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
@ -18,6 +20,35 @@ public final class ExampleDesignFileAction extends JMenu {
|
|||||||
*/
|
*/
|
||||||
private final BasicFrame parent;
|
private final BasicFrame parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order in which the example files should be displayed in the menu.
|
||||||
|
* A null items means there should be a separator.
|
||||||
|
* <p>
|
||||||
|
* NOTE: update this list if you add a new example file, or update the name of an existing one!!.
|
||||||
|
*/
|
||||||
|
private static final String[] exampleFileOrder = {
|
||||||
|
// Examples of basic rockets
|
||||||
|
"A simple model rocket",
|
||||||
|
"Two-stage rocket",
|
||||||
|
"Three-stage rocket",
|
||||||
|
"TARC payload rocket",
|
||||||
|
"Tube fin rocket",
|
||||||
|
null,
|
||||||
|
// Examples demonstrating complex rocket features
|
||||||
|
"Airstart timing",
|
||||||
|
"Chute release",
|
||||||
|
"Dual parachute deployment",
|
||||||
|
"Clustered motors",
|
||||||
|
"Parallel booster staging",
|
||||||
|
"Pods--airframes and winglets",
|
||||||
|
"Pods--powered with recovery deployment",
|
||||||
|
null,
|
||||||
|
// Examples demonstrating customized functionality
|
||||||
|
"Presets",
|
||||||
|
"Simulation extensions",
|
||||||
|
"Simulation extensions and scripting"
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
@ -38,11 +69,37 @@ public final class ExampleDesignFileAction extends JMenu {
|
|||||||
private void updateMenu() {
|
private void updateMenu() {
|
||||||
removeAll();
|
removeAll();
|
||||||
ExampleDesignFile[] examples = ExampleDesignFile.getExampleDesigns();
|
ExampleDesignFile[] examples = ExampleDesignFile.getExampleDesigns();
|
||||||
|
List<JMenuItem> itemList = new ArrayList<>();
|
||||||
|
|
||||||
|
// First create the menu items
|
||||||
for (ExampleDesignFile file : examples) {
|
for (ExampleDesignFile file : examples) {
|
||||||
Action action = createAction(file);
|
Action action = createAction(file);
|
||||||
action.putValue(Action.NAME, file.toString());
|
action.putValue(Action.NAME, file.toString());
|
||||||
JMenuItem menuItem = new JMenuItem(action);
|
JMenuItem menuItem = new JMenuItem(action);
|
||||||
add(menuItem);
|
itemList.add(menuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add them according to their order
|
||||||
|
for (String s : exampleFileOrder) {
|
||||||
|
if (s == null) {
|
||||||
|
addSeparator();
|
||||||
|
} else {
|
||||||
|
for (JMenuItem item : itemList) {
|
||||||
|
if (item.getText().equals(s)) {
|
||||||
|
add(item);
|
||||||
|
itemList.remove(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the remaining (unordered) items to the end
|
||||||
|
if (itemList.size() > 0) {
|
||||||
|
addSeparator();
|
||||||
|
for (JMenuItem item : itemList) {
|
||||||
|
add(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,12 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import net.sf.openrocket.document.Simulation;
|
import net.sf.openrocket.document.Simulation;
|
||||||
import net.sf.openrocket.gui.simulation.SimulationPlotPanel;
|
import net.sf.openrocket.gui.simulation.SimulationPlotPanel;
|
||||||
|
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||||
import net.sf.openrocket.simulation.FlightDataType;
|
import net.sf.openrocket.simulation.FlightDataType;
|
||||||
import net.sf.openrocket.simulation.FlightEvent;
|
import net.sf.openrocket.simulation.FlightEvent;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
|
import net.sf.openrocket.startup.Preferences;
|
||||||
import net.sf.openrocket.unit.Unit;
|
import net.sf.openrocket.unit.Unit;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.LinearInterpolator;
|
import net.sf.openrocket.util.LinearInterpolator;
|
||||||
@ -65,6 +68,8 @@ import org.jfree.ui.TextAnchor;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class SimulationPlot {
|
public class SimulationPlot {
|
||||||
|
private static final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
|
||||||
|
|
||||||
|
|
||||||
private static final float PLOT_STROKE_WIDTH = 1.5f;
|
private static final float PLOT_STROKE_WIDTH = 1.5f;
|
||||||
|
|
||||||
@ -79,7 +84,7 @@ public class SimulationPlot {
|
|||||||
|
|
||||||
private final LegendItems legendItems;
|
private final LegendItems legendItems;
|
||||||
|
|
||||||
int branchCount;
|
private int branchCount;
|
||||||
|
|
||||||
void setShowPoints(boolean showPoints) {
|
void setShowPoints(boolean showPoints) {
|
||||||
for (ModifiedXYItemRenderer r : renderers) {
|
for (ModifiedXYItemRenderer r : renderers) {
|
||||||
@ -286,32 +291,19 @@ public class SimulationPlot {
|
|||||||
}
|
}
|
||||||
XYSeries ser = collection.getSeries(series);
|
XYSeries ser = collection.getSeries(series);
|
||||||
String name = ser.getDescription();
|
String name = ser.getDescription();
|
||||||
|
|
||||||
// Extract the unit from the last part of the series description, between parenthesis
|
// Extract the unit from the last part of the series description, between parenthesis
|
||||||
Matcher m = Pattern.compile(".*\\((.*?)\\)").matcher(name);
|
Matcher m = Pattern.compile(".*\\((.*?)\\)").matcher(name);
|
||||||
String unit_y = "";
|
String unitY = "";
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
unit_y = m.group(1);
|
unitY = m.group(1);
|
||||||
}
|
}
|
||||||
String unit_x = domainUnit.getUnit();
|
String unitX = domainUnit.getUnit();
|
||||||
String ord_end = "th"; // Ordinal number ending (1'st', 2'nd'...)
|
|
||||||
if (item % 10 == 1)
|
double dataY = dataset.getYValue(series, item);
|
||||||
ord_end = "st";
|
double dataX = dataset.getXValue(series, item);
|
||||||
else if (item % 10 == 2)
|
|
||||||
ord_end = "nd";
|
return formatSampleTooltip(name, dataX, unitX, dataY, unitY, item);
|
||||||
else if (item % 10 == 3)
|
|
||||||
ord_end = "rd";
|
|
||||||
double data_y = dataset.getYValue(series, item);
|
|
||||||
double data_x = dataset.getXValue(series, item);
|
|
||||||
DecimalFormat df_y = DecimalFormatter.df(data_y, 2, false);
|
|
||||||
DecimalFormat df_x = DecimalFormatter.df(data_x, 2, false);
|
|
||||||
return String.format("<html>" +
|
|
||||||
"<b><i>%s</i></b><br>" +
|
|
||||||
"Y: %s %s<br>" +
|
|
||||||
"X: %s %s<br>" +
|
|
||||||
"%d<sup>%s</sup> sample" +
|
|
||||||
"</html>",
|
|
||||||
name, df_y.format(data_y), unit_y,
|
|
||||||
df_x.format(data_x), unit_x, item, ord_end);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -364,6 +356,42 @@ public class SimulationPlot {
|
|||||||
return chart;
|
return chart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String formatSampleTooltip(String dataName, double dataX, String unitX, double dataY, String unitY, int sampleIdx, boolean addYValue) {
|
||||||
|
String ord_end = "th"; // Ordinal number ending (1'st', 2'nd'...)
|
||||||
|
if (sampleIdx % 10 == 1) {
|
||||||
|
ord_end = "st";
|
||||||
|
} else if (sampleIdx % 10 == 2) {
|
||||||
|
ord_end = "nd";
|
||||||
|
} else if (sampleIdx % 10 == 3) {
|
||||||
|
ord_end = "rd";
|
||||||
|
}
|
||||||
|
|
||||||
|
DecimalFormat df_y = DecimalFormatter.df(dataY, 2, false);
|
||||||
|
DecimalFormat df_x = DecimalFormatter.df(dataX, 2, false);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(String.format("<html>" +
|
||||||
|
"<b><i>%s</i></b><br>", dataName));
|
||||||
|
|
||||||
|
if (addYValue) {
|
||||||
|
sb.append(String.format("Y: %s %s<br>", df_y.format(dataY), unitY));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(String.format("X: %s %s<br>" +
|
||||||
|
"%d<sup>%s</sup> sample" +
|
||||||
|
"</html>", df_x.format(dataX), unitX, sampleIdx, ord_end));
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatSampleTooltip(String dataName, double dataX, String unitX, double dataY, String unitY, int sampleIdx) {
|
||||||
|
return formatSampleTooltip(dataName, dataX, unitX, dataY, unitY, sampleIdx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatSampleTooltip(String dataName, double dataX, String unitX, int sampleIdx) {
|
||||||
|
return formatSampleTooltip(dataName, dataX, unitX, 0, "", sampleIdx, false);
|
||||||
|
}
|
||||||
|
|
||||||
private String getLabel(FlightDataType type, Unit unit) {
|
private String getLabel(FlightDataType type, Unit unit) {
|
||||||
String name = type.getName();
|
String name = type.getName();
|
||||||
if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
|
if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
|
||||||
@ -372,27 +400,58 @@ public class SimulationPlot {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawDomainMarkers(int stage) {
|
/**
|
||||||
|
* Draw the domain markers for a certain branch. Draws all the markers if the branch is -1.
|
||||||
|
* @param branch branch to draw, or -1 to draw all
|
||||||
|
*/
|
||||||
|
private void drawDomainMarkers(int branch) {
|
||||||
XYPlot plot = chart.getXYPlot();
|
XYPlot plot = chart.getXYPlot();
|
||||||
FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0);
|
FlightDataBranch dataBranch = simulation.getSimulatedData().getBranch(Math.max(branch, 0));
|
||||||
|
|
||||||
// Clear existing domain markers
|
// Clear existing domain markers and annotations
|
||||||
plot.clearDomainMarkers();
|
plot.clearDomainMarkers();
|
||||||
|
plot.clearAnnotations();
|
||||||
|
|
||||||
// Construct domain marker lists collapsing based on time.
|
// Store flight event information
|
||||||
|
List<Double> eventTimes = new ArrayList<>();
|
||||||
|
List<String> eventLabels = new ArrayList<>();
|
||||||
|
List<Color> eventColors = new ArrayList<>();
|
||||||
|
List<Image> eventImages = new ArrayList<>();
|
||||||
|
|
||||||
List<Double> eventTimes = new ArrayList<Double>();
|
// Plot the markers
|
||||||
List<String> eventLabels = new ArrayList<String>();
|
if (config.getDomainAxisType() == FlightDataType.TYPE_TIME && !preferences.getBoolean(Preferences.MARKER_STYLE_ICON, false)) {
|
||||||
List<Color> eventColors = new ArrayList<Color>();
|
fillEventLists(branch, eventTimes, eventLabels, eventColors, eventImages);
|
||||||
List<Image> eventImages = new ArrayList<Image>();
|
plotVerticalLineMarkers(plot, eventTimes, eventLabels, eventColors);
|
||||||
{
|
|
||||||
HashSet<FlightEvent.Type> typeSet = new HashSet<FlightEvent.Type>();
|
} else { // Other domains are plotted as image annotations
|
||||||
|
if (branch == -1) {
|
||||||
|
// For icon markers, we need to do the plotting separately, otherwise you can have icon markers from e.g.
|
||||||
|
// branch 1 be plotted on branch 0
|
||||||
|
for (int b = 0; b < simulation.getSimulatedData().getBranchCount(); b++) {
|
||||||
|
fillEventLists(b, eventTimes, eventLabels, eventColors, eventImages);
|
||||||
|
dataBranch = simulation.getSimulatedData().getBranch(b);
|
||||||
|
plotIconMarkers(plot, dataBranch, eventTimes, eventLabels, eventImages);
|
||||||
|
eventTimes.clear();
|
||||||
|
eventLabels.clear();
|
||||||
|
eventColors.clear();
|
||||||
|
eventImages.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fillEventLists(branch, eventTimes, eventLabels, eventColors, eventImages);
|
||||||
|
plotIconMarkers(plot, dataBranch, eventTimes, eventLabels, eventImages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillEventLists(int branch, List<Double> eventTimes, List<String> eventLabels,
|
||||||
|
List<Color> eventColors, List<Image> eventImages) {
|
||||||
|
HashSet<FlightEvent.Type> typeSet = new HashSet<>();
|
||||||
double prevTime = -100;
|
double prevTime = -100;
|
||||||
String text = null;
|
String text = null;
|
||||||
Color color = null;
|
Color color = null;
|
||||||
Image image = null;
|
Image image = null;
|
||||||
for (EventDisplayInfo info : eventList) {
|
for (EventDisplayInfo info : eventList) {
|
||||||
if (stage >= 0 && stage != info.stage) {
|
if (branch >= 0 && branch != info.stage) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +459,6 @@ public class SimulationPlot {
|
|||||||
FlightEvent.Type type = info.event.getType();
|
FlightEvent.Type type = info.event.getType();
|
||||||
|
|
||||||
if (Math.abs(t - prevTime) <= 0.05) {
|
if (Math.abs(t - prevTime) <= 0.05) {
|
||||||
|
|
||||||
if (!typeSet.contains(type)) {
|
if (!typeSet.contains(type)) {
|
||||||
text = text + ", " + type.toString();
|
text = text + ", " + type.toString();
|
||||||
color = EventGraphics.getEventColor(type);
|
color = EventGraphics.getEventColor(type);
|
||||||
@ -409,7 +467,6 @@ public class SimulationPlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
eventTimes.add(prevTime);
|
eventTimes.add(prevTime);
|
||||||
eventLabels.add(text);
|
eventLabels.add(text);
|
||||||
@ -433,11 +490,10 @@ public class SimulationPlot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plot the markers
|
private static void plotVerticalLineMarkers(XYPlot plot, List<Double> eventTimes, List<String> eventLabels, List<Color> eventColors) {
|
||||||
if (config.getDomainAxisType() == FlightDataType.TYPE_TIME) {
|
|
||||||
double markerWidth = 0.01 * plot.getDomainAxis().getUpperBound();
|
double markerWidth = 0.01 * plot.getDomainAxis().getUpperBound();
|
||||||
|
|
||||||
// Domain time is plotted as vertical markers
|
// Domain time is plotted as vertical lines
|
||||||
for (int i = 0; i < eventTimes.size(); i++) {
|
for (int i = 0; i < eventTimes.size(); i++) {
|
||||||
double t = eventTimes.get(i);
|
double t = eventTimes.get(i);
|
||||||
String event = eventLabels.get(i);
|
String event = eventLabels.get(i);
|
||||||
@ -455,27 +511,28 @@ public class SimulationPlot {
|
|||||||
plot.setDomainAxis(new PresetNumberAxis(plot.getDomainAxis().getLowerBound(), t + markerWidth));
|
plot.setDomainAxis(new PresetNumberAxis(plot.getDomainAxis().getLowerBound(), t + markerWidth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
private void plotIconMarkers(XYPlot plot, FlightDataBranch dataBranch, List<Double> eventTimes,
|
||||||
|
List<String> eventLabels, List<Image> eventImages) {
|
||||||
// Other domains are plotted as image annotations
|
List<Double> time = dataBranch.get(FlightDataType.TYPE_TIME);
|
||||||
List<Double> time = mainBranch.get(FlightDataType.TYPE_TIME);
|
List<Double> domain = dataBranch.get(config.getDomainAxisType());
|
||||||
List<Double> domain = mainBranch.get(config.getDomainAxisType());
|
|
||||||
|
|
||||||
LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain);
|
LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain);
|
||||||
|
|
||||||
for (int i = 0; i < eventTimes.size(); i++) {
|
for (int i = 0; i < eventTimes.size(); i++) {
|
||||||
double t = eventTimes.get(i);
|
double t = eventTimes.get(i);
|
||||||
String event = eventLabels.get(i);
|
|
||||||
Image image = eventImages.get(i);
|
Image image = eventImages.get(i);
|
||||||
|
|
||||||
if (image == null)
|
if (image == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
double xcoord = domainInterpolator.getValue(t);
|
double xcoord = domainInterpolator.getValue(t);
|
||||||
|
|
||||||
for (int index = 0; index < config.getTypeCount(); index++) {
|
for (int index = 0; index < config.getTypeCount(); index++) {
|
||||||
FlightDataType type = config.getType(index);
|
FlightDataType type = config.getType(index);
|
||||||
List<Double> range = mainBranch.get(type);
|
List<Double> range = dataBranch.get(type);
|
||||||
|
|
||||||
LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range);
|
LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range);
|
||||||
// Image annotations are not supported on the right-side axis
|
// Image annotations are not supported on the right-side axis
|
||||||
@ -490,14 +547,22 @@ public class SimulationPlot {
|
|||||||
xcoord = config.getDomainAxisUnit().toUnit(xcoord);
|
xcoord = config.getDomainAxisUnit().toUnit(xcoord);
|
||||||
ycoord = config.getUnit(index).toUnit(ycoord);
|
ycoord = config.getUnit(index).toUnit(ycoord);
|
||||||
|
|
||||||
|
// Get the sample index of the flight event. Because this can be an interpolation between two samples,
|
||||||
|
// take the closest sample.
|
||||||
|
final int sampleIdx;
|
||||||
|
Optional<Double> closestSample = time.stream()
|
||||||
|
.min(Comparator.comparingDouble(sample -> Math.abs(sample - t)));
|
||||||
|
sampleIdx = closestSample.map(time::indexOf).orElse(-1);
|
||||||
|
|
||||||
|
String tooltipText = formatSampleTooltip(eventLabels.get(i), xcoord, config.getDomainAxisUnit().getUnit(), sampleIdx) ;
|
||||||
|
|
||||||
XYImageAnnotation annotation =
|
XYImageAnnotation annotation =
|
||||||
new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
|
new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
|
||||||
annotation.setToolTipText(event);
|
annotation.setToolTipText(tooltipText);
|
||||||
plot.addAnnotation(annotation);
|
plot.addAnnotation(annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private List<EventDisplayInfo> buildEventInfo() {
|
private List<EventDisplayInfo> buildEventInfo() {
|
||||||
ArrayList<EventDisplayInfo> eventList = new ArrayList<EventDisplayInfo>();
|
ArrayList<EventDisplayInfo> eventList = new ArrayList<EventDisplayInfo>();
|
||||||
|
@ -9,12 +9,15 @@ import java.util.Arrays;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.ButtonGroup;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JRadioButton;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.table.AbstractTableModel;
|
import javax.swing.table.AbstractTableModel;
|
||||||
@ -29,11 +32,13 @@ import net.sf.openrocket.gui.plot.PlotConfiguration;
|
|||||||
import net.sf.openrocket.gui.plot.SimulationPlotDialog;
|
import net.sf.openrocket.gui.plot.SimulationPlotDialog;
|
||||||
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.simulation.FlightDataBranch;
|
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||||
import net.sf.openrocket.simulation.FlightDataType;
|
import net.sf.openrocket.simulation.FlightDataType;
|
||||||
import net.sf.openrocket.simulation.FlightEvent;
|
import net.sf.openrocket.simulation.FlightEvent;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
|
import net.sf.openrocket.startup.Preferences;
|
||||||
import net.sf.openrocket.unit.Unit;
|
import net.sf.openrocket.unit.Unit;
|
||||||
import net.sf.openrocket.util.Utils;
|
import net.sf.openrocket.util.Utils;
|
||||||
import net.sf.openrocket.gui.widgets.SelectColorButton;
|
import net.sf.openrocket.gui.widgets.SelectColorButton;
|
||||||
@ -47,6 +52,7 @@ public class SimulationPlotPanel extends JPanel {
|
|||||||
private static final long serialVersionUID = -2227129713185477998L;
|
private static final long serialVersionUID = -2227129713185477998L;
|
||||||
|
|
||||||
private static final Translator trans = Application.getTranslator();
|
private static final Translator trans = Application.getTranslator();
|
||||||
|
private static final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
|
||||||
|
|
||||||
// TODO: LOW: Should these be somewhere else?
|
// TODO: LOW: Should these be somewhere else?
|
||||||
public static final int AUTO = -1;
|
public static final int AUTO = -1;
|
||||||
@ -201,7 +207,7 @@ public class SimulationPlotPanel extends JPanel {
|
|||||||
|
|
||||||
typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
|
typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
|
||||||
JScrollPane scroll = new JScrollPane(typeSelectorPanel);
|
JScrollPane scroll = new JScrollPane(typeSelectorPanel);
|
||||||
this.add(scroll, "spany 2, height 10px, wmin 400lp, grow 100, gapright para");
|
this.add(scroll, "spany 3, height 10px, wmin 400lp, grow 100, gapright para");
|
||||||
|
|
||||||
|
|
||||||
//// Flight events
|
//// Flight events
|
||||||
@ -244,9 +250,45 @@ public class SimulationPlotPanel extends JPanel {
|
|||||||
eventTableModel.fireTableDataChanged();
|
eventTableModel.fireTableDataChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
|
this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap");
|
||||||
|
|
||||||
|
|
||||||
|
//// Style event marker
|
||||||
|
JLabel styleEventMarker = new JLabel(trans.get("simplotpanel.MarkerStyle.lbl.MarkerStyle"));
|
||||||
|
JRadioButton radioVerticalMarker = new JRadioButton(trans.get("simplotpanel.MarkerStyle.btn.VerticalMarker"));
|
||||||
|
JRadioButton radioIcon = new JRadioButton(trans.get("simplotpanel.MarkerStyle.btn.Icon"));
|
||||||
|
ButtonGroup bg = new ButtonGroup();
|
||||||
|
bg.add(radioVerticalMarker);
|
||||||
|
bg.add(radioIcon);
|
||||||
|
|
||||||
|
boolean useIcon = preferences.getBoolean(Preferences.MARKER_STYLE_ICON, false);
|
||||||
|
if (useIcon) {
|
||||||
|
radioIcon.setSelected(true);
|
||||||
|
} else {
|
||||||
|
radioVerticalMarker.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
radioIcon.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (modifying > 0)
|
||||||
|
return;
|
||||||
|
preferences.putBoolean(Preferences.MARKER_STYLE_ICON, radioIcon.isSelected());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
domainTypeSelector.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
updateStyleEventWidgets(styleEventMarker, radioVerticalMarker, radioIcon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateStyleEventWidgets(styleEventMarker, radioVerticalMarker, radioIcon);
|
||||||
|
|
||||||
|
this.add(styleEventMarker, "split 3, growx");
|
||||||
|
this.add(radioVerticalMarker);
|
||||||
|
this.add(radioIcon, "wrap para");
|
||||||
|
|
||||||
|
|
||||||
//// New Y axis plot type
|
//// New Y axis plot type
|
||||||
button = new SelectColorButton(trans.get("simplotpanel.but.NewYaxisplottype"));
|
button = new SelectColorButton(trans.get("simplotpanel.but.NewYaxisplottype"));
|
||||||
@ -323,6 +365,19 @@ public class SimulationPlotPanel extends JPanel {
|
|||||||
updatePlots();
|
updatePlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateStyleEventWidgets(JLabel styleEventMarker, JRadioButton radioVerticalMarker, JRadioButton radioIcon) {
|
||||||
|
if (modifying > 0)
|
||||||
|
return;
|
||||||
|
FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
|
||||||
|
boolean isTime = type == FlightDataType.TYPE_TIME;
|
||||||
|
styleEventMarker.setEnabled(isTime);
|
||||||
|
radioVerticalMarker.setEnabled(isTime);
|
||||||
|
radioIcon.setEnabled(isTime);
|
||||||
|
styleEventMarker.setToolTipText(isTime ? trans.get("simplotpanel.MarkerStyle.lbl.MarkerStyle.ttip") : trans.get("simplotpanel.MarkerStyle.OnlyInTime"));
|
||||||
|
radioVerticalMarker.setToolTipText(isTime ? null : trans.get("simplotpanel.MarkerStyle.OnlyInTime"));
|
||||||
|
radioIcon.setToolTipText(isTime ? null : trans.get("simplotpanel.MarkerStyle.OnlyInTime"));
|
||||||
|
}
|
||||||
|
|
||||||
public JDialog doPlot(Window parent) {
|
public JDialog doPlot(Window parent) {
|
||||||
if (configuration.getTypeCount() == 0) {
|
if (configuration.getTypeCount() == 0) {
|
||||||
JOptionPane.showMessageDialog(SimulationPlotPanel.this,
|
JOptionPane.showMessageDialog(SimulationPlotPanel.this,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user