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