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      private static final long serialVersionUID = 20210119L;
29  
30      /**
31       * Construct a new Ray2d.
32       * @param x the x coordinate of the finite end point of the ray
33       * @param y the y coordinate of the finite end point of the ray
34       * @param dirZ the angle from the positive X axis direction in radians.
35       * @throws ArithmeticException when <code>dirZ</code> is <code>NaN</code>
36       */
37      public Ray2d(final double x, final double y, final double dirZ)
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 the <code>x</code> and <code>y</code> coordinates of the finite end point in that order
45       * @param dirZ the counter-clockwise rotation around the point in radians
46       * @throws NullPointerException when <code>xy</code> is <code>null</code>
47       * @throws ArithmeticException when <code>xy</code> contains <code>NaN</code>, or rotZ is <code>NaN</code>
48       * @throws IllegalArgumentException when the length of <code>xy</code> is not 2, or <code>dirZ</code> is infinite
49       */
50      public Ray2d(final double[] xy, final double dirZ)
51      {
52          super(xy, dirZ);
53      }
54  
55      /**
56       * Construct a new Ray2d from an AWT Point2D and a direction.
57       * @param point an AWT Point2D
58       * @param dirZ the counter-clockwise rotation around the point in radians
59       * @throws NullPointerException when <code>point</code> is <code>null</code>
60       * @throws ArithmeticException when any coordinate in <code>point</code> is <code>NaN</code>, or <code>rotZ</code> is
61       *             <code>NaN</code>
62       */
63      public Ray2d(final Point2D point, final double dirZ)
64      {
65          super(point, dirZ);
66      }
67  
68      /**
69       * Construct a new Ray2d from a Point2d and a direction.
70       * @param point the finite end point of the ray
71       * @param dirZ the angle from the positive X axis direction in radians.
72       * @throws NullPointerException when <code>point</code> is <code>null</code>
73       * @throws ArithmeticException when <code>dirZ</code> is <code>NaN</code>
74       */
75      public Ray2d(final Point2d point, final double dirZ)
76      {
77          super(point, dirZ);
78      }
79  
80      /**
81       * Construct a new Ray2d.
82       * @param x the x coordinate of the finite end point of the ray
83       * @param y the y coordinate of the finite end point of the ray
84       * @param throughX the x coordinate of another point on the ray
85       * @param throughY the y coordinate of another point on the ray
86       * @throws IllegalArgumentException when <code>throughX == x</code> and <code>throughY ==
87       *             y</code>
88       * @throws ArithmeticException when any <code>throughX</code> or <code>throughY</code> is <code>NaN</code>
89       */
90      public Ray2d(final double x, final double y, final double throughX, final double throughY)
91      {
92          super(x, y, throughX, throughY);
93      }
94  
95      /**
96       * Construct a new Ray2d.
97       * @param point the finite end point of the ray
98       * @param throughX the x coordinate of another point on the ray
99       * @param throughY the y coordinate of another point on the ray
100      * @throws NullPointerException when <code>point</code> is <code>null</code>
101      * @throws ArithmeticException when any <code>throughX</code>, or <code>throughY</code> is <code>NaN</code>
102      * @throws IllegalArgumentException when <code>throughX == x</code> and <code>throughY ==
103      *             y</code>
104      */
105     public Ray2d(final Point2d point, final double throughX, final double throughY)
106     {
107         super(point, throughX, throughY);
108     }
109 
110     /**
111      * Construct a new Ray2d.
112      * @param x the x coordinate of the finite end point of the ray
113      * @param y the y coordinate of the finite end point of the ray
114      * @param throughPoint another point on the ray
115      * @throws NullPointerException when <code>throughPoint</code> is <code>null</code>
116      * @throws IllegalArgumentException when <code>throughPoint</code> is exactly at (x, y)
117      */
118     public Ray2d(final double x, final double y, final Point2d throughPoint)
119     {
120         this(x, y, Throw.whenNull(throughPoint, "througPoint").x, throughPoint.y);
121     }
122 
123     /**
124      * Construct a new Ray2d.
125      * @param point the finite end point of the ray
126      * @param throughPoint another point on the ray
127      * @throws NullPointerException when <code>throughPoint</code> is <code>null</code>
128      * @throws IllegalArgumentException when <code>throughPoint</code> is exactly at <code>(x,y)</code>
129      */
130     public Ray2d(final Point2d point, final Point2d throughPoint)
131     {
132         this(Throw.whenNull(point, "point").x, point.y, Throw.whenNull(throughPoint, "throughPoint").x, throughPoint.y);
133     }
134 
135     /**
136      * Construct a new Ray2d.
137      * @param directedPoint point and direction of the new Ray2d
138      */
139     public Ray2d(final DirectedPoint2d directedPoint)
140     {
141         this(directedPoint, directedPoint.dirZ);
142     }
143 
144     @Override
145     public final double getDirZ()
146     {
147         return this.dirZ;
148     }
149 
150     @Override
151     public DirectedPoint2d getEndPoint()
152     {
153         return this;
154     }
155 
156     @Override
157     public int size()
158     {
159         return 2;
160     }
161 
162     @Override
163     public Iterator<Point2d> iterator()
164     {
165         double cosDirZ = Math.cos(this.dirZ);
166         double sinDirZ = Math.sin(this.dirZ);
167         Point2d[] array = new Point2d[] {this, new Point2d(cosDirZ == 0 ? this.x : cosDirZ * Double.POSITIVE_INFINITY,
168                 sinDirZ == 0 ? this.y : sinDirZ * Double.POSITIVE_INFINITY)};
169         return Arrays.stream(array).iterator();
170     }
171 
172     @Override
173     public Bounds2d getBounds()
174     {
175         double cosDirZ = Math.cos(this.dirZ);
176         double sinDirZ = Math.sin(this.dirZ);
177         return new Bounds2d(cosDirZ >= 0 ? this.x : Double.NEGATIVE_INFINITY, cosDirZ <= 0 ? this.x : Double.POSITIVE_INFINITY,
178                 sinDirZ >= 0 ? this.y : Double.NEGATIVE_INFINITY, sinDirZ <= 0 ? this.y : Double.POSITIVE_INFINITY);
179     }
180 
181     @Override
182     public Ray2d neg()
183     {
184         return new Ray2d(-this.x, -this.y, AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
185     }
186 
187     @Override
188     public Ray2d flip()
189     {
190         return new Ray2d(this.x, this.y, AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
191     }
192 
193     @Override
194     public Ray2d getLocationExtended(final double position)
195     {
196         Throw.whenNaN(position, "position");
197         Throw.when(Double.isInfinite(position), IllegalArgumentException.class, "position must be finite");
198         return new Ray2d(this.x + Math.cos(this.dirZ) * position, this.y + Math.sin(this.dirZ) * position, this.dirZ);
199     }
200 
201     @Override
202     public Point2d closestPointOnRay(final Point2d point)
203     {
204         Throw.whenNull(point, "point");
205         double dX = Math.cos(this.dirZ);
206         double dY = Math.sin(this.dirZ);
207         return point.closestPointOnLine(this.x, this.y, this.x + dX, this.y + dY, true, false);
208     }
209 
210     @Override
211     public Point2d projectOrthogonal(final Point2d point)
212     {
213         Throw.whenNull(point, "point");
214         return point.closestPointOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), null,
215                 false);
216     }
217 
218     @Override
219     public Point2d projectOrthogonalExtended(final Point2d point)
220     {
221         Throw.whenNull(point, "point");
222         return point.closestPointOnLine(getX(), getY(), getX() + Math.cos(this.dirZ), getY() + Math.sin(this.dirZ), false,
223                 false);
224     }
225 
226     @Override
227     public double projectOrthogonalFractional(final Point2d point)
228     {
229         Throw.whenNull(point, "point");
230         return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), null,
231                 false);
232     }
233 
234     @Override
235     public double projectOrthogonalFractionalExtended(final Point2d point)
236     {
237         Throw.whenNull(point, "point");
238         return point.fractionalPositionOnLine(this.x, this.y, this.x + Math.cos(this.dirZ), this.y + Math.sin(this.dirZ), false,
239                 false);
240     }
241 
242     @Override
243     public String toString()
244     {
245         return toString("%f", false);
246     }
247 
248     @Override
249     public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
250     {
251         String format = String.format("%1$s[x=%2$s, y=%2$s, dirZ=%2%s]", doNotIncludeClassName ? "" : "Ray2d ", doubleFormat);
252         return String.format(Locale.US, format, this.x, this.y, this.dirZ);
253     }
254 
255     @Override
256     public int hashCode()
257     {
258         return super.hashCode();
259     }
260 
261     @Override
262     @SuppressWarnings("checkstyle:needbraces")
263     public boolean equals(final Object obj)
264     {
265         if (this == obj)
266             return true;
267         if (!super.equals(obj))
268             return false;
269         if (getClass() != obj.getClass())
270             return false;
271         return true;
272     }
273 
274 }