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 }