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