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