View Javadoc
1   package org.opentrafficsim.draw.graphs;
2   
3   import org.djunits.value.vdouble.scalar.Duration;
4   import org.djutils.exceptions.Throw;
5   import org.jfree.chart.JFreeChart;
6   import org.jfree.chart.axis.ValueAxis;
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   * Plot that allows hard bounds to be set, with upper and lower bound independent. Manual zooming and auto ranges are bounded
14   * within the bounds.
15   * <p>
16   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
18   * <p>
19   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 okt. 2018 <br>
20   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
21   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
22   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
23   */
24  public abstract class AbstractBoundedPlot extends AbstractPlot
25  {
26  
27      /** */
28      private static final long serialVersionUID = 20182021L;
29  
30      /** Lower bound of domain axis. */
31      private Double lowerDomainBound = null;
32  
33      /** Upper bound of domain axis. */
34      private Double upperDomainBound = null;
35  
36      /** Lower bound of range axis. */
37      private Double lowerRangeBound = null;
38  
39      /** Upper bound of range axis. */
40      private Double upperRangeBound = null;
41  
42      /**
43       * Constructor.
44       * @param caption String; caption
45       * @param updateInterval Duration; regular update interval (simulation time)
46       * @param simulator OTSSimulatorInterface; simulator
47       * @param delay Duration; delay so critical future events have occurred, e.g. GTU's next move's to extend trajectories
48       */
49      public AbstractBoundedPlot(final String caption, final Duration updateInterval, final OTSSimulatorInterface simulator,
50              final Duration delay)
51      {
52          super(caption, updateInterval, simulator, delay);
53      }
54  
55      /** {@inheritDoc} */
56      @Override
57      protected void setChart(final JFreeChart chart)
58      {
59          Throw.when(!(chart.getPlot() instanceof XYPlot), IllegalArgumentException.class,
60                  "AbstractBoundedPlot can only work with XYPlot.");
61  
62          super.setChart(chart);
63  
64          XYPlot xyPlot = chart.getXYPlot();
65          xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
66          {
67              /** Whether to listen, this prevents a stack overflow. */
68              private boolean listen = true;
69  
70              /** {@inheritDoc} */
71              @SuppressWarnings("synthetic-access")
72              @Override
73              public void axisChanged(final AxisChangeEvent event)
74              {
75                  if (!this.listen)
76                  {
77                      return;
78                  }
79                  this.listen = false;
80                  constrainAxis(xyPlot.getDomainAxis(), AbstractBoundedPlot.this.lowerDomainBound,
81                          AbstractBoundedPlot.this.upperDomainBound);
82                  this.listen = true;
83              }
84          });
85          xyPlot.getRangeAxis().addChangeListener(new AxisChangeListener()
86          {
87              /** Whether to listen, this prevents a stack overflow. */
88              private boolean listen = true;
89  
90              /** {@inheritDoc} */
91              @SuppressWarnings("synthetic-access")
92              @Override
93              public void axisChanged(final AxisChangeEvent event)
94              {
95                  if (!this.listen)
96                  {
97                      return;
98                  }
99                  this.listen = false;
100                 constrainAxis(xyPlot.getRangeAxis(), AbstractBoundedPlot.this.lowerRangeBound,
101                         AbstractBoundedPlot.this.upperRangeBound);
102                 this.listen = true;
103             }
104         });
105     }
106 
107     /**
108      * Sets the lower domain bound.
109      * @param bound Double; use {@code null} to disable bound
110      */
111     public void setLowerDomainBound(final Double bound)
112     {
113         this.lowerDomainBound = bound;
114         constrainAxis(getChart().getXYPlot().getDomainAxis(), this.lowerDomainBound, this.upperDomainBound);
115     }
116 
117     /**
118      * Sets the upper domain bound.
119      * @param bound Double; use {@code null} to disable bound
120      */
121     public void setUpperDomainBound(final Double bound)
122     {
123         this.upperDomainBound = bound;
124         constrainAxis(getChart().getXYPlot().getDomainAxis(), this.lowerDomainBound, this.upperDomainBound);
125     }
126 
127     /**
128      * Sets the lower range bound.
129      * @param bound Double; use {@code null} to disable bound
130      */
131     public void setLowerRangeBound(final Double bound)
132     {
133         this.lowerRangeBound = bound;
134         constrainAxis(getChart().getXYPlot().getRangeAxis(), this.lowerRangeBound, this.upperRangeBound);
135     }
136 
137     /**
138      * Sets the upper range bound.
139      * @param bound Double; use {@code null} to disable bound
140      */
141     public void setUpperRangeBound(final Double bound)
142     {
143         this.upperRangeBound = bound;
144         constrainAxis(getChart().getXYPlot().getRangeAxis(), this.lowerRangeBound, this.upperRangeBound);
145     }
146 
147     /**
148      * Constrains axis.
149      * @param axis ValueAxis; axis
150      * @param min Double; minimum value, use {@code null} to apply no bound
151      * @param max Double; maximum value, use {@code null} to apply no bound
152      */
153     private void constrainAxis(final ValueAxis axis, final Double min, final Double max)
154     {
155         double xLow = axis.getLowerBound();
156         double xUpp = axis.getUpperBound();
157         if (min != null && max != null && xUpp - xLow > max - min)
158         {
159             axis.setLowerBound(min);
160             axis.setUpperBound(max);
161         }
162         else if (min != null && xLow < min)
163         {
164             axis.setLowerBound(min);
165             axis.setUpperBound(xUpp + (min - xLow));
166         }
167         else if (max != null && xUpp > max)
168         {
169             axis.setLowerBound(xLow - (xUpp - max));
170             axis.setUpperBound(max);
171         }
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     protected void setAutoBoundDomain(final XYPlot plot)
177     {
178         if (this.lowerDomainBound != null)
179         {
180             plot.getDomainAxis().setLowerBound(this.lowerDomainBound);
181         }
182         if (this.upperDomainBound != null)
183         {
184             plot.getDomainAxis().setUpperBound(this.upperDomainBound);
185         }
186     }
187 
188     /** {@inheritDoc} */
189     @Override
190     protected void setAutoBoundRange(final XYPlot plot)
191     {
192         if (this.lowerRangeBound != null)
193         {
194             plot.getRangeAxis().setLowerBound(this.lowerRangeBound);
195         }
196         if (this.upperRangeBound != null)
197         {
198             plot.getRangeAxis().setUpperBound(this.upperRangeBound);
199         }
200     }
201 
202 }