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