1 package org.djutils.draw.point;
2
3 import java.util.Arrays;
4 import java.util.Iterator;
5 import java.util.Locale;
6
7 import org.djutils.draw.Direction3d;
8 import org.djutils.draw.DrawRuntimeException;
9 import org.djutils.draw.Drawable3d;
10 import org.djutils.draw.bounds.Bounds3d;
11 import org.djutils.exceptions.Throw;
12
13
14
15
16
17
18
19
20
21
22
23
24 public class Point3d implements Drawable3d, Point<Point3d>
25 {
26
27 private static final long serialVersionUID = 20201201L;
28
29
30 @SuppressWarnings("checkstyle:visibilitymodifier")
31 public final double x;
32
33
34 @SuppressWarnings("checkstyle:visibilitymodifier")
35 public final double y;
36
37
38 @SuppressWarnings("checkstyle:visibilitymodifier")
39 public final double z;
40
41
42
43
44
45
46
47
48 public Point3d(final double x, final double y, final double z)
49 {
50 Throw.whenNaN(x, "x");
51 Throw.whenNaN(y, "y");
52 Throw.whenNaN(z, "z");
53 this.x = x;
54 this.y = y;
55 this.z = z;
56 }
57
58
59
60
61
62
63
64
65 public Point3d(final double[] xyz) throws NullPointerException, IllegalArgumentException
66 {
67 this(checkLengthIsThree(Throw.whenNull(xyz, "xyz"))[0], xyz[1], xyz[2]);
68 }
69
70
71
72
73
74
75
76
77 public Point3d(final Point2d point, final double z)
78 {
79 Throw.whenNull(point, "point");
80 Throw.whenNaN(z, "z");
81 this.x = point.x;
82 this.y = point.y;
83 this.z = z;
84 }
85
86
87
88
89
90
91
92
93
94 public Point3d(final java.awt.geom.Point2D point, final double z)
95 {
96 Throw.whenNull(point, "point");
97 Throw.whenNaN(point.getX(), "point.getX()");
98 Throw.whenNaN(point.getY(), "point.getY()");
99 Throw.whenNaN(z, "z");
100 this.x = point.getX();
101 this.y = point.getY();
102 this.z = z;
103 }
104
105
106
107
108
109
110
111
112 private static double[] checkLengthIsThree(final double[] xyz)
113 {
114 Throw.when(xyz.length != 3, IllegalArgumentException.class, "Length of xyz-array must be 3");
115 return xyz;
116 }
117
118 @Override
119 public final double getX()
120 {
121 return this.x;
122 }
123
124 @Override
125 public final double getY()
126 {
127 return this.y;
128 }
129
130
131
132
133
134 public final double getZ()
135 {
136 return this.z;
137 }
138
139 @Override
140 public double distanceSquared(final Point3d otherPoint) throws NullPointerException
141 {
142 Throw.whenNull(otherPoint, "otherPoint");
143 double dx = this.x - otherPoint.x;
144 double dy = this.y - otherPoint.y;
145 double dz = this.z - otherPoint.z;
146 return dx * dx + dy * dy + dz * dz;
147 }
148
149 @Override
150 public double distance(final Point3d otherPoint) throws NullPointerException
151 {
152 Throw.whenNull(otherPoint, "otherPoint");
153 return Math.sqrt(distanceSquared(otherPoint));
154 }
155
156 @Override
157 public int size()
158 {
159 return 1;
160 }
161
162 @Override
163 public Iterator<Point3d> iterator()
164 {
165 return Arrays.stream(new Point3d[] {this}).iterator();
166 }
167
168 @Override
169 public Point2d project() throws DrawRuntimeException
170 {
171 return new Point2d(this.x, this.y);
172 }
173
174
175
176
177
178
179
180
181 public Point3d translate(final double dX, final double dY) throws ArithmeticException
182 {
183 Throw.whenNaN(dX, "dX");
184 Throw.whenNaN(dY, "dY");
185 return new Point3d(this.x + dX, this.y + dY, this.z);
186 }
187
188
189
190
191
192
193
194
195
196 public Point3d translate(final double dX, final double dY, final double dZ)
197 {
198 Throw.whenNaN(dX, "dX");
199 Throw.whenNaN(dY, "dY");
200 Throw.whenNaN(dZ, "dZ");
201 return new Point3d(this.x + dX, this.y + dY, this.z + dZ);
202 }
203
204 @Override
205 public Point3d scale(final double factor)
206 {
207 Throw.whenNaN(factor, "factor");
208 return new Point3d(this.x * factor, this.y * factor, this.z * factor);
209 }
210
211 @Override
212 public Point3d neg()
213 {
214 return scale(-1.0);
215 }
216
217 @Override
218 public Point3d abs()
219 {
220 return new Point3d(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
221 }
222
223 @Override
224 public Point3d normalize() throws DrawRuntimeException
225 {
226 double length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
227 Throw.when(length == 0.0, DrawRuntimeException.class, "cannot normalize (0.0, 0.0, 0.0)");
228 return this.scale(1.0 / length);
229 }
230
231 @Override
232 public Point3d interpolate(final Point3d point, final double fraction)
233 {
234 Throw.whenNull(point, "point");
235 Throw.whenNaN(fraction, "fraction");
236 return new Point3d((1.0 - fraction) * this.x + fraction * point.x, (1.0 - fraction) * this.y + fraction * point.y,
237 (1.0 - fraction) * this.z + fraction * point.z);
238
239 }
240
241 @Override
242 public boolean epsilonEquals(final Point3d otherPoint, final double epsilon)
243 {
244 Throw.whenNull(otherPoint, "otherPoint");
245 if (Math.abs(this.x - otherPoint.x) > epsilon)
246 {
247 return false;
248 }
249 if (Math.abs(this.y - otherPoint.y) > epsilon)
250 {
251 return false;
252 }
253 if (Math.abs(this.z - otherPoint.z) > epsilon)
254 {
255 return false;
256 }
257 return true;
258 }
259
260 @Override
261 public Bounds3d getBounds()
262 {
263 return new Bounds3d(this);
264 }
265
266
267
268
269
270
271
272 public Direction3d directionTo(final Point3d otherPoint)
273 {
274 return new Direction3d(Math.atan2(Math.hypot(otherPoint.x - this.x, otherPoint.y - this.y), otherPoint.z - this.z),
275 Math.atan2(otherPoint.y - this.y, otherPoint.x - this.x));
276 }
277
278 @Override
279 public final Point3d closestPointOnSegment(final Point3d segmentPoint1, final Point3d segmentPoint2)
280 {
281 Throw.whenNull(segmentPoint1, "segmentPoint1");
282 Throw.whenNull(segmentPoint2, "segmentPoint2");
283 return closestPointOnSegment(segmentPoint1.x, segmentPoint1.y, segmentPoint1.z, segmentPoint2.x, segmentPoint2.y,
284 segmentPoint2.z);
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 @SuppressWarnings("checkstyle:parameternumber")
308 public Point3d closestPointOnLine(final double p1X, final double p1Y, final double p1Z, final double p2X, final double p2Y,
309 final double p2Z, final Boolean lowLimitHandling, final Boolean highLimitHandling)
310 {
311 double fraction = fractionalPositionOnLine(p1X, p1Y, p1Z, p2X, p2Y, p2Z, lowLimitHandling, highLimitHandling);
312 if (Double.isNaN(fraction))
313 {
314 return null;
315 }
316 if (fraction == 1.0)
317 {
318 return new Point3d(p2X, p2Y, p2Z);
319 }
320 return new Point3d(p1X + fraction * (p2X - p1X), p1Y + fraction * (p2Y - p1Y), p1Z + fraction * (p2Z - p1Z));
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 @SuppressWarnings("checkstyle:parameternumber")
344 public double fractionalPositionOnLine(final double p1X, final double p1Y, final double p1Z, final double p2X,
345 final double p2Y, final double p2Z, final Boolean lowLimitHandling, final Boolean highLimitHandling)
346 {
347 double dX = p2X - p1X;
348 double dY = p2Y - p1Y;
349 double dZ = p2Z - p1Z;
350 Throw.whenNaN(dX, "dX");
351 Throw.whenNaN(dY, "dY");
352 Throw.whenNaN(dZ, "dZ");
353 if (0 == dX && 0 == dY && 0 == dZ)
354 {
355 return 0.0;
356 }
357 double fraction = ((this.x - p1X) * dX + (this.y - p1Y) * dY + (this.z - p1Z) * dZ) / (dX * dX + dY * dY + dZ * dZ);
358 if (fraction < 0.0)
359 {
360 if (lowLimitHandling == null)
361 {
362 return Double.NaN;
363 }
364 if (lowLimitHandling)
365 {
366 fraction = 0.0;
367 }
368 }
369 else if (fraction > 1.0)
370 {
371 if (highLimitHandling == null)
372 {
373 return Double.NaN;
374 }
375 if (highLimitHandling)
376 {
377 fraction = 1.0;
378 }
379 }
380 return fraction;
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398 public final Point3d closestPointOnSegment(final double p1X, final double p1Y, final double p1Z, final double p2X,
399 final double p2Y, final double p2Z)
400 {
401 return closestPointOnLine(p1X, p1Y, p1Z, p2X, p2Y, p2Z, true, true);
402 }
403
404 @Override
405 public final Point3d closestPointOnLine(final Point3d linePoint1, final Point3d linePoint2)
406 {
407 Throw.whenNull(linePoint1, "linePoint1");
408 Throw.whenNull(linePoint2, "linePoint2");
409 return closestPointOnLine(linePoint1.x, linePoint1.y, linePoint1.z, linePoint2.x, linePoint2.y, linePoint2.z);
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425 public final Point3d closestPointOnLine(final double p1X, final double p1Y, final double p1Z, final double p2X,
426 final double p2Y, final double p2Z)
427 {
428 Throw.when(p1X == p2X && p1Y == p2Y && p1Z == p2Z, DrawRuntimeException.class, "degenerate line not allowed");
429 return closestPointOnLine(p1X, p1Y, p1Z, p2X, p2Y, p2Z, false, false);
430 }
431
432
433
434
435
436 final double horizontalDirection()
437 {
438 return Math.atan2(this.y, this.x);
439 }
440
441
442
443
444
445
446
447 final double horizontalDirection(final Point3d otherPoint)
448 {
449 Throw.whenNull(otherPoint, "otherPoint");
450 return Math.atan2(otherPoint.y - this.y, otherPoint.x - this.x);
451 }
452
453
454
455
456
457
458
459 final double verticalDirection(final Point3d otherPoint)
460 {
461 Throw.whenNull(otherPoint, "otherPoint");
462 return Math.atan2(Math.hypot(otherPoint.y - this.y, otherPoint.x - this.x), otherPoint.z - this.z);
463 }
464
465
466
467
468
469
470
471 final double horizontalDistanceSquared(final Point3d otherPoint)
472 {
473 Throw.whenNull(otherPoint, "otherPoint");
474 double dx = this.x - otherPoint.x;
475 double dy = this.y - otherPoint.y;
476 return dx * dx + dy * dy;
477 }
478
479
480
481
482
483
484
485 final double horizontalDistance(final Point3d otherPoint)
486 {
487 return Math.sqrt(horizontalDistanceSquared(otherPoint));
488 }
489
490 @Override
491 @SuppressWarnings("checkstyle:designforextension")
492 public String toString()
493 {
494 return toString("%f");
495 }
496
497 @Override
498 public String toString(final String doubleFormat, final boolean doNotIncludeClassName)
499 {
500 String format = String.format("%1$s[x=%2$s, y=%2$s, z=%2$s]", doNotIncludeClassName ? "" : "Point3d ", doubleFormat);
501 return String.format(Locale.US, format, this.x, this.y, this.z);
502 }
503
504 @Override
505 public int hashCode()
506 {
507 final int prime = 31;
508 int result = 1;
509 long temp;
510 temp = Double.doubleToLongBits(this.x);
511 result = prime * result + (int) (temp ^ (temp >>> 32));
512 temp = Double.doubleToLongBits(this.y);
513 result = prime * result + (int) (temp ^ (temp >>> 32));
514 temp = Double.doubleToLongBits(this.z);
515 result = prime * result + (int) (temp ^ (temp >>> 32));
516 return result;
517 }
518
519 @SuppressWarnings("checkstyle:needbraces")
520 @Override
521 public boolean equals(final Object obj)
522 {
523 if (this == obj)
524 return true;
525 if (obj == null)
526 return false;
527 if (getClass() != obj.getClass())
528 return false;
529 Point3d other = (Point3d) obj;
530 if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x))
531 return false;
532 if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y))
533 return false;
534 if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z))
535 return false;
536 return true;
537 }
538
539 }