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
15
16
17
18
19
20
21
22
23 public class LineSegment2d implements Drawable2d, LineSegment<Point2d, Ray2d>
24 {
25
26 private static final long serialVersionUID = 20210121L;
27
28
29 @SuppressWarnings("checkstyle:visibilitymodifier")
30 public final double startX;
31
32
33 @SuppressWarnings("checkstyle:visibilitymodifier")
34 public final double startY;
35
36
37 @SuppressWarnings("checkstyle:visibilitymodifier")
38 public final double endX;
39
40
41 @SuppressWarnings("checkstyle:visibilitymodifier")
42 public final double endY;
43
44
45
46
47
48
49
50
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
64
65
66
67
68
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
78
79
80
81
82
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
92
93
94
95
96
97 public LineSegment2d(final 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
104 @Override
105 public Point2d getStartPoint()
106 {
107 return new Point2d(this.startX, this.startY);
108 }
109
110
111 @Override
112 public Point2d getEndPoint()
113 {
114 return new Point2d(this.endX, this.endY);
115 }
116
117
118 @Override
119 public double getLength()
120 {
121 return Math.hypot(this.endX - this.startX, this.endY - this.startY);
122 }
123
124
125 @Override
126 public Iterator<? extends Point2d> getPoints()
127 {
128 return Arrays.stream(new Point2d[] { getStartPoint(), getEndPoint() }).iterator();
129 }
130
131
132 @Override
133 public int size()
134 {
135 return 2;
136 }
137
138
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
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
159 @Override
160 public 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
167 @Override
168 public 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
175 @Override
176 public Point2d 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
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
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
199 @Override
200 public String toString()
201 {
202 return toString("%f", false);
203 }
204
205
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
215 @Override
216 public String toExcel()
217 {
218 return this.startX + "\t" + this.startY + "\n" + this.endX + "\t" + this.endY + "\n";
219 }
220
221
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
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 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 }