View Javadoc
1   package org.djutils.draw.line;
2   
3   import java.util.Arrays;
4   import java.util.Iterator;
5   import java.util.Locale;
6   
7   import org.djutils.base.AngleUtil;
8   import org.djutils.draw.DrawRuntimeException;
9   import org.djutils.draw.Drawable3d;
10  import org.djutils.draw.bounds.Bounds3d;
11  import org.djutils.draw.point.DirectedPoint3d;
12  import org.djutils.draw.point.Point3d;
13  import org.djutils.exceptions.Throw;
14  
15  /**
16   * Ray3d is a half-line; it has one end point with non-infinite coordinates; the other end point is infinitely far away.
17   * <p>
18   * Copyright (c) 2020-2024 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://www.tudelft.nl/pknoppers">Peter Knoppers</a>
23   */
24  public class Ray3d extends DirectedPoint3d implements Drawable3d, Ray<Ray3d, DirectedPoint3d, Point3d>
25  {
26      /** */
27      private static final long serialVersionUID = 20210119L;
28  
29      /**
30       * Construct a new Ray3d.
31       * @param x double; the x coordinate of the finite end point of the ray
32       * @param y double; the y coordinate of the finite end point of the ray
33       * @param z double; the z coordinate of the finite end point of the ray
34       * @param dirY double; the angle from the positive Z axis direction in radians (the complement of the slope)
35       * @param dirZ double; the angle from the positive X axis direction in radians
36       * @throws IllegalArgumentException when <cite>dirY</cite>, or <cite>dirZ</cite> is NaN (should be impossible)
37       */
38      public Ray3d(final double x, final double y, final double z, final double dirY, final double dirZ)
39              throws IllegalArgumentException
40      {
41          super(x, y, z, dirY, dirZ);
42      }
43  
44      /**
45       * Create a new Ray3d from x, y, and z coordinates packed in a double array of three elements and direction dirY,dirZ.
46       * @param xyz double[]; the <cite>x</cite>, <cite>y</cite> and <cite>z</cite> coordinates in that order
47       * @param dirY double; the angle from the positive Z axis direction in radians (the complement of the slope)
48       * @param dirZ double; the angle from the positive X axis direction in radians
49       * @throws NullPointerException when <cite>xyx</cite> is null
50       * @throws IllegalArgumentException when the length of the <cite>xyz</cite> array is not 3, or contains a NaN value, or
51       *             <cite>dirY</cite>, or <cite>dirZ</cite> is NaN
52       */
53      public Ray3d(final double[] xyz, final double dirY, final double dirZ) throws NullPointerException, IllegalArgumentException
54      {
55          super(xyz, dirY, dirZ);
56      }
57  
58      /**
59       * Construct a new Ray3d.
60       * @param point Point3d; the finite end point of the ray
61       * @param dirY double; the angle from the positive Z axis direction in radians (the complement of the slope)
62       * @param dirZ double; the angle from the positive X axis direction in radians
63       * @throws NullPointerException when point is null
64       * @throws IllegalArgumentException when <cite>dirY</cite>, or <cite>dirZ</cite> is NaN
65       */
66      public Ray3d(final Point3d point, final double dirY, final double dirZ)
67              throws NullPointerException, IllegalArgumentException
68      {
69          super(point, dirY, dirZ);
70      }
71  
72      /**
73       * Construct a new Ray3d.
74       * @param x double; the x coordinate of the finite end point of the ray
75       * @param y double; the y coordinate of the finite end point of the ray
76       * @param z double; the z coordinate of the finite end point of the ray
77       * @param throughX double; the x coordinate of another point on the ray
78       * @param throughY double; the y coordinate of another point on the ray
79       * @param throughZ double; the z coordinate of another point on the ray
80       * @throws DrawRuntimeException when throughX == x and throughY == y and throughZ == z, or any through-value is NaN
81       */
82      public Ray3d(final double x, final double y, final double z, final double throughX, final double throughY,
83              final double throughZ) throws DrawRuntimeException
84      {
85          super(x, y, z, throughX, throughY, throughZ);
86      }
87  
88      /**
89       * Construct a new Ray3d.
90       * @param point Point3d; the finite end point of the ray
91       * @param throughX double; the x coordinate of another point on the ray
92       * @param throughY double; the y coordinate of another point on the ray
93       * @param throughZ double; the z coordinate of another point on the ray
94       * @throws NullPointerException when point is null
95       * @throws DrawRuntimeException when throughX == point.x and throughY == point.y and point.z == throughZ, or any
96       *             through-value is NaN
97       */
98      public Ray3d(final Point3d point, final double throughX, final double throughY, final double throughZ)
99              throws NullPointerException, DrawRuntimeException
100     {
101         super(point, throughX, throughY, throughZ);
102     }
103 
104     /**
105      * Create a new Ray3d with x, y, and z coordinates and orientation specified using a double array of three elements
106      * (containing dirX,dirY,dirZ in that order).
107      * @param x double; the x coordinate
108      * @param y double; the y coordinate
109      * @param z double; the z coordinate
110      * @param orientation double[]; the two direction values in a double array containing dirY and dirZ in that order. DirY is
111      *            the rotation from the positive z-axis to the direction. DirZ is the angle from the positive x-axis to the
112      *            projection of the direction in the x-y-plane.
113      * @throws NullPointerException when <code>orientation</code> is null
114      * @throws IllegalArgumentException when the length of the <code>direction</code> array is not 2, or contains a NaN value
115      */
116     public Ray3d(final double x, final double y, final double z, final double[] orientation)
117             throws NullPointerException, IllegalArgumentException
118     {
119         super(x, y, z, orientation);
120     }
121 
122     /**
123      * Create a new Rayt3d from x, y, and z coordinates packed in a double array of three elements and a direction specified
124      * using a double array of two elements.
125      * @param xyz double[]; the <cite>x</cite>, <cite>y</cite> and <cite>z</cite> coordinates in that order
126      * @param orientation double[]; the two orientation angles <cite>dirY</cite> and <cite>dirZ</cite> in that order
127      * @throws NullPointerException when <cite>xyx</cite> or <cite>orientation</cite> is null
128      * @throws IllegalArgumentException when the length of the <cite>xyx</cite> array is not 3 or the length of the
129      *             <cite>orientation</cite> array is not 2
130      */
131     public Ray3d(final double[] xyz, final double[] orientation) throws NullPointerException, IllegalArgumentException
132     {
133         super(xyz, orientation);
134     }
135 
136     /**
137      * Construct a new Ray3d.
138      * @param x double; the x coordinate of the finite end point of the ray
139      * @param y double; the y coordinate of the finite end point of the ray
140      * @param z double; the z coordinate of the finite end point of the ray
141      * @param throughPoint Point3d; another point on the ray
142      * @throws NullPointerException when throughPoint is null
143      * @throws DrawRuntimeException when throughPoint is exactly at (x, y)
144      */
145     public Ray3d(final double x, final double y, final double z, final Point3d throughPoint)
146             throws NullPointerException, DrawRuntimeException
147     {
148         super(x, y, z, throughPoint);
149     }
150 
151     /**
152      * Construct a new Ray3d.
153      * @param point Point3d; the finite end point of the ray
154      * @param throughPoint Point3d; another point on the ray
155      * @throws NullPointerException when point is null or throughPoint is null
156      * @throws DrawRuntimeException when throughPoint is exactly at point
157      */
158     public Ray3d(final Point3d point, final Point3d throughPoint) throws NullPointerException, DrawRuntimeException
159     {
160         super(point, throughPoint);
161     }
162 
163     /**
164      * Construct a new Ray3d.
165      * @param directedPoint DirectedPoint3d; point and direction of the new Ray3d
166      */
167     public Ray3d(final DirectedPoint3d directedPoint)
168     {
169         this(directedPoint, directedPoint.dirY, directedPoint.dirZ);
170     }
171 
172     @Override
173     public final double getDirY()
174     {
175         return this.dirY;
176     }
177 
178     @Override
179     public final double getDirZ()
180     {
181         return this.dirZ;
182     }
183 
184     @Override
185     public DirectedPoint3d getEndPoint()
186     {
187         return this;
188     }
189 
190     @Override
191     public int size()
192     {
193         return 2;
194     }
195 
196     @Override
197     public Iterator<DirectedPoint3d> getPoints()
198     {
199         double sinDirZ = Math.sin(this.dirZ);
200         double cosDirZ = Math.cos(this.dirZ);
201         double sinDirY = Math.sin(this.dirY);
202         double cosDirY = Math.cos(this.dirY);
203         DirectedPoint3d[] array = new DirectedPoint3d[] {this,
204                 new DirectedPoint3d(cosDirZ * sinDirY == 0 ? this.x : cosDirZ * sinDirY * Double.POSITIVE_INFINITY,
205                         cosDirZ * sinDirZ == 0 ? this.y : cosDirZ * sinDirZ * Double.POSITIVE_INFINITY,
206                         cosDirY == 0 ? this.z : cosDirY * Double.POSITIVE_INFINITY, this.dirZ, this.dirY)};
207         return Arrays.stream(array).iterator();
208     }
209 
210     @Override
211     public Bounds3d getBounds()
212     {
213         double sinDirZ = Math.sin(this.dirZ);
214         double cosDirZ = Math.cos(this.dirZ);
215         double sinDirY = Math.sin(this.dirY);
216         double cosDirY = Math.cos(this.dirY);
217         return new Bounds3d(cosDirZ * sinDirY >= 0 ? this.x : Double.NEGATIVE_INFINITY,
218                 cosDirZ * sinDirY <= 0 ? this.x : Double.POSITIVE_INFINITY,
219                 sinDirZ * sinDirY >= 0 ? this.y : Double.NEGATIVE_INFINITY,
220                 sinDirZ * sinDirY <= 0 ? this.y : Double.POSITIVE_INFINITY, cosDirY >= 0 ? this.z : Double.NEGATIVE_INFINITY,
221                 cosDirY <= 0 ? this.z : Double.POSITIVE_INFINITY);
222     }
223 
224     @Override
225     public Ray3d neg()
226     {
227         return new Ray3d(-this.x, -this.y, -this.z, AngleUtil.normalizeAroundZero(this.dirY + Math.PI),
228                 AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
229     }
230 
231     @Override
232     public Ray3d flip()
233     {
234         return new Ray3d(this.x, this.y, this.z, AngleUtil.normalizeAroundZero(Math.PI - this.dirY),
235                 AngleUtil.normalizeAroundZero(this.dirZ + Math.PI));
236     }
237 
238     @Override
239     public Ray3d getLocationExtended(final double position) throws DrawRuntimeException
240     {
241         Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
242                 "position must be finite");
243         double sinDirY = Math.sin(this.dirY);
244         double dX = Math.cos(this.dirZ) * sinDirY;
245         double dY = Math.sin(this.dirZ) * sinDirY;
246         double dZ = Math.cos(this.dirY);
247         return new Ray3d(this.x + dX * position, this.y + dY * position, this.z + dZ * position, this.dirY, this.dirZ);
248     }
249 
250     @Override
251     public Point3d closestPointOnRay(final Point3d point) throws NullPointerException
252     {
253         Throw.whenNull(point, "point");
254         double sinDirY = Math.sin(this.dirY);
255         return point.closestPointOnLine(this.x, this.y, this.z, this.x + Math.cos(this.dirZ) * sinDirY,
256                 this.y + Math.sin(this.dirZ) * sinDirY, this.z + Math.cos(this.dirY), true, false);
257     }
258 
259     @Override
260     public Point3d projectOrthogonal(final Point3d point) throws NullPointerException
261     {
262         Throw.whenNull(point, "point");
263         double sinDirY = Math.sin(this.dirY);
264         return point.closestPointOnLine(this.x, this.y, this.z, this.x + Math.cos(this.dirZ) * sinDirY,
265                 this.y + Math.sin(this.dirZ) * sinDirY, this.z + Math.cos(this.dirY), null, false);
266     }
267 
268     @Override
269     public Point3d projectOrthogonalExtended(final Point3d point)
270     {
271         Throw.whenNull(point, "point");
272         double sinDirY = Math.sin(this.dirY);
273         return point.closestPointOnLine(getX(), getY(), getZ(), getX() + Math.cos(this.dirZ) * sinDirY,
274                 getY() + Math.sin(this.dirZ) * sinDirY, getZ() + Math.cos(this.dirY), false, false);
275     }
276 
277     @Override
278     public double projectOrthogonalFractional(final Point3d point) throws NullPointerException
279     {
280         Throw.whenNull(point, "point");
281         double sinDirY = Math.sin(this.dirY);
282         return point.fractionalPositionOnLine(this.x, this.y, this.z, this.x + Math.cos(this.dirZ) * sinDirY,
283                 this.y + Math.sin(this.dirZ) * sinDirY, this.z + Math.cos(this.dirY), null, false);
284     }
285 
286     @Override
287     public double projectOrthogonalFractionalExtended(final Point3d point) throws NullPointerException
288     {
289         Throw.whenNull(point, "point");
290         double sinDirY = Math.sin(this.dirY);
291         return point.fractionalPositionOnLine(getX(), getY(), getZ(), getX() + Math.cos(this.dirZ) * sinDirY,
292                 getY() + Math.sin(this.dirZ) * sinDirY, getZ() + Math.cos(this.dirY), false, false);
293     }
294 
295     @Override
296     public String toString()
297     {
298         return toString("%f", false);
299     }
300 
301     @Override
302     public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
303     {
304         String format = String.format("%1$s[x=%2$s, y=%2$s, z=%2$s, dirY=%2$s, dirZ=%2$s]",
305                 doNotIncludeClassName ? "" : "Ray3d ", doubleFormat);
306         return String.format(Locale.US, format, this.x, this.y, this.z, this.dirY, this.dirZ);
307     }
308 
309     @Override
310     public int hashCode()
311     {
312         return super.hashCode();
313     }
314 
315     @Override
316     @SuppressWarnings("checkstyle:needbraces")
317     public boolean equals(final Object obj)
318     {
319         if (this == obj)
320             return true;
321         if (!super.equals(obj))
322             return false;
323         if (getClass() != obj.getClass())
324             return false;
325         return true;
326     }
327 
328 }