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