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
|
||||
SimulationPlotPanel.error.noPlotSelected = Please add one or more variables to plot on the Y-axis.
|
||||
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
|
||||
compaddbuttons.AxialStage = Stage
|
||||
@ -1150,6 +1155,8 @@ NoseConeCfg.tab.General = General
|
||||
NoseConeCfg.tab.ttip.General = General properties
|
||||
NoseConeCfg.tab.Shoulder = Shoulder
|
||||
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
|
||||
Parachute.Parachute = Parachute
|
||||
|
@ -76,6 +76,7 @@ public abstract class Preferences implements ChangeSource {
|
||||
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 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_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
|
||||
|
||||
|
@ -82,7 +82,7 @@ public class LinearInterpolator implements Cloneable {
|
||||
|
||||
if ( y1 != null ) {
|
||||
// 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.
|
||||
@ -96,16 +96,16 @@ public class LinearInterpolator implements Cloneable {
|
||||
Double firstKey = sortMap.firstKey();
|
||||
|
||||
// x is smaller than the first entry in the map.
|
||||
if ( x < firstKey.doubleValue() ) {
|
||||
if ( x < 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,
|
||||
// and x>=firstKey, we know that floorKey != null.
|
||||
Double floorKey = sortMap.subMap(firstKey, x).lastKey();
|
||||
|
||||
x1 = floorKey.doubleValue();
|
||||
x1 = floorKey;
|
||||
y1 = sortMap.get(floorKey);
|
||||
|
||||
// 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.
|
||||
if ( tailMap.isEmpty() ) {
|
||||
return y1.doubleValue();
|
||||
return y1;
|
||||
}
|
||||
Double ceilKey = tailMap.firstKey();
|
||||
|
||||
// Check if x is bigger than all the entries.
|
||||
if ( ceilKey == null ) {
|
||||
return y1.doubleValue();
|
||||
return y1;
|
||||
}
|
||||
|
||||
x2 = ceilKey.doubleValue();
|
||||
x2 = ceilKey;
|
||||
y2 = sortMap.get(ceilKey);
|
||||
|
||||
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.
|
||||
*/
|
||||
public class FlightEventsTest extends BaseTestCase {
|
||||
private static final double EPSILON = 0.005;
|
||||
|
||||
/**
|
||||
* Tests for a single stage design.
|
||||
*/
|
||||
@ -66,7 +68,7 @@ public class FlightEventsTest extends BaseTestCase {
|
||||
// Test that the event times are correct
|
||||
for (int i = 0; i < expectedEventTimes.length; i++) {
|
||||
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
|
||||
for (int i = 0; i < expectedEventTimes.length; i++) {
|
||||
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
|
||||
|
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;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
@ -18,6 +20,35 @@ public final class ExampleDesignFileAction extends JMenu {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -38,11 +69,37 @@ public final class ExampleDesignFileAction extends JMenu {
|
||||
private void updateMenu() {
|
||||
removeAll();
|
||||
ExampleDesignFile[] examples = ExampleDesignFile.getExampleDesigns();
|
||||
List<JMenuItem> itemList = new ArrayList<>();
|
||||
|
||||
// First create the menu items
|
||||
for (ExampleDesignFile file : examples) {
|
||||
Action action = createAction(file);
|
||||
action.putValue(Action.NAME, file.toString());
|
||||
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.gui.simulation.SimulationPlotPanel;
|
||||
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
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.UnitGroup;
|
||||
import net.sf.openrocket.util.LinearInterpolator;
|
||||
@ -65,6 +68,8 @@ import org.jfree.ui.TextAnchor;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SimulationPlot {
|
||||
private static final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
|
||||
|
||||
|
||||
private static final float PLOT_STROKE_WIDTH = 1.5f;
|
||||
|
||||
@ -79,7 +84,7 @@ public class SimulationPlot {
|
||||
|
||||
private final LegendItems legendItems;
|
||||
|
||||
int branchCount;
|
||||
private int branchCount;
|
||||
|
||||
void setShowPoints(boolean showPoints) {
|
||||
for (ModifiedXYItemRenderer r : renderers) {
|
||||
@ -286,32 +291,19 @@ public class SimulationPlot {
|
||||
}
|
||||
XYSeries ser = collection.getSeries(series);
|
||||
String name = ser.getDescription();
|
||||
|
||||
// Extract the unit from the last part of the series description, between parenthesis
|
||||
Matcher m = Pattern.compile(".*\\((.*?)\\)").matcher(name);
|
||||
String unit_y = "";
|
||||
String unitY = "";
|
||||
if (m.find()) {
|
||||
unit_y = m.group(1);
|
||||
unitY = m.group(1);
|
||||
}
|
||||
String unit_x = domainUnit.getUnit();
|
||||
String ord_end = "th"; // Ordinal number ending (1'st', 2'nd'...)
|
||||
if (item % 10 == 1)
|
||||
ord_end = "st";
|
||||
else if (item % 10 == 2)
|
||||
ord_end = "nd";
|
||||
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);
|
||||
String unitX = domainUnit.getUnit();
|
||||
|
||||
double dataY = dataset.getYValue(series, item);
|
||||
double dataX = dataset.getXValue(series, item);
|
||||
|
||||
return formatSampleTooltip(name, dataX, unitX, dataY, unitY, item);
|
||||
}
|
||||
};
|
||||
|
||||
@ -364,6 +356,42 @@ public class SimulationPlot {
|
||||
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) {
|
||||
String name = type.getName();
|
||||
if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) &&
|
||||
@ -372,129 +400,166 @@ public class SimulationPlot {
|
||||
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();
|
||||
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.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>();
|
||||
List<String> eventLabels = new ArrayList<String>();
|
||||
List<Color> eventColors = new ArrayList<Color>();
|
||||
List<Image> eventImages = new ArrayList<Image>();
|
||||
{
|
||||
HashSet<FlightEvent.Type> typeSet = new HashSet<FlightEvent.Type>();
|
||||
double prevTime = -100;
|
||||
String text = null;
|
||||
Color color = null;
|
||||
Image image = null;
|
||||
for (EventDisplayInfo info : eventList) {
|
||||
if (stage >= 0 && stage != info.stage) {
|
||||
continue;
|
||||
// Plot the markers
|
||||
if (config.getDomainAxisType() == FlightDataType.TYPE_TIME && !preferences.getBoolean(Preferences.MARKER_STYLE_ICON, false)) {
|
||||
fillEventLists(branch, eventTimes, eventLabels, eventColors, eventImages);
|
||||
plotVerticalLineMarkers(plot, eventTimes, eventLabels, eventColors);
|
||||
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double t = info.time;
|
||||
FlightEvent.Type type = info.event.getType();
|
||||
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;
|
||||
String text = null;
|
||||
Color color = null;
|
||||
Image image = null;
|
||||
for (EventDisplayInfo info : eventList) {
|
||||
if (branch >= 0 && branch != info.stage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Math.abs(t - prevTime) <= 0.05) {
|
||||
double t = info.time;
|
||||
FlightEvent.Type type = info.event.getType();
|
||||
|
||||
if (!typeSet.contains(type)) {
|
||||
text = text + ", " + type.toString();
|
||||
color = EventGraphics.getEventColor(type);
|
||||
image = EventGraphics.getEventImage(type);
|
||||
typeSet.add(type);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (text != null) {
|
||||
eventTimes.add(prevTime);
|
||||
eventLabels.add(text);
|
||||
eventColors.add(color);
|
||||
eventImages.add(image);
|
||||
}
|
||||
prevTime = t;
|
||||
text = type.toString();
|
||||
if (Math.abs(t - prevTime) <= 0.05) {
|
||||
if (!typeSet.contains(type)) {
|
||||
text = text + ", " + type.toString();
|
||||
color = EventGraphics.getEventColor(type);
|
||||
image = EventGraphics.getEventImage(type);
|
||||
typeSet.clear();
|
||||
typeSet.add(type);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (text != null) {
|
||||
eventTimes.add(prevTime);
|
||||
eventLabels.add(text);
|
||||
eventColors.add(color);
|
||||
eventImages.add(image);
|
||||
}
|
||||
prevTime = t;
|
||||
text = type.toString();
|
||||
color = EventGraphics.getEventColor(type);
|
||||
image = EventGraphics.getEventImage(type);
|
||||
typeSet.clear();
|
||||
typeSet.add(type);
|
||||
}
|
||||
if (text != null) {
|
||||
eventTimes.add(prevTime);
|
||||
eventLabels.add(text);
|
||||
eventColors.add(color);
|
||||
eventImages.add(image);
|
||||
|
||||
}
|
||||
if (text != null) {
|
||||
eventTimes.add(prevTime);
|
||||
eventLabels.add(text);
|
||||
eventColors.add(color);
|
||||
eventImages.add(image);
|
||||
}
|
||||
}
|
||||
|
||||
private static void plotVerticalLineMarkers(XYPlot plot, List<Double> eventTimes, List<String> eventLabels, List<Color> eventColors) {
|
||||
double markerWidth = 0.01 * plot.getDomainAxis().getUpperBound();
|
||||
|
||||
// Domain time is plotted as vertical lines
|
||||
for (int i = 0; i < eventTimes.size(); i++) {
|
||||
double t = eventTimes.get(i);
|
||||
String event = eventLabels.get(i);
|
||||
Color color = eventColors.get(i);
|
||||
|
||||
ValueMarker m = new ValueMarker(t);
|
||||
m.setLabel(event);
|
||||
m.setPaint(color);
|
||||
m.setLabelPaint(color);
|
||||
m.setAlpha(0.7f);
|
||||
m.setLabelFont(new Font("Dialog", Font.PLAIN, 13));
|
||||
plot.addDomainMarker(m);
|
||||
|
||||
if (t > plot.getDomainAxis().getUpperBound() - markerWidth) {
|
||||
plot.setDomainAxis(new PresetNumberAxis(plot.getDomainAxis().getLowerBound(), t + markerWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plot the markers
|
||||
if (config.getDomainAxisType() == FlightDataType.TYPE_TIME) {
|
||||
double markerWidth = 0.01 * plot.getDomainAxis().getUpperBound();
|
||||
private void plotIconMarkers(XYPlot plot, FlightDataBranch dataBranch, List<Double> eventTimes,
|
||||
List<String> eventLabels, List<Image> eventImages) {
|
||||
List<Double> time = dataBranch.get(FlightDataType.TYPE_TIME);
|
||||
List<Double> domain = dataBranch.get(config.getDomainAxisType());
|
||||
|
||||
// Domain time is plotted as vertical markers
|
||||
for (int i = 0; i < eventTimes.size(); i++) {
|
||||
double t = eventTimes.get(i);
|
||||
String event = eventLabels.get(i);
|
||||
Color color = eventColors.get(i);
|
||||
LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain);
|
||||
|
||||
ValueMarker m = new ValueMarker(t);
|
||||
m.setLabel(event);
|
||||
m.setPaint(color);
|
||||
m.setLabelPaint(color);
|
||||
m.setAlpha(0.7f);
|
||||
m.setLabelFont(new Font("Dialog", Font.PLAIN, 13));
|
||||
plot.addDomainMarker(m);
|
||||
for (int i = 0; i < eventTimes.size(); i++) {
|
||||
double t = eventTimes.get(i);
|
||||
Image image = eventImages.get(i);
|
||||
|
||||
if (t > plot.getDomainAxis().getUpperBound() - markerWidth) {
|
||||
plot.setDomainAxis(new PresetNumberAxis(plot.getDomainAxis().getLowerBound(), t + markerWidth));
|
||||
}
|
||||
if (image == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
double xcoord = domainInterpolator.getValue(t);
|
||||
|
||||
// Other domains are plotted as image annotations
|
||||
List<Double> time = mainBranch.get(FlightDataType.TYPE_TIME);
|
||||
List<Double> domain = mainBranch.get(config.getDomainAxisType());
|
||||
for (int index = 0; index < config.getTypeCount(); index++) {
|
||||
FlightDataType type = config.getType(index);
|
||||
List<Double> range = dataBranch.get(type);
|
||||
|
||||
LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain);
|
||||
|
||||
for (int i = 0; i < eventTimes.size(); i++) {
|
||||
double t = eventTimes.get(i);
|
||||
String event = eventLabels.get(i);
|
||||
Image image = eventImages.get(i);
|
||||
|
||||
if (image == null)
|
||||
LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range);
|
||||
// Image annotations are not supported on the right-side axis
|
||||
// TODO: LOW: Can this be achieved by JFreeChart?
|
||||
if (filled.getAxis(index) != SimulationPlotPanel.LEFT) {
|
||||
continue;
|
||||
|
||||
double xcoord = domainInterpolator.getValue(t);
|
||||
for (int index = 0; index < config.getTypeCount(); index++) {
|
||||
FlightDataType type = config.getType(index);
|
||||
List<Double> range = mainBranch.get(type);
|
||||
|
||||
LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range);
|
||||
// Image annotations are not supported on the right-side axis
|
||||
// TODO: LOW: Can this be achieved by JFreeChart?
|
||||
if (filled.getAxis(index) != SimulationPlotPanel.LEFT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double ycoord = rangeInterpolator.getValue(t);
|
||||
|
||||
// Convert units
|
||||
xcoord = config.getDomainAxisUnit().toUnit(xcoord);
|
||||
ycoord = config.getUnit(index).toUnit(ycoord);
|
||||
|
||||
XYImageAnnotation annotation =
|
||||
new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
|
||||
annotation.setToolTipText(event);
|
||||
plot.addAnnotation(annotation);
|
||||
}
|
||||
|
||||
double ycoord = rangeInterpolator.getValue(t);
|
||||
|
||||
// Convert units
|
||||
xcoord = config.getDomainAxisUnit().toUnit(xcoord);
|
||||
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 =
|
||||
new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER);
|
||||
annotation.setToolTipText(tooltipText);
|
||||
plot.addAnnotation(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,15 @@ import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
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.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.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
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.util.Utils;
|
||||
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 Translator trans = Application.getTranslator();
|
||||
private static final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
|
||||
|
||||
// TODO: LOW: Should these be somewhere else?
|
||||
public static final int AUTO = -1;
|
||||
@ -201,7 +207,7 @@ public class SimulationPlotPanel extends JPanel {
|
||||
|
||||
typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
|
||||
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
|
||||
@ -244,10 +250,46 @@ public class SimulationPlotPanel extends JPanel {
|
||||
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
|
||||
button = new SelectColorButton(trans.get("simplotpanel.but.NewYaxisplottype"));
|
||||
button.addActionListener(new ActionListener() {
|
||||
@ -322,6 +364,19 @@ public class SimulationPlotPanel extends JPanel {
|
||||
*/
|
||||
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) {
|
||||
if (configuration.getTypeCount() == 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user