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 LineSegment2d reverse()
169 {
170 return new LineSegment2d(this.endX, this.endY, this.startX, this.startY);
171 }
172
173
174 @Override
175 public Point2d projectOrthogonal(final Point2d point) throws NullPointerException
176 {
177 Throw.whenNull(point, "point may not be null");
178 return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
179 }
180
181
182 @Override
183 public Point2d projectOrthogonalExtended(final Point2d point)
184 {
185 Throw.whenNull(point, "point may not be null");
186 return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
187 }
188
189
190 @Override
191 public double projectOrthogonalFractional(final Point2d point) throws NullPointerException
192 {
193 Throw.whenNull(point, "point may not be null");
194 return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
195 }
196
197
198 @Override
199 public double projectOrthogonalFractionalExtended(final Point2d point) throws NullPointerException
200 {
201 Throw.whenNull(point, "point may not be null");
202 return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
203 }
204
205
206 @Override
207 public String toString()
208 {
209 return toString("%f", false);
210 }
211
212
213 @Override
214 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
215 {
216 String format = String.format("%1$s[startX=%2$s, startY=%2$s - endX=%2%s, endY=%2$s]",
217 doNotIncludeClassName ? "" : "LineSegment2d ", doubleFormat);
218 return String.format(Locale.US, format, this.startX, this.startY, this.endX, this.endY);
219 }
220
221
222 @Override
223 public String toExcel()
224 {
225 return this.startX + "\t" + this.startY + "\n" + this.endX + "\t" + this.endY + "\n";
226 }
227
228
229
230
231
232 public String toPlot()
233 {
234 return String.format(Locale.US, "M%.3f,%.3fL%.3f,%.3f\n", this.startX, this.startY, this.endX, this.endY);
235 }
236
237
238 @Override
239 public int hashCode()
240 {
241 final int prime = 31;
242 int result = 1;
243 long temp;
244 temp = Double.doubleToLongBits(this.endX);
245 result = prime * result + (int) (temp ^ (temp >>> 32));
246 temp = Double.doubleToLongBits(this.endY);
247 result = prime * result + (int) (temp ^ (temp >>> 32));
248 temp = Double.doubleToLongBits(this.startX);
249 result = prime * result + (int) (temp ^ (temp >>> 32));
250 temp = Double.doubleToLongBits(this.startY);
251 result = prime * result + (int) (temp ^ (temp >>> 32));
252 return result;
253 }
254
255
256 @Override
257 @SuppressWarnings("checkstyle:needbraces")
258 public boolean equals(final Object obj)
259 {
260 if (this == obj)
261 return true;
262 if (obj == null)
263 return false;
264 if (getClass() != obj.getClass())
265 return false;
266 LineSegment2d other = (LineSegment2d) obj;
267 if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
268 return false;
269 if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
270 return false;
271 if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
272 return false;
273 if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
274 return false;
275 return true;
276 }
277
278 }