View Javadoc
1   package org.djutils.draw.line;
2   
3   import java.awt.geom.Point2D;
4   import java.util.Arrays;
5   import java.util.Iterator;
6   import java.util.Locale;
7   
8   import org.djutils.base.AngleUtil;
9   import org.djutils.draw.DrawRuntimeException;
10  import org.djutils.draw.Drawable2d;
11  import org.djutils.draw.bounds.Bounds2d;
12  import org.djutils.draw.point.DirectedPoint2d;
13  import org.djutils.draw.point.Point2d;
14  import org.djutils.exceptions.Throw;
15  
16  /**
17   * Ray2d is a half-line; it has one end point with finite coordinates; the other end point is infinitely far away.
18   * <p>
19   * Copyright (c) 2020-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
21   * </p>
22   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
24   */
25  public class Ray2d extends DirectedPoint2d implements Drawable2d, Ray<Ray2d, DirectedPoint2d, Point2d>
26  {
27      /** */
28      private static final long serialVersionUID = 20210119L;
29  
30      /**
31       * Construct a new Ray2d.
32       * @param x double; the x coordinate of the finite end point of the ray
33       * @param y double; the y coordinate of the finite end point of the ray
34       * @param dirZ double; the angle from the positive X axis direction in radians.
35       * @throws DrawRuntimeException when dirZ is NaN
36       */
37      public Ray2d(final double x, final double y, final double dirZ) throws DrawRuntimeException
38      {
39          super(x, y, dirZ);
40      }
41  
42      /**
43       * Construct a new Ray2d from x and y coordinates in a double[] and a direction.
44       * @param xy double[]; the <cite>x</cite> and <cite>y</cite> coordinates of the finite end point in that order
45       * @param dirZ double; the counter-clockwise rotation around the point in radians
46       * @throws NullPointerException when xy is null
47       * @throws IllegalArgumentException when the dimension of xy is not 2 or any value in xy is NaN or rotZ is NaN
48       */
49      public Ray2d(final double[] xy, final double dirZ) throws IllegalArgumentException
50      {
51          super(xy, dirZ);
52      }
53  
54      /**
55       * Construct a new Ray2d from an AWT Point2D and a direction.
56       * @param point Point2D; an AWT Point2D
57       * @param dirZ double; the counter-clockwise rotation around the point in radians
58       * @throws IllegalArgumentException when any coordinate in point is NaN, or rotZ is NaN
59       */
60      public Ray2d(final Point2D point, final double dirZ) throws IllegalArgumentException
61      {
62          super(point, dirZ);
63      }
64  
65      /**
66       * Construct a new Ray2d from a Point2d and a direction.
67       * @param point Point2d; the finite end point of the ray
68       * @param dirZ double; the angle from the positive X axis direction in radians.
69       * @throws NullPointerException when point is null
70       * @throws DrawRuntimeException when dirZ is NaN
71       */
72      public Ray2d(final Point2d point, final double dirZ) throws NullPointerException, DrawRuntimeException
73      {
74          super(point, dirZ);
75      }
76  
77      /**
78       * Construct a new Ray2d.
79       * @param x double; the x coordinate of the finite end point of the ray
80       * @param y double; the y coordinate of the finite end point of the ray
81       * @param throughX double; the x coordinate of another point on the ray
82       * @param throughY double; the y coordinate of another point on the ray
83       * @throws DrawRuntimeException when throughX == x and throughY == y, or any through-value is NaN
84       */
85      public Ray2d(final double x, final double y, final double throughX, final double throughY) throws DrawRuntimeException
86      {
87          super(x, y, throughX, throughY);
88      }
89  
90      /**
91       * Construct a new Ray2d.
92       * @param point Point2d; the finite end point of the ray
93       * @param throughX double; the x coordinate of another point on the ray
94       * @param throughY double; the y coordinate of another point on the ray
95       * @throws NullPointerException when point is null
96       * @throws DrawRuntimeException when throughX == point.x and throughY == point.y, or any through-value is NaN
97       */
98      public Ray2d(final Point2d point, final double throughX, final double throughY)
99              throws NullPointerException, DrawRuntimeException
100     {
101         super(point, throughX, throughY);
102     }
103 
104     /**
105      * Construct a new Ray2d.
106      * @param x double; the x coordinate of the finite end point of the ray
107      * @param y double; the y coordinate of the finite end point of the ray
108      * @param throughPoint Point2d; another point on the ray
109      * @throws NullPointerException when throughPoint is null
110      * @throws DrawRuntimeException when throughPoint is exactly at (x, y)
111      */
112     public Ray2d(final double x, final double y, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
113     {
114         this(x, y, Throw.whenNull(throughPoint, "througPoint").x, throughPoint.y);
115     }
116 
117     /**
118      * Construct a new Ray2d.
119      * @param point Point2d; the finite end point of the ray
120      * @param throughPoint Point2d; another point on the ray
121      * @throws NullPointerException when point is null or throughPoint is null
122      * @throws DrawRuntimeException when throughPoint is exactly at point
123      */
124     public Ray2d(final Point2d point, final Point2d throughPoint) throws NullPointerException, DrawRuntimeException
125     {
126         this(Throw.whenNull(point, "point").x, point.y, Throw.whenNull(throughPoint, "throughPoint").x, throughPoint.y);
127     }
128     
129     /**
130      * Construct a new Ray2d.
131      * @param directedPoint DirectedPoint2d; point and direction of the new Ray2d
132      */
133     public Ray2d(final DirectedPoint2d directedPoint)
134     {
135         this(directedPoint, directedPoint.dirZ);
136     }
137 
138     @Override
139     public final double getDirZ()
140     {
141         return this.dirZ;
142     }
143 
144     @Override
145     public DirectedPoint2d getEndPoint()
146     {
147         return this;
148     }
149 
150     @Override
151     public int size()
152     {
153         return 2;
154     }
155 
156     @Override
157     public Iterator<DirectedPoint2d> getPoints()
158     {
159         double cosDirZ = Math.cos(this.dirZ);
160         double sinDirZ = Math.sin(this.dirZ);
161         DirectedPoint2d[] array =
162                 new DirectedPoint2d[] {this, new DirectedPoint2d(cosDirZ == 0 ? this.x : cosDirZ * Double.POSITIVE_INFINITY,
163                         sinDirZ == 0 ? this.y : sinDirZ * Double.POSITIVE_INFINITY, this.dirZ)};
164         return Arrays.stream(array).iterator();
165     }
166 
167     @Override
168     public Bounds2d getBounds()
169     {
170         double cosDirZ = Math.cos(this.dirZ);
171         double sinDirZ = Math.sin(this.dirZ);
172         return new Bounds2d(cosDirZ >= 0 ? this.x : Double.NEGATIVE_INFINITY, cosDirZ <= 0 ? this.x : Double.POSITIVE_INFINITY,
173                 sinDirZ >= 0 ? this.y : Double.NEGATIVE_INFINITY, sinDirZ <= 0 ? this.y : Double.POSITIVE_INFINITY);
174     }
175 
176     @Override
177     public Ray2d neg()
178     {
179         return new Ray2d(-this.x, -this.y, AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
180     }
181 
182     @Override
183     public Ray2d flip()
184     {
185         return new Ray2d(this.x, this.y, AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
186     }
187 
188     @Override
189     public Ray2d getLocationExtended(final double position) throws DrawRuntimeException
190     {
191         Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
192                 "position must be finite");
193         return new Ray2d(this.x + Math.cos(this.dirZ) * position, this.y + Math.sin(this.dirZ) * position, this.dirZ);
194     }
195 
196     @Override
197     public Point2d closestPointOnRay(final Point2d point) throws NullPointerException
198     {
199         Throw.whenNull(point, "point");
200         double dX = Math.cos(this.dirZ);
201         double dY = Math.sin(this.dirZ);
202         return point.closestPointOnLine(this.x, this.y, this.x + dX, this.y + dY, true, false);
203     }
204 
205     @Override
206     public Point2d projectOrthogonal(final Point2d point) throws NullPointerException
207     {
208         Throw.whenNull(point, "point");
209         return point.closestPointOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), null,
210                 false);
211     }
212 
213     @Override
214     public Point2d projectOrthogonalExtended(final Point2d point)
215     {
216         Throw.whenNull(point, "point");
217         return point.closestPointOnLine(getX(), getY(), getX() + Math.cos(this.dirZ), getY() + Math.sin(this.dirZ), false,
218                 false);
219     }
220 
221     @Override
222     public double projectOrthogonalFractional(final Point2d point) throws NullPointerException
223     {
224         Throw.whenNull(point, "point");
225         return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), null,
226                 false);
227     }
228 
229     @Override
230     public double projectOrthogonalFractionalExtended(final Point2d point) throws NullPointerException
231     {
232         Throw.whenNull(point, "point");
233         return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), false,
234                 false);
235     }
236 
237     @Override
238     public String toString()
239     {
240         return toString("%f", false);
241     }
242 
243     @Override
244     public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
245     {
246         String format = String.format("%1$s[x=%2$s, y=%2$s, dirZ=%2%s]", doNotIncludeClassName ? "" : "Ray2d ", doubleFormat);
247         return String.format(Locale.US, format, this.x, this.y, this.dirZ);
248     }
249 
250     @Override
251     public int hashCode()
252     {
253         return super.hashCode();
254     }
255 
256     @Override
257     @SuppressWarnings("checkstyle:needbraces")
258     public boolean equals(final Object obj)
259     {
260         if (this == obj)
261             return true;
262         if (!super.equals(obj))
263             return false;
264         if (getClass() != obj.getClass())
265             return false;
266         return true;
267     }
268 
269 }