View Javadoc
1   package org.opentrafficsim.animation.graphs;
2   
3   import org.djunits.value.vdouble.scalar.Duration;
4   import org.djunits.value.vdouble.scalar.Length;
5   import org.jfree.chart.JFreeChart;
6   import org.jfree.chart.event.AxisChangeEvent;
7   import org.jfree.chart.event.AxisChangeListener;
8   import org.jfree.chart.plot.XYPlot;
9   import org.opentrafficsim.animation.graphs.AbstractPlot.PaintState;
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-2026 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 Alexander Verbraeck
19   * @author Peter Knoppers
20   * @author Wouter Schakel
21   * @param <S> paint state type
22   */
23  public abstract class AbstractSpaceTimePlot<S extends PaintState> extends AbstractBoundedPlot<S>
24  {
25  
26      /** Initial end time of plot. */
27      private final Duration 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 programmaticBoundChanges = false;
34  
35      /** Fixed domain range. */
36      private Double fixedDomainRange = null;
37  
38      /**
39       * Constructor.
40       * @param caption caption
41       * @param updateInterval regular update interval (simulation time)
42       * @param scheduler scheduler
43       * @param delay amount of time that chart runs behind simulation to prevent gaps in the charted data
44       * @param initialEnd initial end time of plots, will be expanded if simulation time exceeds it
45       */
46      public AbstractSpaceTimePlot(final String caption, final Duration updateInterval, final PlotScheduler scheduler,
47              final Duration delay, final Duration initialEnd)
48      {
49          super(scheduler, caption, updateInterval, delay);
50          this.initialEnd = initialEnd;
51      }
52  
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              @Override
67              public void axisChanged(final AxisChangeEvent event)
68              {
69                  if (!AbstractSpaceTimePlot.this.programmaticBoundChanges)
70                  {
71                      // axis was changed, but not by a command from this class (i.e. user zoomed), auto bounds should be disabled
72                      AbstractSpaceTimePlot.this.autoBoundAxes = false;
73                  }
74              }
75          });
76      }
77  
78      @Override
79      protected void setPaintState()
80      {
81          super.setPaintState();
82          this.programmaticBoundChanges = true;
83          setUpperDomainBound(Math.max(getAvailableTime().si, this.initialEnd.si));
84          this.programmaticBoundChanges = false;
85          if (this.autoBoundAxes && getChart() != null) // null during construction
86          {
87              setAutoBounds(getChart().getXYPlot());
88          }
89      }
90  
91      /**
92       * Update the fixed-ness of the domain range.
93       * @param fixed if true; the domain range will not update when new data becomes available; if false; the domain range will
94       *            update to show newly available data
95       */
96      public void updateFixedDomainRange(final boolean fixed)
97      {
98          this.fixedDomainRange = fixed ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
99          notifyPlotChange();
100     }
101 
102     /**
103      * Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
104      * plot, and to update the plot when time is increased.
105      * @param plot plot with default zoom-all bounds set
106      */
107     private void setAutoBounds(final XYPlot plot)
108     {
109         // disables the axis change listener from registering user input that is actually an update of bounds as time increases
110         this.programmaticBoundChanges = true;
111         if (this.fixedDomainRange != null && getAvailableTime().si > 0.0)
112         {
113             setLowerDomainBound(Math.max(getAvailableTime().si - this.fixedDomainRange, 0.0));
114             setUpperDomainBound(Math.max(getAvailableTime().si, this.initialEnd.si));
115         }
116         else
117         {
118             super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
119         }
120         super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
121         this.programmaticBoundChanges = false;
122     }
123 
124     /** {@inheritDoc} This implementation overrides to reset user-disabled auto-bounds. */
125     @Override
126     public final void setAutoBoundDomain(final XYPlot plot)
127     {
128         super.setAutoBoundDomain(plot);
129         this.autoBoundAxes = true;
130     }
131 
132     /** {@inheritDoc} This implementation overrides to reset user-disabled auto-bounds. */
133     @Override
134     public final void setAutoBoundRange(final XYPlot plot)
135     {
136         super.setAutoBoundRange(plot);
137         this.autoBoundAxes = true;
138     }
139 
140     /**
141      * Returns the total space.
142      * @return total space
143      */
144     protected abstract Length getEndLocation();
145 
146 }