View Javadoc
1   package org.opentrafficsim.base.geometry;
2   
3   import org.djunits.value.vdouble.scalar.Duration;
4   import org.djutils.draw.Directed;
5   import org.djutils.draw.Transform2d;
6   import org.djutils.draw.bounds.Bounds2d;
7   import org.djutils.draw.line.PolyLine2d;
8   import org.djutils.draw.line.Polygon2d;
9   import org.djutils.draw.point.DirectedPoint2d;
10  import org.djutils.draw.point.Point2d;
11  
12  import nl.tudelft.simulation.dsol.animation.Locatable;
13  
14  /**
15   * Locatable that provides absolute and relative contours and bounds, both statically and dynamically.
16   * <p>
17   * Copyright (c) 2024-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
19   * </p>
20   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
21   */
22  public interface OtsShape extends Locatable
23  {
24  
25      /** Minimum world margin to click on object line or point objects. */
26      double WORLD_MARGIN_LINE = 1.0;
27  
28      /** The default intended number of polygon segments to represent continuous shapes. */
29      int DEFAULT_POLYGON_SEGMENTS = 128;
30  
31      @Override
32      DirectedPoint2d getLocation();
33  
34      /**
35       * Returns the contour of the object in world coordinates.
36       * @return the contour of the object in world coordinates
37       */
38      default Polygon2d getAbsoluteContour()
39      {
40          Transform2d transform = toAbsoluteTransform(getLocation());
41          return new Polygon2d(transform.transform(getRelativeContour().iterator()));
42      }
43  
44      /**
45       * Return the contour of a dynamic object at time 'time' in world coordinates. The default implementation returns the static
46       * contour.
47       * @param time simulation time for which we want the shape
48       * @return the shape of the object at time 'time'
49       */
50      default Polygon2d getAbsoluteContour(final Duration time)
51      {
52          return getAbsoluteContour();
53      }
54  
55      /**
56       * Returns the contour of the object in relative coordinates.
57       * @return the contour of the object in relative coordinates
58       */
59      Polygon2d getRelativeContour();
60  
61      /**
62       * Returns the bounds relative to the location. The default implementation returns the bounds of the contour.
63       * @return bounds relative to the location.
64       */
65      @Override
66      default Bounds2d getRelativeBounds()
67      {
68          return getRelativeContour().getAbsoluteBounds();
69      }
70  
71      /**
72       * Returns whether the point is contained within the shape. The default implementation calculates this based on the contour.
73       * @param point point
74       * @return whether the point is contained within the shape
75       */
76      default boolean contains(final Point2d point)
77      {
78          return signedDistance(point) < 0.0;
79      }
80  
81      /**
82       * Returns whether the point is contained within the shape. The default implementation calculates this based on the contour.
83       * @param x x coordinate of the point
84       * @param y y coordinate of the point
85       * @return whether the point is contained within the shape
86       */
87      default boolean contains(final double x, final double y)
88      {
89          return contains(new Point2d(x, y));
90      }
91  
92      /**
93       * Returns the bounds in world coordinates.
94       * @return bounds in world coordinates.
95       */
96      default Bounds2d getAbsoluteBounds()
97      {
98          return getAbsoluteContour().getAbsoluteBounds();
99      }
100 
101     @Override
102     default double getDirZ()
103     {
104         return getLocation().dirZ;
105     }
106 
107     /**
108      * Signed distance function. The point must be relative. Negative distances returned are inside the bounds, with the
109      * absolute value of the distance towards the edge. The default implementation is based on the polygon representation, which
110      * is expensive.
111      * @param point point for which distance is returned.
112      * @return distance from point to these bounds.
113      */
114     default double signedDistance(final Point2d point)
115     {
116         double dist = getRelativeContour().closestPointOnPolyLine(point).distance(point);
117         return getRelativeContour().contains(point) ? -dist : dist;
118     }
119 
120     /**
121      * Signed distance function. The point must be relative. Negative distances returned are inside the bounds, with the
122      * absolute value of the distance towards the edge. The default implementation is based on the polygon representation, which
123      * is expensive.
124      * @param x x coordinate of the point
125      * @param y y coordinate of the point
126      * @return distance from point to these bounds.
127      */
128     default double signedDistance(final double x, final double y)
129     {
130         return signedDistance(new Point2d(x, y));
131     }
132 
133     /*
134      * The following methods that take an OtsLocatable as input are defined not as default but as static, as these methods
135      * should not be exposed as functions of an OtsLocatable.
136      */
137 
138     /**
139      * Generates a polygon as contour based on bounds and location of the locatable.
140      * @param locatable locatable
141      * @return contour
142      */
143     static Polygon2d boundsAsAbsoluteContour(final OtsShape locatable)
144     {
145         return new Polygon2d(toAbsoluteTransform(locatable.getLocation()).transform(locatable.getRelativeBounds().iterator()));
146     }
147 
148     /**
149      * Transform the line by location, which may also be an {@code OrientedPoint} for rotation.
150      * @param line line
151      * @param location location, which may also be an {@code OrientedPoint} for rotation
152      * @return transformed line
153      */
154     static PolyLine2d transformLine(final PolyLine2d line, final Point2d location)
155     {
156         return new PolyLine2d(toRelativeTransform(location).transform(line.iterator()));
157     }
158 
159     /**
160      * Returns a transformation by which absolute coordinates can be translated and rotated to the frame of the possibly
161      * oriented location around which bounds are defined.
162      * @param location location (can be an {@code Oriented}).
163      * @return transformation.
164      */
165     static Transform2d toRelativeTransform(final Point2d location)
166     {
167         Transform2d transformation = new Transform2d();
168         if (location instanceof Directed dir)
169         {
170             transformation.rotation(-dir.getDirZ());
171         }
172         transformation.translate(-location.getX(), -location.getY());
173         return transformation;
174     }
175 
176     /**
177      * Returns a transformation by which relative coordinates can be translated and rotated to the frame of the possibly
178      * oriented location around which bounds are defined.
179      * @param location location (can be an {@code Oriented}).
180      * @return transformation.
181      */
182     static Transform2d toAbsoluteTransform(final Point2d location)
183     {
184         Transform2d transformation = new Transform2d();
185         transformation.translate(location.getX(), location.getY());
186         if (location instanceof Directed dir)
187         {
188             transformation.rotation(dir.getDirZ());
189         }
190         return transformation;
191     }
192 
193 }