View Javadoc
1   package org.opentrafficsim.core.geometry;
2   
3   import java.awt.geom.Point2D;
4   import java.io.Serializable;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import org.djunits.unit.DirectionUnit;
9   import org.djunits.unit.LengthUnit;
10  import org.djunits.unit.Unit;
11  import org.djunits.value.storage.StorageType;
12  import org.djunits.value.vdouble.scalar.Direction;
13  import org.djunits.value.vdouble.scalar.Length;
14  import org.djunits.value.vdouble.scalar.base.DoubleScalarInterface;
15  import org.djunits.value.vdouble.vector.base.DoubleVector;
16  import org.djunits.value.vdouble.vector.base.DoubleVectorInterface;
17  import org.djutils.draw.point.Point3d;
18  import org.locationtech.jts.geom.Coordinate;
19  import org.locationtech.jts.geom.Point;
20  
21  import nl.tudelft.simulation.dsol.animation.Locatable;
22  import nl.tudelft.simulation.language.d3.CartesianPoint;
23  
24  /**
25   * An OTSPoint3D implements a 3D-coordinate for OTS. X, y and z are stored as doubles, but it is assumed that the scale is in SI
26   * units, i.e. in meters. A distance between two points is therefore also in meters.
27   * <p>
28   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
29   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
30   * <p>
31   * $LastChangedDate: 2015-07-16 10:20:53 +0200 (Thu, 16 Jul 2015) $, @version $Revision: 1124 $, by $Author: pknoppers $,
32   * initial version Jul 22, 2015 <br>
33   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
34   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
35   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
36   */
37  public class OTSPoint3D implements Locatable, Serializable
38  {
39      /** */
40      private static final long serialVersionUID = 20150722L;
41  
42      /** The internal representation of the point; x-coordinate. */
43      @SuppressWarnings("checkstyle:visibilitymodifier")
44      public final double x;
45  
46      /** The internal representation of the point; y-coordinate. */
47      @SuppressWarnings("checkstyle:visibilitymodifier")
48      public final double y;
49  
50      /** The internal representation of the point; z-coordinate. */
51      @SuppressWarnings("checkstyle:visibilitymodifier")
52      public final double z;
53  
54      /**
55       * The x, y and z in the point are assumed to be in meters relative to an origin.
56       * @param x double; x-coordinate
57       * @param y double; y-coordinate
58       * @param z double; z-coordinate
59       */
60      public OTSPoint3D(final double x, final double y, final double z)
61      {
62          this.x = x;
63          this.y = y;
64          this.z = z;
65      }
66  
67      /**
68       * @param xyz array with three elements; x, y and z are assumed to be in meters relative to an origin.
69       */
70      public OTSPoint3D(final double[] xyz)
71      {
72          this(xyz[0], xyz[1], (xyz.length > 2) ? xyz[2] : 0.0);
73      }
74  
75      /**
76       * @param point OTSPoint3D; a point to "clone".
77       */
78      public OTSPoint3D(final OTSPoint3D point)
79      {
80          this(point.x, point.y, point.z);
81      }
82  
83      /**
84       * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
85       *            origin.
86       */
87      public OTSPoint3D(final Point3d point)
88      {
89          this(point.x, point.y, point.z);
90      }
91  
92      /**
93       * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
94       *            origin.
95       */
96      public OTSPoint3D(final CartesianPoint point)
97      {
98          this(point.x, point.y, point.z);
99      }
100 
101     /**
102      * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
103      *            origin.
104      */
105     public OTSPoint3D(final DirectedPoint point)
106     {
107         this(point.x, point.y, point.z);
108     }
109 
110     /**
111      * @param point2d java.awt 2D point, z-coordinate will be zero; the x and y in the point are assumed to be in meters
112      *            relative to an origin.
113      */
114     public OTSPoint3D(final Point2D point2d)
115     {
116         this(point2d.getX(), point2d.getY(), 0.0);
117     }
118 
119     /**
120      * @param coordinate geotools coordinate; the x, y and z in the coordinate are assumed to be in meters relative to an
121      *            origin.
122      */
123     public OTSPoint3D(final Coordinate coordinate)
124     {
125         this(coordinate.x, coordinate.y, Double.isNaN(coordinate.getZ()) ? 0.0 : coordinate.getZ());
126     }
127 
128     /**
129      * @param point geotools point; z-coordinate will be zero; the x and y in the point are assumed to be in meters relative to
130      *            an origin.
131      */
132     public OTSPoint3D(final Point point)
133     {
134         this(point.getX(), point.getY(), 0.0);
135     }
136 
137     /**
138      * The x and y in the point are assumed to be in meters relative to an origin. z will be set to 0.
139      * @param x double; x-coordinate
140      * @param y double; y-coordinate
141      */
142     public OTSPoint3D(final double x, final double y)
143     {
144         this(x, y, 0.0);
145     }
146 
147     /**
148      * Interpolate (or extrapolate) between (outside) two given points.
149      * @param ratio double; 0 selects the zeroValue point, 1 selects the oneValue point, 0.5 selects a point halfway, etc.
150      * @param zeroValue OTSPoint3D; the point that is returned when ratio equals 0
151      * @param oneValue OTSPoint3D; the point that is returned when ratio equals 1
152      * @return OTSPoint3D
153      */
154     public static OTSPoint3D interpolate(final double ratio, final OTSPoint3D zeroValue, final OTSPoint3D oneValue)
155     {
156         double complement = 1 - ratio;
157         return new OTSPoint3D(complement * zeroValue.x + ratio * oneValue.x, complement * zeroValue.y + ratio * oneValue.y,
158                 complement * zeroValue.z + ratio * oneValue.z);
159     }
160 
161     /**
162      * Compute the 2D intersection of two line segments. The Z-component of the lines is ignored. Both line segments are defined
163      * by two points (that should be distinct). This version suffers loss of precision when called with very large coordinate
164      * values.
165      * @param line1P1 OTSPoint3D; first point of line segment 1
166      * @param line1P2 OTSPoint3D; second point of line segment 1
167      * @param line2P1 OTSPoint3D; first point of line segment 2
168      * @param line2P2 OTSPoint3D; second point of line segment 2
169      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel, or do not intersect
170      */
171     @Deprecated
172     public static OTSPoint3D intersectionOfLineSegmentsDumb(final OTSPoint3D line1P1, final OTSPoint3D line1P2,
173             final OTSPoint3D line2P1, final OTSPoint3D line2P2)
174     {
175         double denominator =
176                 (line2P2.y - line2P1.y) * (line1P2.x - line1P1.x) - (line2P2.x - line2P1.x) * (line1P2.y - line1P1.y);
177         if (denominator == 0f)
178         {
179             return null; // lines are parallel (they might even be on top of each other, but we don't check that)
180         }
181         double uA = ((line2P2.x - line2P1.x) * (line1P1.y - line2P1.y) - (line2P2.y - line2P1.y) * (line1P1.x - line2P1.x))
182                 / denominator;
183         if ((uA < 0f) || (uA > 1f))
184         {
185             return null; // intersection outside line 1
186         }
187         double uB = ((line1P2.x - line1P1.x) * (line1P1.y - line2P1.y) - (line1P2.y - line1P1.y) * (line1P1.x - line2P1.x))
188                 / denominator;
189         if (uB < 0 || uB > 1)
190         {
191             return null; // intersection outside line 2
192         }
193         return new OTSPoint3D(line1P1.x + uA * (line1P2.x - line1P1.x), line1P1.y + uA * (line1P2.y - line1P1.y), 0);
194     }
195 
196     /**
197      * Compute the 2D intersection of two line segments. The Z-component of the lines is ignored. Both line segments are defined
198      * by two points (that should be distinct).
199      * @param line1P1 OTSPoint3D; first point of line segment 1
200      * @param line1P2 OTSPoint3D; second point of line segment 1
201      * @param line2P1 OTSPoint3D; first point of line segment 2
202      * @param line2P2 OTSPoint3D; second point of line segment 2
203      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel, or do not intersect
204      */
205     public static OTSPoint3D intersectionOfLineSegments(final OTSPoint3D line1P1, final OTSPoint3D line1P2,
206             final OTSPoint3D line2P1, final OTSPoint3D line2P2)
207     {
208         double l1p1x = line1P1.x;
209         double l1p1y = line1P1.y;
210         double l1p2x = line1P2.x - l1p1x;
211         double l1p2y = line1P2.y - l1p1y;
212         double l2p1x = line2P1.x - l1p1x;
213         double l2p1y = line2P1.y - l1p1y;
214         double l2p2x = line2P2.x - l1p1x;
215         double l2p2y = line2P2.y - l1p1y;
216         double denominator = (l2p2y - l2p1y) * l1p2x - (l2p2x - l2p1x) * l1p2y;
217         if (denominator == 0.0)
218         {
219             return null; // lines are parallel (they might even be on top of each other, but we don't check that)
220         }
221         double uA = ((l2p2x - l2p1x) * (-l2p1y) - (l2p2y - l2p1y) * (-l2p1x)) / denominator;
222         // System.out.println("uA is " + uA);
223         if ((uA < 0.0) || (uA > 1.0))
224         {
225             return null; // intersection outside line 1
226         }
227         double uB = (l1p2y * l2p1x - l1p2x * l2p1y) / denominator;
228         // System.out.println("uB is " + uB);
229         if (uB < 0.0 || uB > 1.0)
230         {
231             return null; // intersection outside line 2
232         }
233         return new OTSPoint3D(line1P1.x + uA * l1p2x, line1P1.y + uA * l1p2y, 0);
234     }
235 
236     /**
237      * Compute the 2D intersection of two infinite lines. The Z-component of the lines is ignored. Both lines are defined by two
238      * points (that should be distinct). This version suffers loss of precision when called with very large coordinate values.
239      * @param line1P1 OTSPoint3D; first point of line 1
240      * @param line1P2 OTSPoint3D; second point of line 1
241      * @param line2P1 OTSPoint3D; first point of line 2
242      * @param line2P2 OTSPoint3D; second point of line 2
243      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel
244      */
245     @Deprecated
246     public static OTSPoint3D intersectionOfLinesDumb(final OTSPoint3D line1P1, final OTSPoint3D line1P2,
247             final OTSPoint3D line2P1, final OTSPoint3D line2P2)
248     {
249         double determinant =
250                 (line1P1.x - line1P2.x) * (line2P1.y - line2P2.y) - (line1P1.y - line1P2.y) * (line2P1.x - line2P2.x);
251         if (Math.abs(determinant) < 0.0000001)
252         {
253             return null;
254         }
255         return new OTSPoint3D(
256                 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.x - line2P2.x)
257                         - (line1P1.x - line1P2.x) * (line2P1.x * line2P2.y - line2P1.y * line2P2.x)) / determinant,
258                 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.y - line2P2.y)
259                         - (line1P1.y - line1P2.y) * (line2P1.x * line2P2.y - line2P1.y * line2P2.x)) / determinant);
260     }
261 
262     /**
263      * Compute the 2D intersection of two infinite lines. The Z-component of the lines is ignored. Both lines are defined by two
264      * points (that should be distinct).
265      * @param line1P1 OTSPoint3D; first point of line 1
266      * @param line1P2 OTSPoint3D; second point of line 1
267      * @param line2P1 OTSPoint3D; first point of line 2
268      * @param line2P2 OTSPoint3D; second point of line 2
269      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel
270      */
271     public static OTSPoint3D intersectionOfLines(final OTSPoint3D line1P1, final OTSPoint3D line1P2, final OTSPoint3D line2P1,
272             final OTSPoint3D line2P2)
273     {
274         double l1p1x = line1P1.x;
275         double l1p1y = line1P1.y;
276         double l1p2x = line1P2.x - l1p1x;
277         double l1p2y = line1P2.y - l1p1y;
278         double l2p1x = line2P1.x - l1p1x;
279         double l2p1y = line2P1.y - l1p1y;
280         double l2p2x = line2P2.x - l1p1x;
281         double l2p2y = line2P2.y - l1p1y;
282         double determinant = (0 - l1p2x) * (l2p1y - l2p2y) - (0 - l1p2y) * (l2p1x - l2p2x);
283         if (Math.abs(determinant) < 0.0000001)
284         {
285             return null;
286         }
287         return new OTSPoint3D(l1p1x + (l1p2x * (l2p1x * l2p2y - l2p1y * l2p2x)) / determinant,
288                 l1p1y + (l1p2y * (l2p1x * l2p2y - l2p1y * l2p2x)) / determinant);
289     }
290 
291     /**
292      * Project a point on a line segment (2D - Z-component is ignored). If the the projected points lies outside the line
293      * segment, the nearest end point of the line segment is returned. Otherwise the returned point lies between the end points
294      * of the line segment. <br>
295      * Adapted from <a href="http://paulbourke.net/geometry/pointlineplane/DistancePoint.java">example code provided by Paul
296      * Bourke</a>.
297      * @param segmentPoint1 OTSPoint3D; start of line segment
298      * @param segmentPoint2 OTSPoint3D; end of line segment
299      * @return Point2D.Double; either <cite>lineP1</cite>, or <cite>lineP2</cite> or a new OTSPoint3D that lies somewhere in
300      *         between those two. The Z-component of the result matches the Z-component of the line segment at that point
301      */
302     public final OTSPoint3D closestPointOnSegment(final OTSPoint3D segmentPoint1, final OTSPoint3D segmentPoint2)
303     {
304         double dX = segmentPoint2.x - segmentPoint1.x;
305         double dY = segmentPoint2.y - segmentPoint1.y;
306         if ((0 == dX) && (0 == dY))
307         {
308             return segmentPoint1;
309         }
310         final double u = ((this.x - segmentPoint1.x) * dX + (this.y - segmentPoint1.y) * dY) / (dX * dX + dY * dY);
311         if (u < 0)
312         {
313             return segmentPoint1;
314         }
315         else if (u > 1)
316         {
317             return segmentPoint2;
318         }
319         else
320         {
321             return interpolate(u, segmentPoint1, segmentPoint2);
322         }
323     }
324 
325     /**
326      * Return the closest point on an OTSLine3D.
327      * @param line OTSLine3D; the line
328      * @param useHorizontalDistance boolean; if true; the horizontal distance is used to determine the closest point; if false;
329      *            the 3D distance is used to determine the closest point
330      * @return OTSPoint3D; the Z component of the returned point matches the Z-component of the line at that point
331      */
332     private OTSPoint3D internalClosestPointOnLine(final OTSLine3D line, final boolean useHorizontalDistance)
333     {
334         OTSPoint3D prevPoint = null;
335         double distance = Double.MAX_VALUE;
336         OTSPoint3D result = null;
337         for (OTSPoint3D nextPoint : line.getPoints())
338         {
339             if (null != prevPoint)
340             {
341                 OTSPoint3D closest = closestPointOnSegment(prevPoint, nextPoint);
342                 double thisDistance = useHorizontalDistance ? horizontalDistanceSI(closest) : distanceSI(closest);
343                 if (thisDistance < distance)
344                 {
345                     result = closest;
346                     distance = thisDistance;
347                 }
348             }
349             prevPoint = nextPoint;
350         }
351         return result;
352     }
353 
354     /**
355      * Return the closest point on an OTSLine3D. This method takes the Z-component of this point and the line into account.
356      * @param line OTSLine3D; the line
357      * @return OTSPoint3D; the Z-component of the returned point matches the Z-component of the line at that point
358      */
359     public final OTSPoint3D closestPointOnLine(final OTSLine3D line)
360     {
361         return internalClosestPointOnLine(line, false);
362     }
363 
364     /**
365      * Return the closest point on an OTSLine3D. This method ignores the Z-component of this point and the line when computing
366      * the distance.
367      * @param line OTSLine3D; the line
368      * @return OTSPoint3D; the Z-component of the returned point matches the Z-component of the line at that point
369      */
370     public final OTSPoint3D closestPointOnLine2D(final OTSLine3D line)
371     {
372         return internalClosestPointOnLine(line, true);
373     }
374 
375     /**
376      * Return the point with a length of 1 to the origin.
377      * @return OTSPoint3D; the normalized point
378      */
379     public final OTSPoint3D normalize()
380     {
381         double length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
382         return this.translate(length);
383     }
384 
385     /**
386      * Return this point translated by a factor from the origin.
387      * @param factor double; the translation factor
388      * @return OTSPoint3D; the translated point
389      */
390     public final OTSPoint3D translate(final double factor)
391     {
392         return new OTSPoint3D(this.x / factor, this.y / factor, this.z / factor);
393     }
394 
395     /**
396      * Return the possible center points of a circle (sphere), given two points and a radius. Only points with Z-coordinate
397      * equal to the mean of the given points are returned. (Without this restriction on the Z-coordinate, the result set would
398      * be either empty, a single point, or all points on a circle.)
399      * @param point1 OTSPoint3D; the first point
400      * @param point2 OTSPoint3D; the second point
401      * @param radius double; the radius
402      * @return List&lt;OTSPoint3D&gt; a list of zero, one or two points
403      */
404     public static final List<OTSPoint3D> circleCenter(final OTSPoint3D point1, final OTSPoint3D point2, final double radius)
405     {
406         List<OTSPoint3D> result = new ArrayList<>();
407         OTSPoint3D m = interpolate(0.5, point1, point2);
408         double h = point1.distanceSI(m);
409         if (radius < h) // no intersection
410         {
411             return result;
412         }
413         if (radius == h) // intersection at m
414         {
415             result.add(m);
416             return result;
417         }
418         OTSPoint3D p = new OTSPoint3D(point2.y - point1.y, point1.x - point2.x).normalize();
419         double d = Math.sqrt(radius * radius - h * h); // distance of center from m
420         d = Math.sqrt(radius * radius - h * h);
421         result.add(new OTSPoint3D(m.x + d * p.x, m.y + d * p.y, m.z));
422         result.add(new OTSPoint3D(m.x - d * p.x, m.y - d * p.y, m.z));
423         return result;
424     }
425 
426     /**
427      * Return the possible intersections between two circles.
428      * @param center1 OTSPoint3D; the center of circle 1
429      * @param radius1 double; the radius of circle 1
430      * @param center2 OTSPoint3D; the center of circle 2
431      * @param radius2 double; the radius of circle 2
432      * @return List&lt;OTSPoint3D&gt; a list of zero, one or two points
433      */
434     public static final List<OTSPoint3D> circleIntersections(final OTSPoint3D center1, final double radius1,
435             final OTSPoint3D center2, final double radius2)
436     {
437         List<OTSPoint3D> center = new ArrayList<>();
438         OTSPoint3D m = interpolate(radius1 / (radius1 + radius2), center1, center2);
439         double h = center1.distanceSI(m);
440         if (radius1 < h) // no intersection
441         {
442             return center;
443         }
444         if (radius1 == h) // intersection at m
445         {
446             center.add(m);
447             return center;
448         }
449         OTSPoint3D p = new OTSPoint3D(center2.y - center1.y, center1.x - center2.x).normalize();
450         double d = Math.sqrt(radius1 * radius1 - h * h); // distance of center from m
451         center.add(new OTSPoint3D(m.x + d * p.x, m.y + d * p.y, m.z));
452         center.add(new OTSPoint3D(m.x - d * p.x, m.y - d * p.y, m.z));
453         return center;
454     }
455 
456     /**
457      * @param point OTSPoint3D; the point to which the distance has to be calculated.
458      * @return the distance in 3D according to Pythagoras, expressed in the SI unit for length (meter)
459      */
460     public final double distanceSI(final OTSPoint3D point)
461     {
462         double dx = point.x - this.x;
463         double dy = point.y - this.y;
464         double dz = point.z - this.z;
465 
466         return Math.sqrt(dx * dx + dy * dy + dz * dz);
467     }
468 
469     /**
470      * @param point OTSPoint3D; the point to which the distance has to be calculated.
471      * @return the distance in 3D according to Pythagoras, expressed in the SI unit for length (meter)
472      */
473     public final double horizontalDistanceSI(final OTSPoint3D point)
474     {
475         double dx = point.x - this.x;
476         double dy = point.y - this.y;
477 
478         return Math.sqrt(dx * dx + dy * dy);
479     }
480 
481     /**
482      * @param point OTSPoint3D; the point to which the distance has to be calculated.
483      * @return the distance in 3D according to Pythagoras
484      */
485     public final Length horizontalDistance(final OTSPoint3D point)
486     {
487         return new Length(horizontalDistanceSI(point), LengthUnit.SI);
488     }
489 
490     /**
491      * Compute the distance to another point.
492      * @param point OTSPoint3D; the point to which the distance has to be calculated.
493      * @return the distance in 3D according to Pythagoras
494      */
495     public final Length distance(final OTSPoint3D point)
496     {
497         return new Length(distanceSI(point), LengthUnit.SI);
498     }
499 
500     /**
501      * Compute the horizontal direction to another point.
502      * @param point OTSPoint3D; the other point
503      * @return double; the direction in radians
504      */
505     public final double horizontalDirectionSI(final OTSPoint3D point)
506     {
507         return Math.atan2(point.y - this.y, point.x - this.x);
508     }
509 
510     /**
511      * Compute the horizontal direction to another point.
512      * @param point OTSPoint3D; the other point
513      * @return double; the direction in radians
514      */
515     public final Direction horizontalDirection(final OTSPoint3D point)
516     {
517         return Direction.instantiateSI(Math.atan2(point.y - this.y, point.x - this.x));
518     }
519 
520     /**
521      * @return the equivalent geotools Coordinate of this point.
522      */
523     public final Coordinate getCoordinate()
524     {
525         return new Coordinate(this.x, this.y, this.z);
526     }
527 
528     /**
529      * @return the equivalent DSOL DirectedPoint of this point. Should the result be cached?
530      */
531     public final DirectedPoint getDirectedPoint()
532     {
533         return new DirectedPoint(this.x, this.y, this.z);
534     }
535 
536     /**
537      * @return a Point2D with the x and y structure.
538      */
539     public final Point2D getPoint2D()
540     {
541         return new Point2D.Double(this.x, this.y);
542     }
543 
544     /** {@inheritDoc} */
545     @Override
546     public final DirectedPoint getLocation()
547     {
548         return getDirectedPoint();
549     }
550 
551     /**
552      * This method returns a sphere with a diameter of half a meter as the default bounds for a point. {@inheritDoc}
553      */
554     @Override
555     public final Bounds getBounds()
556     {
557         return new Bounds();
558     }
559 
560     /**
561      * Create a DENSE DoubleVector with the x, y and z values of this OTSPoint3D.
562      * @param unit U; unit of the values in this OTSPoint3D (and also the display unit of the returned DoubleVector)
563      * @return DoubleVector; the constructed DoubleVector, size is 3; first value is x, second is y, third is z
564      * @param <U> the unit type
565      * @param <S> the corresponding scalar type
566      * @param <V> the corresponding vector type
567      */
568     public <U extends Unit<U>, S extends DoubleScalarInterface<U, S>,
569             V extends DoubleVectorInterface<U, S, V>> V doubleVector(final U unit)
570     {
571         return DoubleVector.instantiate(new double[] { this.x, this.y, this.z }, unit, StorageType.DENSE);
572     }
573     
574     /**
575      * Construct a Direction from the rotZ component of a DirectedPoint.
576      * @param directedPoint DirectedPoint; the DirectedPoint
577      * @param directionUnit DirectionUnit; the unit in which the rotZ of <code>directedPoint</code> is expressed and which is
578      *            also the display unit of the returned Direction
579      * @return Direction; the horizontal direction (rotZ) of the <code>directedPoint</code> with display unit
580      *         <code>directionUnit</code>
581      */
582     public static Direction direction(final DirectedPoint directedPoint, final DirectionUnit directionUnit)
583     {
584         return new Direction(directedPoint.getRotZ(), directionUnit);
585     }
586 
587     /** {@inheritDoc} */
588     @Override
589     @SuppressWarnings("checkstyle:designforextension")
590     public String toString()
591     {
592         return String.format("(%.3f,%.3f,%.3f)", this.x, this.y, this.z);
593     }
594 
595     /** {@inheritDoc} */
596     @Override
597     @SuppressWarnings("checkstyle:designforextension")
598     public int hashCode()
599     {
600         final int prime = 31;
601         int result = 1;
602         long temp;
603         temp = Double.doubleToLongBits(this.x);
604         result = prime * result + (int) (temp ^ (temp >>> 32));
605         temp = Double.doubleToLongBits(this.y);
606         result = prime * result + (int) (temp ^ (temp >>> 32));
607         temp = Double.doubleToLongBits(this.z);
608         result = prime * result + (int) (temp ^ (temp >>> 32));
609         return result;
610     }
611 
612     /** {@inheritDoc} */
613     @Override
614     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
615     public boolean equals(final Object obj)
616     {
617         if (this == obj)
618             return true;
619         if (obj == null)
620             return false;
621         if (getClass() != obj.getClass())
622             return false;
623         OTSPoint3D other = (OTSPoint3D) obj;
624         if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x))
625             return false;
626         if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y))
627             return false;
628         if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z))
629             return false;
630         return true;
631     }
632 
633 }