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.DirectedPoint2d;
11 import org.djutils.draw.point.Point2d;
12 import org.djutils.exceptions.Throw;
13
14
15
16
17
18
19
20
21
22
23
24 public class LineSegment2d implements Drawable2d, LineSegment<Point2d, DirectedPoint2d>
25 {
26
27 private static final long serialVersionUID = 20210121L;
28
29
30 @SuppressWarnings("checkstyle:visibilitymodifier")
31 public final double startX;
32
33
34 @SuppressWarnings("checkstyle:visibilitymodifier")
35 public final double startY;
36
37
38 @SuppressWarnings("checkstyle:visibilitymodifier")
39 public final double endX;
40
41
42 @SuppressWarnings("checkstyle:visibilitymodifier")
43 public final double endY;
44
45
46
47
48
49
50
51
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
65
66
67
68
69
70
71 public LineSegment2d(final Point2d start, final double endX, final double endY)
72 throws NullPointerException, DrawRuntimeException
73 {
74 this(Throw.whenNull(start, "start").x, start.y, endX, endY);
75 }
76
77
78
79
80
81
82
83
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").x, end.y);
89 }
90
91
92
93
94
95
96
97
98 public LineSegment2d(final Point2d start, final Point2d end) throws NullPointerException, DrawRuntimeException
99 {
100 this(Throw.whenNull(start, "start").x, start.y, Throw.whenNull(end, "end").x, end.y);
101 }
102
103 @Override
104 public Point2d getStartPoint()
105 {
106 return new Point2d(this.startX, this.startY);
107 }
108
109 @Override
110 public Point2d getEndPoint()
111 {
112 return new Point2d(this.endX, this.endY);
113 }
114
115 @Override
116 public double getLength()
117 {
118 return Math.hypot(this.endX - this.startX, this.endY - this.startY);
119 }
120
121 @Override
122 public Iterator<? extends Point2d> getPoints()
123 {
124 return Arrays.stream(new Point2d[] {getStartPoint(), getEndPoint()}).iterator();
125 }
126
127 @Override
128 public int size()
129 {
130 return 2;
131 }
132
133 @Override
134 public Bounds2d getBounds()
135 {
136 return new Bounds2d(Math.min(this.startX, this.endX), Math.max(this.startX, this.endX),
137 Math.min(this.startY, this.endY), Math.max(this.startY, this.endY));
138 }
139
140 @Override
141 public DirectedPoint2d getLocationExtended(final double position) throws DrawRuntimeException
142 {
143 Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
144 "position must be finite");
145 double dX = this.endX - this.startX;
146 double dY = this.endY - this.startY;
147 double length = Math.hypot(dX, dY);
148 return new DirectedPoint2d(this.startX + position * dX / length, this.startY + position * dY / length,
149 Math.atan2(dY, dX));
150 }
151
152 @Override
153 public Point2d closestPointOnSegment(final Point2d point) throws NullPointerException
154 {
155 Throw.whenNull(point, "point");
156 return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, true, true);
157 }
158
159 @Override
160 public LineSegment2d reverse()
161 {
162 return new LineSegment2d(this.endX, this.endY, this.startX, this.startY);
163 }
164
165 @Override
166 public Point2d projectOrthogonal(final Point2d point) throws NullPointerException
167 {
168 Throw.whenNull(point, "point");
169 return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
170 }
171
172 @Override
173 public Point2d projectOrthogonalExtended(final Point2d point)
174 {
175 Throw.whenNull(point, "point");
176 return point.closestPointOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
177 }
178
179 @Override
180 public double projectOrthogonalFractional(final Point2d point) throws NullPointerException
181 {
182 Throw.whenNull(point, "point");
183 return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, null, null);
184 }
185
186 @Override
187 public double projectOrthogonalFractionalExtended(final Point2d point) throws NullPointerException
188 {
189 Throw.whenNull(point, "point");
190 return point.fractionalPositionOnLine(this.startX, this.startY, this.endX, this.endY, false, false);
191 }
192
193 @Override
194 public String toString()
195 {
196 return toString("%f", false);
197 }
198
199 @Override
200 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
201 {
202 String format = String.format("%1$s[startX=%2$s, startY=%2$s - endX=%2%s, endY=%2$s]",
203 doNotIncludeClassName ? "" : "LineSegment2d ", doubleFormat);
204 return String.format(Locale.US, format, this.startX, this.startY, this.endX, this.endY);
205 }
206
207 @Override
208 public int hashCode()
209 {
210 final int prime = 31;
211 int result = 1;
212 long temp;
213 temp = Double.doubleToLongBits(this.endX);
214 result = prime * result + (int) (temp ^ (temp >>> 32));
215 temp = Double.doubleToLongBits(this.endY);
216 result = prime * result + (int) (temp ^ (temp >>> 32));
217 temp = Double.doubleToLongBits(this.startX);
218 result = prime * result + (int) (temp ^ (temp >>> 32));
219 temp = Double.doubleToLongBits(this.startY);
220 result = prime * result + (int) (temp ^ (temp >>> 32));
221 return result;
222 }
223
224 @Override
225 @SuppressWarnings("checkstyle:needbraces")
226 public boolean equals(final Object obj)
227 {
228 if (this == obj)
229 return true;
230 if (obj == null)
231 return false;
232 if (getClass() != obj.getClass())
233 return false;
234 LineSegment2d other = (LineSegment2d) obj;
235 if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
236 return false;
237 if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
238 return false;
239 if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
240 return false;
241 if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
242 return false;
243 return true;
244 }
245
246 }