package org.opentrafficsim.draw.graphs; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JCheckBoxMenuItem; import javax.swing.JPopupMenu; import org.djunits.value.vdouble.scalar.Duration; import org.djunits.value.vdouble.scalar.Length; import org.djunits.value.vdouble.scalar.Time; import org.jfree.chart.JFreeChart; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.event.AxisChangeListener; import org.jfree.chart.plot.XYPlot; import org.opentrafficsim.core.dsol.OTSSimulatorInterface; /** * Plots with space-time. This class adds some zoom control, where a user can manually select a zoom range, or the plot * automatically zooms over the entire space range, and either the entire or some most recent fixed period in time. *
* Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 okt. 2018
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
*/
public abstract class AbstractSpaceTimePlot extends AbstractBoundedPlot
{
/** */
private static final long serialVersionUID = 20181014L;
/** Initial end time of plot. */
private final Time initialEnd;
/** Whether to update the axes. */
private boolean autoBoundAxes = true;
/** Whether to disable auto bounds on the axes on any change on the axes. */
private boolean virtualAutoBounds = false;
/** Fixed domain range. */
private Double fixedDomainRange = null;
/**
* Constructor.
* @param caption String; caption
* @param updateInterval Duration; regular update interval (simulation time)
* @param simulator OTSSimulatorInterface; simulator
* @param delay Duration; delay so critical future events have occurred, e.g. GTU's next move's to extend trajectories
* @param initialEnd Time; initial end time of plots, will be expanded if simulation time exceeds it
*/
public AbstractSpaceTimePlot(final String caption, final Duration updateInterval, final OTSSimulatorInterface simulator,
final Duration delay, final Time initialEnd)
{
super(caption, updateInterval, simulator, delay);
this.initialEnd = initialEnd;
}
/** {@inheritDoc} */
@Override
protected void setChart(final JFreeChart chart)
{
super.setChart(chart);
XYPlot xyPlot = chart.getXYPlot();
setLowerRangeBound(0.0);
setUpperRangeBound(getEndLocation().si);
setLowerDomainBound(0.0);
setUpperDomainBound(this.initialEnd.si);
setAutoBounds(xyPlot);
// axis listeners to enable/disable auto zoom
xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
{
/** {@inheritDoc} */
@SuppressWarnings("synthetic-access")
@Override
public void axisChanged(final AxisChangeEvent event)
{
if (!AbstractSpaceTimePlot.this.virtualAutoBounds)
{
// the axis was changed, but not by a command from this class, auto bounds should be disabled
AbstractSpaceTimePlot.this.autoBoundAxes = false;
}
}
});
}
/** {@inheritDoc} */
@Override
protected void addPopUpMenuItems(final JPopupMenu popupMenu)
{
JCheckBoxMenuItem fixedDomainCheckBox = new JCheckBoxMenuItem("Fix time range", false);
fixedDomainCheckBox.addActionListener(new ActionListener()
{
/** {@inheritDoc} */
@SuppressWarnings("synthetic-access")
@Override
public void actionPerformed(final ActionEvent e)
{
boolean fix = ((JCheckBoxMenuItem) e.getSource()).isSelected();
AbstractSpaceTimePlot.this.fixedDomainRange =
fix ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
notifyPlotChange();
}
});
popupMenu.insert(fixedDomainCheckBox, 0);
popupMenu.insert(new JPopupMenu.Separator(), 1);
}
/** {@inheritDoc} */
@Override
protected void update()
{
if (getUpdateTime() != null && this.initialEnd != null)
{
setUpperDomainBound(Math.max(getUpdateTime().si, this.initialEnd.si));
}
if (this.autoBoundAxes && getChart() != null) // null during construction
{
setAutoBounds(getChart().getXYPlot());
}
super.update();
}
/**
* Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
* plot, and to update the plot when time is increased.
* @param plot XYPlot; plot with default zoom-all bounds set
*/
private void setAutoBounds(final XYPlot plot)
{
// disables the axis change listener from registering a user input that is actually an update of bounds as the time
// increases
this.virtualAutoBounds = true;
if (this.fixedDomainRange != null && getUpdateTime().si > 0.0)
{
plot.getDomainAxis().setRange(Math.max(getUpdateTime().si - this.fixedDomainRange, 0.0), getUpdateTime().si);
}
else
{
super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
}
super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
this.virtualAutoBounds = false;
}
/** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
@Override
protected final void setAutoBoundDomain(final XYPlot plot)
{
super.setAutoBoundDomain(plot);
this.autoBoundAxes = true;
}
/** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
@Override
protected final void setAutoBoundRange(final XYPlot plot)
{
super.setAutoBoundRange(plot);
this.autoBoundAxes = true;
}
/**
* Returns the total path length.
* @return Length; total path length
*/
protected abstract Length getEndLocation();
}