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-2020 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      /** Lower bound of domain axis. */
28      private Double lowerDomainBound = null;
29  
30      /** Upper bound of domain axis. */
31      private Double upperDomainBound = null;
32  
33      /** Lower bound of range axis. */
34      private Double lowerRangeBound = null;
35  
36      /** Upper bound of range axis. */
37      private Double upperRangeBound = null;
38  
39      /**
40       * Constructor.
41       * @param simulator OTSSimulatorInterface; simulator
42       * @param caption String; caption
43       * @param updateInterval Duration; regular update interval (simulation time)
44       * @param delay Duration; amount of time that chart runs behind simulation to prevent gaps in the charted data
45       */
46      public AbstractBoundedPlot(final OTSSimulatorInterface simulator, final String caption, final Duration updateInterval,
47              final Duration delay)
48      {
49          super(simulator, caption, updateInterval, delay);
50      }
51  
52      /** {@inheritDoc} */
53      @Override
54      protected void setChart(final JFreeChart chart)
55      {
56          Throw.when(!(chart.getPlot() instanceof XYPlot), IllegalArgumentException.class,
57                  "AbstractBoundedPlot can only work with XYPlot.");
58  
59          super.setChart(chart);
60  
61          XYPlot xyPlot = chart.getXYPlot();
62          xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
63          {
64              /** Whether to listen, this prevents a stack overflow. */
65              private boolean listen = true;
66  
67              /** {@inheritDoc} */
68              @SuppressWarnings("synthetic-access")
69              @Override
70              public void axisChanged(final AxisChangeEvent event)
71              {
72                  if (!this.listen)
73                  {
74                      return;
75                  }
76                  this.listen = false;
77                  constrainAxis(xyPlot.getDomainAxis(), AbstractBoundedPlot.this.lowerDomainBound,
78                          AbstractBoundedPlot.this.upperDomainBound);
79                  this.listen = true;
80              }
81          });
82          xyPlot.getRangeAxis().addChangeListener(new AxisChangeListener()
83          {
84              /** Whether to listen, this prevents a stack overflow. */
85              private boolean listen = true;
86  
87              /** {@inheritDoc} */
88              @SuppressWarnings("synthetic-access")
89              @Override
90              public void axisChanged(final AxisChangeEvent event)
91              {
92                  if (!this.listen)
93                  {
94                      return;
95                  }
96                  this.listen = false;
97                  constrainAxis(xyPlot.getRangeAxis(), AbstractBoundedPlot.this.lowerRangeBound,
98                          AbstractBoundedPlot.this.upperRangeBound);
99                  this.listen = true;
100             }
101         });
102     }
103 
104     /**
105      * Sets the lower domain bound.
106      * @param bound Double; use {@code null} to disable bound
107      */
108     public void setLowerDomainBound(final Double bound)
109     {
110         this.lowerDomainBound = bound;
111         constrainAxis(getChart().getXYPlot().getDomainAxis(), this.lowerDomainBound, this.upperDomainBound);
112     }
113 
114     /**
115      * Sets the upper domain bound.
116      * @param bound Double; use {@code null} to disable bound
117      */
118     public void setUpperDomainBound(final Double bound)
119     {
120         this.upperDomainBound = bound;
121         constrainAxis(getChart().getXYPlot().getDomainAxis(), this.lowerDomainBound, this.upperDomainBound);
122     }
123 
124     /**
125      * Sets the lower range bound.
126      * @param bound Double; use {@code null} to disable bound
127      */
128     public void setLowerRangeBound(final Double bound)
129     {
130         this.lowerRangeBound = bound;
131         constrainAxis(getChart().getXYPlot().getRangeAxis(), this.lowerRangeBound, this.upperRangeBound);
132     }
133 
134     /**
135      * Sets the upper range bound.
136      * @param bound Double; use {@code null} to disable bound
137      */
138     public void setUpperRangeBound(final Double bound)
139     {
140         this.upperRangeBound = bound;
141         constrainAxis(getChart().getXYPlot().getRangeAxis(), this.lowerRangeBound, this.upperRangeBound);
142     }
143 
144     /**
145      * Constrains axis.
146      * @param axis ValueAxis; axis
147      * @param min Double; minimum value, use {@code null} to apply no bound
148      * @param max Double; maximum value, use {@code null} to apply no bound
149      */
150     private void constrainAxis(final ValueAxis axis, final Double min, final Double max)
151     {
152         double xLow = axis.getLowerBound();
153         double xUpp = axis.getUpperBound();
154         if (min != null && max != null && xUpp - xLow > max - min)
155         {
156             axis.setLowerBound(min);
157             axis.setUpperBound(max);
158         }
159         else if (min != null && xLow < min)
160         {
161             axis.setLowerBound(min);
162             axis.setUpperBound(xUpp + (min - xLow));
163         }
164         else if (max != null && xUpp > max)
165         {
166             axis.setLowerBound(xLow - (xUpp - max));
167             axis.setUpperBound(max);
168         }
169     }
170 
171     /** {@inheritDoc} */
172     @Override
173     public void setAutoBoundDomain(final XYPlot plot)
174     {
175         if (this.lowerDomainBound != null)
176         {
177             plot.getDomainAxis().setLowerBound(this.lowerDomainBound);
178         }
179         if (this.upperDomainBound != null)
180         {
181             plot.getDomainAxis().setUpperBound(this.upperDomainBound);
182         }
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public void setAutoBoundRange(final XYPlot plot)
188     {
189         if (this.lowerRangeBound != null)
190         {
191             plot.getRangeAxis().setLowerBound(this.lowerRangeBound);
192         }
193         if (this.upperRangeBound != null)
194         {
195             plot.getRangeAxis().setUpperBound(this.upperRangeBound);
196         }
197     }
198 
199 }