View Javadoc
1   package org.opentrafficsim.draw;
2   
3   import org.djutils.draw.bounds.Bounds2d;
4   import org.opentrafficsim.base.geometry.OtsLocatable;
5   import org.opentrafficsim.base.geometry.OtsShape;
6   
7   /**
8    * This class returns bounds that respond to {@code contains(x, y)} by checking the actual shape, while also accounting for a
9    * minimum clickable expanse. For line objects use {@code ClickableLineLocatable}.
10   * <p>
11   * Copyright (c) 2024-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
12   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
13   * </p>
14   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
15   */
16  public interface ClickableLocatable extends OtsLocatable
17  {
18  
19      /** Minimum expanse to click on object. */
20      double EXPANSE = 2.0;
21  
22      @Override
23      default Bounds2d getBounds()
24      {
25          return getBounds(this);
26      }
27  
28      /**
29       * Returns bounds that comply to the actual shape.
30       * @param locatable locatable
31       * @return bounds that comply to the actual shape.
32       */
33      static Bounds2d getBounds(final ClickableLocatable locatable)
34      {
35          /*
36           * The reason this method is implemented as static, is such that sub-sub-classes can use this functionality, while an
37           * intermediate sub-class overrides this. In particular, ClickableLineLocatable returns bounds regarding a line.
38           * AnimationConflictData extends that via LaneBasedObjectData, but should be clickable over the entire region of the
39           * conflict as a ClickableLocatable.
40           */
41          OtsShape shape = locatable.getShape();
42          double deltaX = shape.getMaxX() - shape.getMinX();
43          double deltaY = shape.getMaxY() - shape.getMinY();
44          boolean xExpand = deltaX < EXPANSE;
45          boolean yExpand = deltaY < EXPANSE;
46          return new Bounds2d(xExpand ? EXPANSE : deltaX, yExpand ? EXPANSE : deltaY)
47          {
48              /** */
49              private static final long serialVersionUID = 20241006L;
50  
51              @Override
52              public boolean contains(final double x, final double y)
53              {
54                  if (xExpand || yExpand)
55                  {
56                      return getMinX() <= x && x <= getMaxX() && getMinY() <= y && y <= getMaxY();
57                  }
58                  return shape.contains(x, y);
59              }
60          };
61      }
62  
63  }