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.Drawable2d;
9   import org.djutils.draw.Space2d;
10  import org.djutils.draw.bounds.Bounds2d;
11  import org.djutils.draw.point.Point2d;
12  import org.djutils.exceptions.Throw;
13  
14  /**
15   * LineSegment2d is a line segment bound by 2 end points in 2D-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 LineSegment2d implements Drawable2d, LineSegment<Point2d, Ray2d, Space2d>
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 end x-coordinate. */
38      @SuppressWarnings("checkstyle:visibilitymodifier")
39      public final double endX;
40  
41      /** The end y-coordinate. */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      public final double endY;
44  
45      /**
46       * Construct a new LineSegment2d start four coordinates.
47       * @param startX double; the x-coordinate of the start point
48       * @param startY double; the y-coordinate of the start point
49       * @param endX double; the x-coordinate of the end point
50       * @param endY double; the y-coordinate of the end point
51       * @throws DrawRuntimeException when (startX,startY) is equals end (endX,endY)
52       */
53      public LineSegment2d(final double startX, final double startY, final double endX, final double endY)
54              throws DrawRuntimeException
55      {
56          Throw.when(startX == endX && startY == endY, DrawRuntimeException.class, "Start and end may not be equal");
57          this.startX = startX;
58          this.startY = startY;
59          this.endX = endX;
60          this.endY = endY;
61      }
62  
63      /**
64       * Construct a new LineSegment2d start a Point2d and two coordinates.
65       * @param start Point2d; the start point
66       * @param endX double; the x-coordinate of the end point
67       * @param endY double; the y-coordinate of the end point
68       * @throws NullPointerException when start is null
69       * @throws DrawRuntimeException when start has the exact coordinates endX, endY
70       */
71      public LineSegment2d(final Point2d start, final double endX, final double endY)
72              throws NullPointerException, DrawRuntimeException
73      {
74          this(Throw.whenNull(start, "start point may not be null").x, start.y, endX, endY);
75      }
76  
77      /**
78       * Construct a new LineSegment2d start two coordinates and a Point2d.
79       * @param startX double; the x-coordinate of the start point
80       * @param startY double; the y-coordinate of the start point
81       * @param end Point2d; the end point
82       * @throws NullPointerException when end is null
83       * @throws DrawRuntimeException when end has the exact coordinates startX, startY
84       */
85      public LineSegment2d(final double startX, final double startY, final Point2d end)
86              throws NullPointerException, DrawRuntimeException
87      {
88          this(startX, startY, Throw.whenNull(end, "end point may not be null").x, end.y);
89      }
90  
91      /**
92       * Construct a new LineSegment2d start two Point2d objects.
93       * @param start Point2d; the start point
94       * @param end Point2d; the end point
95       * @throws NullPointerException when start is null
96       * @throws DrawRuntimeException when start has the exact coordinates endX, endY
97       */
98      public LineSegment2d(final Point2dd.html#Point2d">Point2d start, final Point2d end) throws NullPointerException, DrawRuntimeException
99      {
100         this(Throw.whenNull(start, "start point may not be null").x, start.y,
101                 Throw.whenNull(end, "end point may not be null").x, end.y);
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public Point2d getStartPoint()
107     {
108         return new Point2d(this.startX, this.startY);
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public Point2d getEndPoint()
114     {
115         return new Point2d(this.endX, this.endY);
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public double getLength()
121     {
122         return Math.hypot(this.endX - this.startX, this.endY - this.startY);
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public Iterator<? extends Point2d> getPoints()
128     {
129         return Arrays.stream(new Point2d[] { getStartPoint(), getEndPoint() }).iterator();
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public int size()
135     {
136         return 2;
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public Bounds2d getBounds()
142     {
143         return new Bounds2d(Math.min(this.startX, this.endX), Math.max(this.startX, this.endX),
144                 Math.min(this.startY, this.endY), Math.max(this.startY, this.endY));
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public Ray2d getLocationExtended(final double position) throws DrawRuntimeException
150     {
151         Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
152                 "position must be finite");
153         double dX = this.endX - this.startX;
154         double dY = this.endY - this.startY;
155         double length = Math.hypot(dX, dY);
156         return new Ray2d(this.startX + position * dX / length, this.startY + position * dY / length, Math.atan2(dY, dX));
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     public Point2d>Point2d closestPointOnSegment(final Point2d point) throws NullPointerException
162     {
163         Throw.whenNull(point, "point may not be null");
164         return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, true, true);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public Point2dt2d">Point2d projectOrthogonal(final Point2d point) throws NullPointerException
170     {
171         Throw.whenNull(point, "point may not be null");
172         return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public Point2dnt2d projectOrthogonalExtended(final Point2d point)
178     {
179         Throw.whenNull(point, "point may not be null");
180         return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
181     }
182 
183     /** {@inheritDoc} */
184     @Override
185     public double projectOrthogonalFractional(final Point2d point) throws NullPointerException
186     {
187         Throw.whenNull(point, "point may not be null");
188         return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     public double projectOrthogonalFractionalExtended(final Point2d point) throws NullPointerException
194     {
195         Throw.whenNull(point, "point may not be null");
196         return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
197     }
198 
199     /** {@inheritDoc} */
200     @Override
201     public String toString()
202     {
203         return toString("%f", false);
204     }
205 
206     /** {@inheritDoc} */
207     @Override
208     public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
209     {
210         String format = String.format("%1$s[startX=%2$s, startY=%2$s - endX=%2%s, endY=%2$s]",
211                 doNotIncludeClassName ? "" : "LineSegment2d ", doubleFormat);
212         return String.format(Locale.US, format, this.startX, this.startY, this.endX, this.endY);
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public String toExcel()
218     {
219         return this.startX + "\t" + this.startY + "\n" + this.endX + "\t" + this.endY + "\n";
220     }
221 
222     /** {@inheritDoc} */
223     @Override
224     public int hashCode()
225     {
226         final int prime = 31;
227         int result = 1;
228         long temp;
229         temp = Double.doubleToLongBits(this.endX);
230         result = prime * result + (int) (temp ^ (temp >>> 32));
231         temp = Double.doubleToLongBits(this.endY);
232         result = prime * result + (int) (temp ^ (temp >>> 32));
233         temp = Double.doubleToLongBits(this.startX);
234         result = prime * result + (int) (temp ^ (temp >>> 32));
235         temp = Double.doubleToLongBits(this.startY);
236         result = prime * result + (int) (temp ^ (temp >>> 32));
237         return result;
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     @SuppressWarnings("checkstyle:needbraces")
243     public boolean equals(final Object obj)
244     {
245         if (this == obj)
246             return true;
247         if (obj == null)
248             return false;
249         if (getClass() != obj.getClass())
250             return false;
251         LineSegment2d../../org/djutils/draw/line/LineSegment2d.html#LineSegment2d">LineSegment2d other = (LineSegment2d) obj;
252         if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
253             return false;
254         if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
255             return false;
256         if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
257             return false;
258         if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
259             return false;
260         return true;
261     }
262 
263 }