Usability enhancements to the simulation plot.
- Made mouse wheel zoom. alt mouse wheel zooms only domain. - added explicit zoom buttons. alt button zooms domain only. - mouse drag pans the plot. - use simulation name for window name & plot title.
This commit is contained in:
parent
4035e711c5
commit
4de4d0d728
@ -1097,10 +1097,8 @@ TCMotorSelPan.noDescription = No description available.
|
|||||||
|
|
||||||
|
|
||||||
! PlotDialog
|
! PlotDialog
|
||||||
PlotDialog.title.Flightdataplot = Flight data plot
|
|
||||||
PlotDialog.Chart.Simulatedflight = Simulated flight
|
|
||||||
PlotDialog.CheckBox.Showdatapoints = Show data points
|
PlotDialog.CheckBox.Showdatapoints = Show data points
|
||||||
PlotDialog.lbl.Chart = Click and drag down+right to zoom in, up+left to zoom out
|
PlotDialog.lbl.Chart = mouse wheel to zoom. alt-mouse wheel to zoom x axis only. drag to pan.
|
||||||
|
|
||||||
|
|
||||||
! "main" prefix is used for the main application dialog
|
! "main" prefix is used for the main application dialog
|
||||||
|
238
core/src/net/sf/openrocket/gui/plot/SimulationChart.java
Normal file
238
core/src/net/sf/openrocket/gui/plot/SimulationChart.java
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
package net.sf.openrocket.gui.plot;
|
||||||
|
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.jfree.chart.ChartPanel;
|
||||||
|
import org.jfree.chart.ChartRenderingInfo;
|
||||||
|
import org.jfree.chart.JFreeChart;
|
||||||
|
import org.jfree.chart.plot.Pannable;
|
||||||
|
import org.jfree.chart.plot.PiePlot;
|
||||||
|
import org.jfree.chart.plot.Plot;
|
||||||
|
import org.jfree.chart.plot.PlotOrientation;
|
||||||
|
import org.jfree.chart.plot.PlotRenderingInfo;
|
||||||
|
import org.jfree.chart.plot.Zoomable;
|
||||||
|
|
||||||
|
import com.jogamp.newt.event.InputEvent;
|
||||||
|
|
||||||
|
public class SimulationChart extends ChartPanel {
|
||||||
|
|
||||||
|
private Point2D panLast;
|
||||||
|
private double panW;
|
||||||
|
private double panH;
|
||||||
|
|
||||||
|
private MouseWheelHandler mouseWheelHandler = null;
|
||||||
|
|
||||||
|
public SimulationChart(JFreeChart chart, boolean properties, boolean save,
|
||||||
|
boolean print, boolean zoom, boolean tooltips) {
|
||||||
|
super(chart, properties, save, print, zoom, tooltips);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationChart(JFreeChart chart, boolean useBuffer) {
|
||||||
|
super(chart, useBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationChart(JFreeChart chart, int width, int height,
|
||||||
|
int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth,
|
||||||
|
int maximumDrawHeight, boolean useBuffer, boolean properties,
|
||||||
|
boolean copy, boolean save, boolean print, boolean zoom,
|
||||||
|
boolean tooltips) {
|
||||||
|
super(chart, width, height, minimumDrawWidth, minimumDrawHeight,
|
||||||
|
maximumDrawWidth, maximumDrawHeight, useBuffer, properties, copy, save,
|
||||||
|
print, zoom, tooltips);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationChart(JFreeChart chart, int width, int height,
|
||||||
|
int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth,
|
||||||
|
int maximumDrawHeight, boolean useBuffer, boolean properties,
|
||||||
|
boolean save, boolean print, boolean zoom, boolean tooltips) {
|
||||||
|
super(chart, width, height, minimumDrawWidth, minimumDrawHeight,
|
||||||
|
maximumDrawWidth, maximumDrawHeight, useBuffer, properties, save,
|
||||||
|
print, zoom, tooltips);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationChart(JFreeChart chart) {
|
||||||
|
super(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMouseWheelEnabled() {
|
||||||
|
return mouseWheelHandler != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMouseWheelEnabled(boolean flag) {
|
||||||
|
if ( flag && mouseWheelHandler == null ) {
|
||||||
|
this.mouseWheelHandler = new MouseWheelHandler();
|
||||||
|
this.addMouseWheelListener(this.mouseWheelHandler);
|
||||||
|
} else if ( !flag && mouseWheelHandler != null ) {
|
||||||
|
this.removeMouseWheelListener(this.mouseWheelHandler);
|
||||||
|
this.mouseWheelHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if ( e.getButton() == MouseEvent.BUTTON1 ) {
|
||||||
|
// if no modifiers, use pan
|
||||||
|
Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY());
|
||||||
|
|
||||||
|
if ( screenDataArea != null && screenDataArea.contains(e.getPoint())) {
|
||||||
|
this.panW = screenDataArea.getWidth();
|
||||||
|
this.panH = screenDataArea.getHeight();
|
||||||
|
this.panLast = e.getPoint();
|
||||||
|
setCursor( Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR));
|
||||||
|
}
|
||||||
|
} else if ( e.getButton() == MouseEvent.BUTTON2 ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
if ( panLast == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double dx = e.getX() - this.panLast.getX();
|
||||||
|
double dy = e.getY() - this.panLast.getY();
|
||||||
|
if ( dx == 0.0 && dy == 0.0 ) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
double wPercent = -dx / this.panW;
|
||||||
|
double hPercent = dy / this.panH;
|
||||||
|
boolean old = this.getChart().getPlot().isNotify();
|
||||||
|
this.getChart().getPlot().setNotify(false);
|
||||||
|
Pannable p = (Pannable) this.getChart().getPlot();
|
||||||
|
if ( p.getOrientation() == PlotOrientation.VERTICAL){
|
||||||
|
p.panDomainAxes( wPercent, this.getChartRenderingInfo().getPlotInfo(),panLast);
|
||||||
|
p.panRangeAxes( hPercent, this.getChartRenderingInfo().getPlotInfo(),panLast);
|
||||||
|
} else {
|
||||||
|
p.panDomainAxes( hPercent, this.getChartRenderingInfo().getPlotInfo(),panLast);
|
||||||
|
p.panRangeAxes( wPercent, this.getChartRenderingInfo().getPlotInfo(),panLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.panLast = e.getPoint();
|
||||||
|
this.getChart().getPlot().setNotify(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if ( this.panLast != null ) {
|
||||||
|
this.panLast = null;
|
||||||
|
setCursor(Cursor.getDefaultCursor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Hacked up copy of MouseWheelHandler from JFreechart. This version
|
||||||
|
* has the special ability to only zoom on the domain if the alt key is pressed.
|
||||||
|
*
|
||||||
|
* A class that handles mouse wheel events for the {@link ChartPanel} class.
|
||||||
|
* Mouse wheel event support was added in JDK 1.4, so this class will be omitted
|
||||||
|
* from JFreeChart if you build it using JDK 1.3.
|
||||||
|
*
|
||||||
|
* @since 1.0.13
|
||||||
|
*/
|
||||||
|
class MouseWheelHandler implements MouseWheelListener, Serializable {
|
||||||
|
|
||||||
|
/** The zoom factor. */
|
||||||
|
double zoomFactor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance for the specified chart panel.
|
||||||
|
*
|
||||||
|
* @param chartPanel the chart panel (<code>null</code> not permitted).
|
||||||
|
*/
|
||||||
|
public MouseWheelHandler() {
|
||||||
|
this.zoomFactor = 0.10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current zoom factor. The default value is 0.10 (ten
|
||||||
|
* percent).
|
||||||
|
*
|
||||||
|
* @return The zoom factor.
|
||||||
|
*
|
||||||
|
* @see #setZoomFactor(double)
|
||||||
|
*/
|
||||||
|
public double getZoomFactor() {
|
||||||
|
return this.zoomFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the zoom factor.
|
||||||
|
*
|
||||||
|
* @param zoomFactor the zoom factor.
|
||||||
|
*
|
||||||
|
* @see #getZoomFactor()
|
||||||
|
*/
|
||||||
|
public void setZoomFactor(double zoomFactor) {
|
||||||
|
this.zoomFactor = zoomFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a mouse wheel event from the underlying chart panel.
|
||||||
|
*
|
||||||
|
* @param e the event.
|
||||||
|
*/
|
||||||
|
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||||
|
JFreeChart chart = SimulationChart.this.getChart();
|
||||||
|
if (chart == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Plot plot = chart.getPlot();
|
||||||
|
if (plot instanceof Zoomable) {
|
||||||
|
boolean domainOnly = ( e.getModifiers() & InputEvent.ALT_MASK ) != 0;
|
||||||
|
Zoomable zoomable = (Zoomable) plot;
|
||||||
|
handleZoomable(zoomable, e, domainOnly);
|
||||||
|
}
|
||||||
|
else if (plot instanceof PiePlot) {
|
||||||
|
PiePlot pp = (PiePlot) plot;
|
||||||
|
pp.handleMouseWheelRotation(e.getWheelRotation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the case where a plot implements the {@link Zoomable} interface.
|
||||||
|
*
|
||||||
|
* @param zoomable the zoomable plot.
|
||||||
|
* @param e the mouse wheel event.
|
||||||
|
*/
|
||||||
|
private void handleZoomable(Zoomable zoomable, MouseWheelEvent e, boolean domainOnly) {
|
||||||
|
// don't zoom unless the mouse pointer is in the plot's data area
|
||||||
|
ChartRenderingInfo info = SimulationChart.this.getChartRenderingInfo();
|
||||||
|
PlotRenderingInfo pinfo = info.getPlotInfo();
|
||||||
|
Point2D p = SimulationChart.this.translateScreenToJava2D(e.getPoint());
|
||||||
|
if (!pinfo.getDataArea().contains(p)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plot plot = (Plot) zoomable;
|
||||||
|
// do not notify while zooming each axis
|
||||||
|
boolean notifyState = plot.isNotify();
|
||||||
|
plot.setNotify(false);
|
||||||
|
int clicks = e.getWheelRotation();
|
||||||
|
double zf = 1.0 + this.zoomFactor;
|
||||||
|
if (clicks < 0) {
|
||||||
|
zf = 1.0 / zf;
|
||||||
|
}
|
||||||
|
if (SimulationChart.this.isDomainZoomable()) {
|
||||||
|
zoomable.zoomDomainAxes(zf, pinfo, p, true);
|
||||||
|
}
|
||||||
|
if (SimulationChart.this.isRangeZoomable() && !domainOnly ) {
|
||||||
|
zoomable.zoomRangeAxes(zf, pinfo, p, true);
|
||||||
|
}
|
||||||
|
plot.setNotify(notifyState); // this generates the change event too
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,7 @@ import java.awt.Stroke;
|
|||||||
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.InputEvent;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
@ -37,6 +38,7 @@ 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.GUIUtil;
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
|
import net.sf.openrocket.gui.util.Icons;
|
||||||
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;
|
||||||
@ -137,7 +139,7 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
|
|
||||||
private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) {
|
private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) {
|
||||||
//// Flight data plot
|
//// Flight data plot
|
||||||
super(parent, trans.get("PlotDialog.title.Flightdataplot"));
|
super(parent, simulation.getName());
|
||||||
this.setModalityType(ModalityType.DOCUMENT_MODAL);
|
this.setModalityType(ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
final boolean initialShowPoints = Application.getPreferences().getBoolean(Preferences.PLOT_SHOW_POINTS, false);
|
final boolean initialShowPoints = Application.getPreferences().getBoolean(Preferences.PLOT_SHOW_POINTS, false);
|
||||||
@ -200,7 +202,7 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
// Create the chart using the factory to get all default settings
|
// Create the chart using the factory to get all default settings
|
||||||
JFreeChart chart = ChartFactory.createXYLineChart(
|
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||||
//// Simulated flight
|
//// Simulated flight
|
||||||
trans.get("PlotDialog.Chart.Simulatedflight"),
|
simulation.getName(),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -214,6 +216,9 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
|
|
||||||
// Add the data and formatting to the plot
|
// Add the data and formatting to the plot
|
||||||
XYPlot plot = chart.getXYPlot();
|
XYPlot plot = chart.getXYPlot();
|
||||||
|
plot.setDomainPannable(true);
|
||||||
|
plot.setRangePannable(true);
|
||||||
|
|
||||||
int axisno = 0;
|
int axisno = 0;
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
// Check whether axis has any data
|
// Check whether axis has any data
|
||||||
@ -408,7 +413,7 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||||
this.add(panel);
|
this.add(panel);
|
||||||
|
|
||||||
ChartPanel chartPanel = new ChartPanel(chart,
|
final ChartPanel chartPanel = new SimulationChart(chart,
|
||||||
false, // properties
|
false, // properties
|
||||||
true, // save
|
true, // save
|
||||||
false, // print
|
false, // print
|
||||||
@ -422,6 +427,10 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
|
|
||||||
panel.add(chartPanel, "grow, wrap 20lp");
|
panel.add(chartPanel, "grow, wrap 20lp");
|
||||||
|
|
||||||
|
//// Description text
|
||||||
|
JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2);
|
||||||
|
panel.add(label, "gapleft para");
|
||||||
|
|
||||||
//// Show data points
|
//// Show data points
|
||||||
final JCheckBox check = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints"));
|
final JCheckBox check = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints"));
|
||||||
check.setSelected(initialShowPoints);
|
check.setSelected(initialShowPoints);
|
||||||
@ -437,15 +446,42 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
});
|
});
|
||||||
panel.add(check, "split, left");
|
panel.add(check, "split, left");
|
||||||
|
|
||||||
|
//// Zoom out button
|
||||||
|
JButton button = new JButton(Icons.ZOOM_OUT);
|
||||||
|
button.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if ( (e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK ) {
|
||||||
|
chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_DOMAIN_COMMAND));
|
||||||
|
} else {
|
||||||
|
chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_BOTH_COMMAND));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
panel.add(button, "gapleft rel");
|
||||||
|
|
||||||
JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2);
|
//// Zoom in button
|
||||||
panel.add(label, "gapleft para");
|
button = new JButton(Icons.ZOOM_IN);
|
||||||
|
button.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if ( (e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK ) {
|
||||||
|
chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_DOMAIN_COMMAND));
|
||||||
|
} else {
|
||||||
|
chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_BOTH_COMMAND));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
panel.add(button, "gapleft rel");
|
||||||
|
|
||||||
|
//// Add series selection box
|
||||||
|
//// FIXME
|
||||||
|
|
||||||
panel.add(new JPanel(), "growx");
|
panel.add(new JPanel(), "growx");
|
||||||
|
|
||||||
//// Close button
|
//// Close button
|
||||||
JButton button = new JButton(trans.get("dlg.but.close"));
|
button = new JButton(trans.get("dlg.but.close"));
|
||||||
button.addActionListener(new ActionListener() {
|
button.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -519,13 +555,13 @@ public class SimulationPlotDialog extends JDialog {
|
|||||||
private static class ModifiedXYItemRenderer extends XYLineAndShapeRenderer {
|
private static class ModifiedXYItemRenderer extends XYLineAndShapeRenderer {
|
||||||
|
|
||||||
private final int branchCount;
|
private final int branchCount;
|
||||||
|
|
||||||
private ModifiedXYItemRenderer( int branchCount ) {
|
private ModifiedXYItemRenderer( int branchCount ) {
|
||||||
this.branchCount = branchCount;
|
this.branchCount = branchCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Paint lookupSeriesPaint(int series) {
|
public Paint lookupSeriesPaint(int series) {
|
||||||
return super.lookupSeriesPaint(series/branchCount);
|
return super.lookupSeriesPaint(series/branchCount);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user