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.draw.DrawRuntimeException;
8   import org.djutils.draw.Drawable3d;
9   import org.djutils.draw.bounds.Bounds3d;
10  import org.djutils.draw.point.Point3d;
11  import org.djutils.exceptions.Throw;
12  
13  /**
14   * LineSegment3d is a line segment bound by 2 end points in 3D-space. A line segment stores the order in which it has been
15   * created, so the end points are known as 'start' and 'end'.
16   * <p>
17   * Copyright (c) 2020-2021 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
19   * </p>
20   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
21   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
22   */
23  public class LineSegment3d implements Drawable3d, LineSegment<Point3d, Ray3d>
24  {
25      /** */
26      private static final long serialVersionUID = 20210121L;
27  
28      /** The start x-coordinate. */
29      @SuppressWarnings("checkstyle:visibilitymodifier")
30      public final double startX;
31  
32      /** The start y-coordinate. */
33      @SuppressWarnings("checkstyle:visibilitymodifier")
34      public final double startY;
35  
36      /** The start z-coordinate. */
37      @SuppressWarnings("checkstyle:visibilitymodifier")
38      public final double startZ;
39  
40      /** The end x-coordinate. */
41      @SuppressWarnings("checkstyle:visibilitymodifier")
42      public final double endX;
43  
44      /** The end y-coordinate. */
45      @SuppressWarnings("checkstyle:visibilitymodifier")
46      public final double endY;
47  
48      /** The end z-coordinate. */
49      @SuppressWarnings("checkstyle:visibilitymodifier")
50      public final double endZ;
51  
52      /**
53       * Construct a new LineSegment3d start six coordinates.
54       * @param startX double; the x-coordinate of the start point
55       * @param startY double; the y-coordinate of the start point
56       * @param startZ double; the z-coordinate of the start point
57       * @param endX double; the x-coordinate of the end point
58       * @param endY double; the y-coordinate of the end point
59       * @param endZ double; the z-coordinate of the end point
60       * @throws DrawRuntimeException when (startX,startY) is equals end (endX,endY)
61       */
62      public LineSegment3d(final double startX, final double startY, final double startZ, final double endX, final double endY,
63              final double endZ) throws DrawRuntimeException
64      {
65          Throw.when(startX == endX && startY == endY && startZ == endZ, DrawRuntimeException.class,
66                  "Start and end may not be equal");
67          this.startX = startX;
68          this.startY = startY;
69          this.startZ = startZ;
70          this.endX = endX;
71          this.endY = endY;
72          this.endZ = endZ;
73      }
74  
75      /**
76       * Construct a new LineSegment3d start a Point3d and three coordinates.
77       * @param start Point3d; the start point
78       * @param endX double; the x-coordinate of the end point
79       * @param endY double; the y-coordinate of the end point
80       * @param endZ double; the z-coordinate of the end point
81       * @throws NullPointerException when start is null
82       * @throws DrawRuntimeException when start has the exact coordinates endX, endY
83       */
84      public LineSegment3d(final Point3d start, final double endX, final double endY, final double endZ)
85              throws NullPointerException, DrawRuntimeException
86      {
87          this(Throw.whenNull(start, "start point may not be null").x, start.y, start.z, endX, endY, endZ);
88      }
89  
90      /**
91       * Construct a new LineSegment3d start three coordinates and a Point3d.
92       * @param startX double; the x-coordinate of the start point
93       * @param startY double; the y-coordinate of the start point
94       * @param startZ double; the z-coordinate of the start point
95       * @param end Point3d; the end point
96       * @throws NullPointerException when end is null
97       * @throws DrawRuntimeException when end has the exact coordinates startX, startY
98       */
99      public LineSegment3d(final double startX, final double startY, final double startZ, final Point3d end)
100             throws NullPointerException, DrawRuntimeException
101     {
102         this(startX, startY, startZ, Throw.whenNull(end, "end point may not be null").x, end.y, end.z);
103     }
104 
105     /**
106      * Construct a new LineSegment3d start two Point3d objects.
107      * @param start Point3d; the start point
108      * @param end Point3d; the end point
109      * @throws NullPointerException when start is null
110      * @throws DrawRuntimeException when start has the exact coordinates endX, endY
111      */
112     public LineSegment3d(final Point3dd.html#Point3d">Point3d start, final Point3d end) throws NullPointerException, DrawRuntimeException
113     {
114         this(Throw.whenNull(start, "start point may not be null").x, start.y, start.z,
115                 Throw.whenNull(end, "end point may not be null").x, end.y, end.z);
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public Point3d getStartPoint()
121     {
122         return new Point3d(this.startX, this.startY, this.startZ);
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public Point3d getEndPoint()
128     {
129         return new Point3d(this.endX, this.endY, this.endZ);
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public double getLength()
135     {
136         // There is no varargs hypot function in Math
137         double dX = this.endX - this.startX;
138         double dY = this.endY - this.startY;
139         double dZ = this.endZ - this.startZ;
140         return Math.sqrt(dX * dX + dY * dY + dZ * dZ);
141     }
142 
143     /** {@inheritDoc} */
144     @Override
145     public Iterator<? extends Point3d> getPoints()
146     {
147         return Arrays.stream(new Point3d[] { getStartPoint(), getEndPoint() }).iterator();
148     }
149 
150     /** {@inheritDoc} */
151     @Override
152     public int size()
153     {
154         return 2;
155     }
156 
157     /** {@inheritDoc} */
158     @Override
159     public Bounds3d getBounds()
160     {
161         return new Bounds3d(Math.min(this.startX, this.endX), Math.max(this.startX, this.endX),
162                 Math.min(this.startY, this.endY), Math.max(this.startY, this.endY), Math.min(this.startZ, this.endZ),
163                 Math.max(this.startZ, this.endZ));
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public LineSegment2d project() throws DrawRuntimeException
169     {
170         return new LineSegment2d(this.startX, this.startY, this.endX, this.endY);
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public Ray3d getLocationExtended(final double position) throws DrawRuntimeException
176     {
177         Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
178                 "position must be finite");
179         double dX = this.endX - this.startX;
180         double dY = this.endY - this.startY;
181         double dZ = this.endZ - this.startZ;
182         double length = Math.sqrt(dX * dX + dY * dY + dZ * dZ);
183         return new Ray3d(this.startX + position * dX / length, this.startY + position * dY / length,
184                 this.startZ + position * dZ / length, Math.atan2(dY, dX), Math.atan2(dZ, Math.hypot(dX, dY)));
185     }
186 
187     /** {@inheritDoc} */
188     @Override
189     public Point3d>Point3d closestPointOnSegment(final Point3d point)
190     {
191         Throw.whenNull(point, "point may not be null");
192         return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, true, true);
193     }
194 
195     /** {@inheritDoc} */
196     @Override
197     public Point3dt3d">Point3d projectOrthogonal(final Point3d point) throws NullPointerException
198     {
199         Throw.whenNull(point, "point may not be null");
200         return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null, null);
201     }
202 
203     /** {@inheritDoc} */
204     @Override
205     public Point3dnt3d projectOrthogonalExtended(final Point3d point) throws NullPointerException
206     {
207         Throw.whenNull(point, "point may not be null");
208         return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public double projectOrthogonalFractional(final Point3d point) throws NullPointerException
214     {
215         Throw.whenNull(point, "point may not be null");
216         return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null,
217                 null);
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public double projectOrthogonalFractionalExtended(final Point3d point) throws NullPointerException
223     {
224         Throw.whenNull(point, "point may not be null");
225         return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, false,
226                 false);
227     }
228 
229     /** {@inheritDoc} */
230     @Override
231     public String toString()
232     {
233         return toString("%f", false);
234     }
235 
236     /** {@inheritDoc} */
237     @Override
238     public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
239     {
240         String format = String.format("%1$s[startX=%2$s, startY=%2$s, startZ=%2$s - endX=%2%s, endY=%2$s, endZ=%2$s]",
241                 doNotIncludeClassName ? "" : "LineSegment3d ", doubleFormat);
242         return String.format(Locale.US, format, this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
243     }
244 
245     /** {@inheritDoc} */
246     @Override
247     public String toExcel()
248     {
249         return this.startX + "\t" + this.startY + "\t" + this.startZ + "\n" + this.endX + "\t" + this.endY + "\t" + this.endZ
250                 + "\n";
251     }
252 
253     /** {@inheritDoc} */
254     @Override
255     public int hashCode()
256     {
257         final int prime = 31;
258         int result = 1;
259         long temp;
260         temp = Double.doubleToLongBits(this.endX);
261         result = prime * result + (int) (temp ^ (temp >>> 32));
262         temp = Double.doubleToLongBits(this.endY);
263         result = prime * result + (int) (temp ^ (temp >>> 32));
264         temp = Double.doubleToLongBits(this.endZ);
265         result = prime * result + (int) (temp ^ (temp >>> 32));
266         temp = Double.doubleToLongBits(this.startX);
267         result = prime * result + (int) (temp ^ (temp >>> 32));
268         temp = Double.doubleToLongBits(this.startY);
269         result = prime * result + (int) (temp ^ (temp >>> 32));
270         temp = Double.doubleToLongBits(this.startZ);
271         result = prime * result + (int) (temp ^ (temp >>> 32));
272         return result;
273     }
274 
275     /** {@inheritDoc} */
276     @Override
277     @SuppressWarnings("checkstyle:needbraces")
278     public boolean equals(final Object obj)
279     {
280         if (this == obj)
281             return true;
282         if (obj == null)
283             return false;
284         if (getClass() != obj.getClass())
285             return false;
286         LineSegment3d../../org/djutils/draw/line/LineSegment3d.html#LineSegment3d">LineSegment3d other = (LineSegment3d) obj;
287         if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
288             return false;
289         if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
290             return false;
291         if (Double.doubleToLongBits(this.endZ) != Double.doubleToLongBits(other.endZ))
292             return false;
293         if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
294             return false;
295         if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
296             return false;
297         if (Double.doubleToLongBits(this.startZ) != Double.doubleToLongBits(other.startZ))
298             return false;
299         return true;
300     }
301 
302 }