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.Space3d;
10 import org.djutils.draw.bounds.Bounds3d;
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 public class LineSegment3d implements Drawable3d, LineSegment<Point3d, Ray3d, Space3d>
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 startZ;
40
41
42 @SuppressWarnings("checkstyle:visibilitymodifier")
43 public final double endX;
44
45
46 @SuppressWarnings("checkstyle:visibilitymodifier")
47 public final double endY;
48
49
50 @SuppressWarnings("checkstyle:visibilitymodifier")
51 public final double endZ;
52
53
54
55
56
57
58
59
60
61
62
63 public LineSegment3d(final double startX, final double startY, final double startZ, final double endX, final double endY,
64 final double endZ) throws DrawRuntimeException
65 {
66 Throw.when(startX == endX && startY == endY && startZ == endZ, DrawRuntimeException.class,
67 "Start and end may not be equal");
68 this.startX = startX;
69 this.startY = startY;
70 this.startZ = startZ;
71 this.endX = endX;
72 this.endY = endY;
73 this.endZ = endZ;
74 }
75
76
77
78
79
80
81
82
83
84
85 public LineSegment3d(final Point3d start, final double endX, final double endY, final double endZ)
86 throws NullPointerException, DrawRuntimeException
87 {
88 this(Throw.whenNull(start, "start point may not be null").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 throws NullPointerException, DrawRuntimeException
102 {
103 this(startX, startY, startZ, Throw.whenNull(end, "end point may not be null").x, end.y, end.z);
104 }
105
106
107
108
109
110
111
112
113 public LineSegment3d(final Point3dd.html#Point3d">Point3d start, final Point3d end) throws NullPointerException, DrawRuntimeException
114 {
115 this(Throw.whenNull(start, "start point may not be null").x, start.y, start.z,
116 Throw.whenNull(end, "end point may not be null").x, end.y, end.z);
117 }
118
119
120 @Override
121 public Point3d getStartPoint()
122 {
123 return new Point3d(this.startX, this.startY, this.startZ);
124 }
125
126
127 @Override
128 public Point3d getEndPoint()
129 {
130 return new Point3d(this.endX, this.endY, this.endZ);
131 }
132
133
134 @Override
135 public double getLength()
136 {
137
138 double dX = this.endX - this.startX;
139 double dY = this.endY - this.startY;
140 double dZ = this.endZ - this.startZ;
141 return Math.sqrt(dX * dX + dY * dY + dZ * dZ);
142 }
143
144
145 @Override
146 public Iterator<? extends Point3d> getPoints()
147 {
148 return Arrays.stream(new Point3d[] { getStartPoint(), getEndPoint() }).iterator();
149 }
150
151
152 @Override
153 public int size()
154 {
155 return 2;
156 }
157
158
159 @Override
160 public Bounds3d getBounds()
161 {
162 return new Bounds3d(Math.min(this.startX, this.endX), Math.max(this.startX, this.endX),
163 Math.min(this.startY, this.endY), Math.max(this.startY, this.endY), Math.min(this.startZ, this.endZ),
164 Math.max(this.startZ, this.endZ));
165 }
166
167
168 @Override
169 public LineSegment2d project() throws DrawRuntimeException
170 {
171 return new LineSegment2d(this.startX, this.startY, this.endX, this.endY);
172 }
173
174
175 @Override
176 public Ray3d getLocationExtended(final double position) throws DrawRuntimeException
177 {
178 Throw.when(Double.isNaN(position) || Double.isInfinite(position), DrawRuntimeException.class,
179 "position must be finite");
180 double dX = this.endX - this.startX;
181 double dY = this.endY - this.startY;
182 double dZ = this.endZ - this.startZ;
183 double length = Math.sqrt(dX * dX + dY * dY + dZ * dZ);
184 return new Ray3d(this.startX + position * dX / length, this.startY + position * dY / length,
185 this.startZ + position * dZ / length, Math.atan2(dY, dX), Math.atan2(dZ, Math.hypot(dX, dY)));
186 }
187
188
189 @Override
190 public Point3d>Point3d closestPointOnSegment(final Point3d point)
191 {
192 Throw.whenNull(point, "point may not be null");
193 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, true, true);
194 }
195
196
197 @Override
198 public Point3dt3d">Point3d projectOrthogonal(final Point3d point) throws NullPointerException
199 {
200 Throw.whenNull(point, "point may not be null");
201 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null, null);
202 }
203
204
205 @Override
206 public Point3dnt3d projectOrthogonalExtended(final Point3d point) throws NullPointerException
207 {
208 Throw.whenNull(point, "point may not be null");
209 return point.closestPointOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
210 }
211
212
213 @Override
214 public double projectOrthogonalFractional(final Point3d point) throws NullPointerException
215 {
216 Throw.whenNull(point, "point may not be null");
217 return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, null,
218 null);
219 }
220
221
222 @Override
223 public double projectOrthogonalFractionalExtended(final Point3d point) throws NullPointerException
224 {
225 Throw.whenNull(point, "point may not be null");
226 return point.fractionalPositionOnLine(this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ, false,
227 false);
228 }
229
230
231 @Override
232 public String toString()
233 {
234 return toString("%f", false);
235 }
236
237
238 @Override
239 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
240 {
241 String format = String.format("%1$s[startX=%2$s, startY=%2$s, startZ=%2$s - endX=%2%s, endY=%2$s, endZ=%2$s]",
242 doNotIncludeClassName ? "" : "LineSegment3d ", doubleFormat);
243 return String.format(Locale.US, format, this.startX, this.startY, this.startZ, this.endX, this.endY, this.endZ);
244 }
245
246
247 @Override
248 public String toExcel()
249 {
250 return this.startX + "\t" + this.startY + "\t" + this.startZ + "\n" + this.endX + "\t" + this.endY + "\t" + this.endZ
251 + "\n";
252 }
253
254
255 @Override
256 public int hashCode()
257 {
258 final int prime = 31;
259 int result = 1;
260 long temp;
261 temp = Double.doubleToLongBits(this.endX);
262 result = prime * result + (int) (temp ^ (temp >>> 32));
263 temp = Double.doubleToLongBits(this.endY);
264 result = prime * result + (int) (temp ^ (temp >>> 32));
265 temp = Double.doubleToLongBits(this.endZ);
266 result = prime * result + (int) (temp ^ (temp >>> 32));
267 temp = Double.doubleToLongBits(this.startX);
268 result = prime * result + (int) (temp ^ (temp >>> 32));
269 temp = Double.doubleToLongBits(this.startY);
270 result = prime * result + (int) (temp ^ (temp >>> 32));
271 temp = Double.doubleToLongBits(this.startZ);
272 result = prime * result + (int) (temp ^ (temp >>> 32));
273 return result;
274 }
275
276
277 @Override
278 @SuppressWarnings("checkstyle:needbraces")
279 public boolean equals(final Object obj)
280 {
281 if (this == obj)
282 return true;
283 if (obj == null)
284 return false;
285 if (getClass() != obj.getClass())
286 return false;
287 LineSegment3d../../org/djutils/draw/line/LineSegment3d.html#LineSegment3d">LineSegment3d other = (LineSegment3d) obj;
288 if (Double.doubleToLongBits(this.endX) != Double.doubleToLongBits(other.endX))
289 return false;
290 if (Double.doubleToLongBits(this.endY) != Double.doubleToLongBits(other.endY))
291 return false;
292 if (Double.doubleToLongBits(this.endZ) != Double.doubleToLongBits(other.endZ))
293 return false;
294 if (Double.doubleToLongBits(this.startX) != Double.doubleToLongBits(other.startX))
295 return false;
296 if (Double.doubleToLongBits(this.startY) != Double.doubleToLongBits(other.startY))
297 return false;
298 if (Double.doubleToLongBits(this.startZ) != Double.doubleToLongBits(other.startZ))
299 return false;
300 return true;
301 }
302
303 }