View Javadoc
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://github.com/peter-knoppers">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 caption
40       * @param updateInterval regular update interval (simulation time)
41       * @param scheduler scheduler.
42       * @param delay amount of time that chart runs behind simulation to prevent gaps in the charted data
43       * @param initialEnd 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      @Override
53      protected void setChart(final JFreeChart chart)
54      {
55          super.setChart(chart);
56          XYPlot xyPlot = chart.getXYPlot();
57          setLowerRangeBound(0.0);
58          setUpperRangeBound(getEndLocation().si);
59          setLowerDomainBound(0.0);
60          setUpperDomainBound(this.initialEnd.si);
61          setAutoBounds(xyPlot);
62          // axis listeners to enable/disable auto zoom
63          xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
64          {
65              @SuppressWarnings("synthetic-access")
66              @Override
67              public void axisChanged(final AxisChangeEvent event)
68              {
69                  if (!AbstractSpaceTimePlot.this.virtualAutoBounds)
70                  {
71                      // the axis was changed, but not by a command from this class, auto bounds should be disabled
72                      AbstractSpaceTimePlot.this.autoBoundAxes = false;
73                  }
74              }
75          });
76      }
77  
78      @Override
79      protected void update()
80      {
81          if (getUpdateTime() != null && this.initialEnd != null)
82          {
83              setUpperDomainBound(Math.max(getUpdateTime().si, this.initialEnd.si));
84          }
85          if (this.autoBoundAxes && getChart() != null) // null during construction
86          {
87              setAutoBounds(getChart().getXYPlot());
88          }
89          super.update();
90      }
91  
92      /**
93       * Update the fixed-ness of the domain range.
94       * @param fixed if true; the domain range will not update when new data becomes available; if false; the domain range will
95       *            update to show newly available data
96       */
97      public void updateFixedDomainRange(final boolean fixed)
98      {
99          this.fixedDomainRange = fixed ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
100         notifyPlotChange();
101     }
102 
103     /**
104      * Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
105      * plot, and to update the plot when time is increased.
106      * @param plot plot with default zoom-all bounds set
107      */
108     private void setAutoBounds(final XYPlot plot)
109     {
110         // disables the axis change listener from registering a user input that is actually an update of bounds as the time
111         // increases
112         this.virtualAutoBounds = true;
113         if (this.fixedDomainRange != null && getUpdateTime().si > 0.0)
114         {
115             plot.getDomainAxis().setRange(Math.max(getUpdateTime().si - this.fixedDomainRange, 0.0), getUpdateTime().si);
116         }
117         else
118         {
119             super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
120         }
121         super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
122         this.virtualAutoBounds = false;
123     }
124 
125     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
126     @Override
127     public final void setAutoBoundDomain(final XYPlot plot)
128     {
129         super.setAutoBoundDomain(plot);
130         this.autoBoundAxes = true;
131     }
132 
133     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
134     @Override
135     public final void setAutoBoundRange(final XYPlot plot)
136     {
137         super.setAutoBoundRange(plot);
138         this.autoBoundAxes = true;
139     }
140 
141     /**
142      * Returns the total path length.
143      * @return total path length
144      */
145     protected abstract Length getEndLocation();
146 
147 }