1 package org.opentrafficsim.draw.graphs;
2
3 import org.djunits.value.vdouble.scalar.Duration;
4 import org.djunits.value.vdouble.scalar.Length;
5 import org.djunits.value.vdouble.scalar.Time;
6 import org.jfree.chart.JFreeChart;
7 import org.jfree.chart.event.AxisChangeEvent;
8 import org.jfree.chart.event.AxisChangeListener;
9 import org.jfree.chart.plot.XYPlot;
10 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
11
12 /**
13 * Plots with space-time. This class adds some zoom control, where a user can manually select a zoom range, or the plot
14 * automatically zooms over the entire space range, and either the entire or some most recent fixed period in time.
15 * <p>
16 * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18 * </p>
19 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
20 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
21 * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
22 */
23 public abstract class AbstractSpaceTimePlot extends AbstractBoundedPlot
24 {
25
26 /** Initial end time of plot. */
27 private final Time initialEnd;
28
29 /** Whether to update the axes. */
30 private boolean autoBoundAxes = true;
31
32 /** Whether to disable auto bounds on the axes on any change on the axes. */
33 private boolean virtualAutoBounds = false;
34
35 /** Fixed domain range. */
36 private Double fixedDomainRange = null;
37
38 /**
39 * Constructor.
40 * @param caption String; caption
41 * @param updateInterval Duration; regular update interval (simulation time)
42 * @param simulator OtsSimulatorInterface; simulator
43 * @param delay Duration; amount of time that chart runs behind simulation to prevent gaps in the charted data
44 * @param initialEnd Time; initial end time of plots, will be expanded if simulation time exceeds it
45 */
46 public AbstractSpaceTimePlot(final String caption, final Duration updateInterval, final OtsSimulatorInterface simulator,
47 final Duration delay, final Time initialEnd)
48 {
49 super(simulator, caption, updateInterval, delay);
50 this.initialEnd = initialEnd;
51 }
52
53 /** {@inheritDoc} */
54 @Override
55 protected void setChart(final JFreeChart chart)
56 {
57 super.setChart(chart);
58 XYPlot xyPlot = chart.getXYPlot();
59 setLowerRangeBound(0.0);
60 setUpperRangeBound(getEndLocation().si);
61 setLowerDomainBound(0.0);
62 setUpperDomainBound(this.initialEnd.si);
63 setAutoBounds(xyPlot);
64 // axis listeners to enable/disable auto zoom
65 xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
66 {
67 /** {@inheritDoc} */
68 @SuppressWarnings("synthetic-access")
69 @Override
70 public void axisChanged(final AxisChangeEvent event)
71 {
72 if (!AbstractSpaceTimePlot.this.virtualAutoBounds)
73 {
74 // the axis was changed, but not by a command from this class, auto bounds should be disabled
75 AbstractSpaceTimePlot.this.autoBoundAxes = false;
76 }
77 }
78 });
79 }
80
81 /** {@inheritDoc} */
82 @Override
83 protected void update()
84 {
85 if (getUpdateTime() != null && this.initialEnd != null)
86 {
87 setUpperDomainBound(Math.max(getUpdateTime().si, this.initialEnd.si));
88 }
89 if (this.autoBoundAxes && getChart() != null) // null during construction
90 {
91 setAutoBounds(getChart().getXYPlot());
92 }
93 super.update();
94 }
95
96 /**
97 * Update the fixed-ness of the domain range.
98 * @param fixed boolean; if true; the domain range will not update when new data becomes available; if false; the domain
99 * range will update to show newly available data
100 */
101 public void updateFixedDomainRange(final boolean fixed)
102 {
103 this.fixedDomainRange = fixed ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
104 notifyPlotChange();
105 }
106
107 /**
108 * Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
109 * plot, and to update the plot when time is increased.
110 * @param plot XYPlot; plot with default zoom-all bounds set
111 */
112 private void setAutoBounds(final XYPlot plot)
113 {
114 // disables the axis change listener from registering a user input that is actually an update of bounds as the time
115 // increases
116 this.virtualAutoBounds = true;
117 if (this.fixedDomainRange != null && getUpdateTime().si > 0.0)
118 {
119 plot.getDomainAxis().setRange(Math.max(getUpdateTime().si - this.fixedDomainRange, 0.0), getUpdateTime().si);
120 }
121 else
122 {
123 super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
124 }
125 super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
126 this.virtualAutoBounds = false;
127 }
128
129 /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
130 @Override
131 public final void setAutoBoundDomain(final XYPlot plot)
132 {
133 super.setAutoBoundDomain(plot);
134 this.autoBoundAxes = true;
135 }
136
137 /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
138 @Override
139 public final void setAutoBoundRange(final XYPlot plot)
140 {
141 super.setAutoBoundRange(plot);
142 this.autoBoundAxes = true;
143 }
144
145 /**
146 * Returns the total path length.
147 * @return Length; total path length
148 */
149 protected abstract Length getEndLocation();
150
151 }