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