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