View Javadoc
1   package org.opentrafficsim.draw.graphs;
2   
3   import java.awt.event.ActionEvent;
4   import java.awt.event.ActionListener;
5   
6   import javax.swing.JCheckBoxMenuItem;
7   import javax.swing.JPopupMenu;
8   
9   import org.djunits.value.vdouble.scalar.Duration;
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.djunits.value.vdouble.scalar.Time;
12  import org.jfree.chart.JFreeChart;
13  import org.jfree.chart.event.AxisChangeEvent;
14  import org.jfree.chart.event.AxisChangeListener;
15  import org.jfree.chart.plot.XYPlot;
16  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
17  
18  /**
19   * Plots with space-time. This class adds some zoom control, where a user can manually select a zoom range, or the plot
20   * automatically zooms over the entire space range, and either the entire or some most recent fixed period in time.
21   * <p>
22   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
24   * <p>
25   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 14 okt. 2018 <br>
26   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
28   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
29   */
30  public abstract class AbstractSpaceTimePlot extends AbstractBoundedPlot
31  {
32  
33      /** */
34      private static final long serialVersionUID = 20181014L;
35  
36      /** Initial end time of plot. */
37      private final Time initialEnd;
38  
39      /** Whether to update the axes. */
40      private boolean autoBoundAxes = true;
41  
42      /** Whether to disable auto bounds on the axes on any change on the axes. */
43      private boolean virtualAutoBounds = false;
44  
45      /** Fixed domain range. */
46      private Double fixedDomainRange = null;
47  
48      /**
49       * Constructor.
50       * @param caption String; caption
51       * @param updateInterval Duration; regular update interval (simulation time)
52       * @param simulator OTSSimulatorInterface; simulator
53       * @param delay Duration; delay so critical future events have occurred, e.g. GTU's next move's to extend trajectories
54       * @param initialEnd Time; initial end time of plots, will be expanded if simulation time exceeds it
55       */
56      public AbstractSpaceTimePlot(final String caption, final Duration updateInterval, final OTSSimulatorInterface simulator,
57              final Duration delay, final Time initialEnd)
58      {
59          super(caption, updateInterval, simulator, delay);
60          this.initialEnd = initialEnd;
61      }
62  
63      /** {@inheritDoc} */
64      @Override
65      protected void setChart(final JFreeChart chart)
66      {
67          super.setChart(chart);
68          XYPlot xyPlot = chart.getXYPlot();
69          setLowerRangeBound(0.0);
70          setUpperRangeBound(getEndLocation().si);
71          setLowerDomainBound(0.0);
72          setUpperDomainBound(this.initialEnd.si);
73          setAutoBounds(xyPlot);
74          // axis listeners to enable/disable auto zoom
75          xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
76          {
77              /** {@inheritDoc} */
78              @SuppressWarnings("synthetic-access")
79              @Override
80              public void axisChanged(final AxisChangeEvent event)
81              {
82                  if (!AbstractSpaceTimePlot.this.virtualAutoBounds)
83                  {
84                      // the axis was changed, but not by a command from this class, auto bounds should be disabled
85                      AbstractSpaceTimePlot.this.autoBoundAxes = false;
86                  }
87              }
88          });
89      }
90  
91      /** {@inheritDoc} */
92      @Override
93      protected void addPopUpMenuItems(final JPopupMenu popupMenu)
94      {
95          JCheckBoxMenuItem fixedDomainCheckBox = new JCheckBoxMenuItem("Fix time range", false);
96          fixedDomainCheckBox.addActionListener(new ActionListener()
97          {
98              /** {@inheritDoc} */
99              @SuppressWarnings("synthetic-access")
100             @Override
101             public void actionPerformed(final ActionEvent e)
102             {
103                 boolean fix = ((JCheckBoxMenuItem) e.getSource()).isSelected();
104                 AbstractSpaceTimePlot.this.fixedDomainRange =
105                         fix ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
106                 notifyPlotChange();
107             }
108         });
109         popupMenu.insert(fixedDomainCheckBox, 0);
110         popupMenu.insert(new JPopupMenu.Separator(), 1);
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     protected void update()
116     {
117         if (getUpdateTime() != null && this.initialEnd != null)
118         {
119             setUpperDomainBound(Math.max(getUpdateTime().si, this.initialEnd.si));
120         }
121         if (this.autoBoundAxes && getChart() != null) // null during construction
122         {
123             setAutoBounds(getChart().getXYPlot());
124         }
125         super.update();
126     }
127 
128     /**
129      * Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
130      * plot, and to update the plot when time is increased.
131      * @param plot XYPlot; plot with default zoom-all bounds set
132      */
133     private void setAutoBounds(final XYPlot plot)
134     {
135         // disables the axis change listener from registering a user input that is actually an update of bounds as the time
136         // increases
137         this.virtualAutoBounds = true;
138         if (this.fixedDomainRange != null && getUpdateTime().si > 0.0)
139         {
140             plot.getDomainAxis().setRange(Math.max(getUpdateTime().si - this.fixedDomainRange, 0.0), getUpdateTime().si);
141         }
142         else
143         {
144             super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
145         }
146         super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
147         this.virtualAutoBounds = false;
148     }
149 
150     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
151     @Override
152     protected final void setAutoBoundDomain(final XYPlot plot)
153     {
154         super.setAutoBoundDomain(plot);
155         this.autoBoundAxes = true;
156     }
157 
158     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
159     @Override
160     protected final void setAutoBoundRange(final XYPlot plot)
161     {
162         super.setAutoBoundRange(plot);
163         this.autoBoundAxes = true;
164     }
165 
166     /**
167      * Returns the total path length.
168      * @return Length; total path length
169      */
170     protected abstract Length getEndLocation();
171 
172 }