1 package org.opentrafficsim.core.geometry;
2
3 import java.awt.geom.Point2D;
4 import java.io.Serializable;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import org.djunits.unit.DirectionUnit;
9 import org.djunits.unit.LengthUnit;
10 import org.djunits.unit.Unit;
11 import org.djunits.value.storage.StorageType;
12 import org.djunits.value.vdouble.scalar.Direction;
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.djunits.value.vdouble.scalar.base.DoubleScalarInterface;
15 import org.djunits.value.vdouble.vector.base.DoubleVector;
16 import org.djunits.value.vdouble.vector.base.DoubleVectorInterface;
17 import org.djutils.draw.point.Point3d;
18 import org.locationtech.jts.geom.Coordinate;
19 import org.locationtech.jts.geom.Point;
20
21 import nl.tudelft.simulation.dsol.animation.Locatable;
22 import nl.tudelft.simulation.language.d3.CartesianPoint;
23
24
25
26
27
28
29
30
31
32
33
34
35 public class OtsPoint3d implements Locatable, Serializable
36 {
37
38 private static final long serialVersionUID = 20150722L;
39
40
41 @SuppressWarnings("checkstyle:visibilitymodifier")
42 public final double x;
43
44
45 @SuppressWarnings("checkstyle:visibilitymodifier")
46 public final double y;
47
48
49 @SuppressWarnings("checkstyle:visibilitymodifier")
50 public final double z;
51
52
53
54
55
56
57
58 public OtsPoint3d(final double x, final double y, final double z)
59 {
60 this.x = x;
61 this.y = y;
62 this.z = z;
63 }
64
65
66
67
68 public OtsPoint3d(final double[] xyz)
69 {
70 this(xyz[0], xyz[1], (xyz.length > 2) ? xyz[2] : 0.0);
71 }
72
73
74
75
76 public OtsPoint3d(final OtsPoint3d point)
77 {
78 this(point.x, point.y, point.z);
79 }
80
81
82
83
84
85 public OtsPoint3d(final Point3d point)
86 {
87 this(point.x, point.y, point.z);
88 }
89
90
91
92
93
94 public OtsPoint3d(final CartesianPoint point)
95 {
96 this(point.x, point.y, point.z);
97 }
98
99
100
101
102
103 public OtsPoint3d(final DirectedPoint point)
104 {
105 this(point.x, point.y, point.z);
106 }
107
108
109
110
111
112 public OtsPoint3d(final Point2D point2d)
113 {
114 this(point2d.getX(), point2d.getY(), 0.0);
115 }
116
117
118
119
120
121 public OtsPoint3d(final Coordinate coordinate)
122 {
123 this(coordinate.x, coordinate.y, Double.isNaN(coordinate.getZ()) ? 0.0 : coordinate.getZ());
124 }
125
126
127
128
129
130 public OtsPoint3d(final Point point)
131 {
132 this(point.getX(), point.getY(), 0.0);
133 }
134
135
136
137
138
139
140 public OtsPoint3d(final double x, final double y)
141 {
142 this(x, y, 0.0);
143 }
144
145
146
147
148
149
150
151
152 public static OtsPoint3d interpolate(final double ratio, final OtsPoint3d zeroValue, final OtsPoint3d oneValue)
153 {
154 double complement = 1 - ratio;
155 return new OtsPoint3d(complement * zeroValue.x + ratio * oneValue.x, complement * zeroValue.y + ratio * oneValue.y,
156 complement * zeroValue.z + ratio * oneValue.z);
157 }
158
159
160
161
162
163
164
165
166
167
168
169 @Deprecated
170 public static OtsPoint3d intersectionOfLineSegmentsDumb(final OtsPoint3d line1P1, final OtsPoint3d line1P2,
171 final OtsPoint3d line2P1, final OtsPoint3d line2P2)
172 {
173 double denominator =
174 (line2P2.y - line2P1.y) * (line1P2.x - line1P1.x) - (line2P2.x - line2P1.x) * (line1P2.y - line1P1.y);
175 if (denominator == 0f)
176 {
177 return null;
178 }
179 double uA = ((line2P2.x - line2P1.x) * (line1P1.y - line2P1.y) - (line2P2.y - line2P1.y) * (line1P1.x - line2P1.x))
180 / denominator;
181 if ((uA < 0f) || (uA > 1f))
182 {
183 return null;
184 }
185 double uB = ((line1P2.x - line1P1.x) * (line1P1.y - line2P1.y) - (line1P2.y - line1P1.y) * (line1P1.x - line2P1.x))
186 / denominator;
187 if (uB < 0 || uB > 1)
188 {
189 return null;
190 }
191 return new OtsPoint3d(line1P1.x + uA * (line1P2.x - line1P1.x), line1P1.y + uA * (line1P2.y - line1P1.y), 0);
192 }
193
194
195
196
197
198
199
200
201
202
203 public static OtsPoint3d intersectionOfLineSegments(final OtsPoint3d line1P1, final OtsPoint3d line1P2,
204 final OtsPoint3d line2P1, final OtsPoint3d line2P2)
205 {
206 double l1p1x = line1P1.x;
207 double l1p1y = line1P1.y;
208 double l1p2x = line1P2.x - l1p1x;
209 double l1p2y = line1P2.y - l1p1y;
210 double l2p1x = line2P1.x - l1p1x;
211 double l2p1y = line2P1.y - l1p1y;
212 double l2p2x = line2P2.x - l1p1x;
213 double l2p2y = line2P2.y - l1p1y;
214 double denominator = (l2p2y - l2p1y) * l1p2x - (l2p2x - l2p1x) * l1p2y;
215 if (denominator == 0.0)
216 {
217 return null;
218 }
219 double uA = ((l2p2x - l2p1x) * (-l2p1y) - (l2p2y - l2p1y) * (-l2p1x)) / denominator;
220
221 if ((uA < 0.0) || (uA > 1.0))
222 {
223 return null;
224 }
225 double uB = (l1p2y * l2p1x - l1p2x * l2p1y) / denominator;
226
227 if (uB < 0.0 || uB > 1.0)
228 {
229 return null;
230 }
231 return new OtsPoint3d(line1P1.x + uA * l1p2x, line1P1.y + uA * l1p2y, 0);
232 }
233
234
235
236
237
238
239
240
241
242
243 @Deprecated
244 public static OtsPoint3d intersectionOfLinesDumb(final OtsPoint3d line1P1, final OtsPoint3d line1P2,
245 final OtsPoint3d line2P1, final OtsPoint3d line2P2)
246 {
247 double determinant =
248 (line1P1.x - line1P2.x) * (line2P1.y - line2P2.y) - (line1P1.y - line1P2.y) * (line2P1.x - line2P2.x);
249 if (Math.abs(determinant) < 0.0000001)
250 {
251 return null;
252 }
253 return new OtsPoint3d(
254 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.x - line2P2.x)
255 - (line1P1.x - line1P2.x) * (line2P1.x * line2P2.y - line2P1.y * line2P2.x)) / determinant,
256 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.y - line2P2.y)
257 - (line1P1.y - line1P2.y) * (line2P1.x * line2P2.y - line2P1.y * line2P2.x)) / determinant);
258 }
259
260
261
262
263
264
265
266
267
268
269 public static OtsPoint3d intersectionOfLines(final OtsPoint3d line1P1, final OtsPoint3d line1P2, final OtsPoint3d line2P1,
270 final OtsPoint3d line2P2)
271 {
272 double l1p1x = line1P1.x;
273 double l1p1y = line1P1.y;
274 double l1p2x = line1P2.x - l1p1x;
275 double l1p2y = line1P2.y - l1p1y;
276 double l2p1x = line2P1.x - l1p1x;
277 double l2p1y = line2P1.y - l1p1y;
278 double l2p2x = line2P2.x - l1p1x;
279 double l2p2y = line2P2.y - l1p1y;
280 double determinant = (0 - l1p2x) * (l2p1y - l2p2y) - (0 - l1p2y) * (l2p1x - l2p2x);
281 if (Math.abs(determinant) < 0.0000001)
282 {
283 return null;
284 }
285 return new OtsPoint3d(l1p1x + (l1p2x * (l2p1x * l2p2y - l2p1y * l2p2x)) / determinant,
286 l1p1y + (l1p2y * (l2p1x * l2p2y - l2p1y * l2p2x)) / determinant);
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300 public final OtsPoint3d closestPointOnSegment(final OtsPoint3d segmentPoint1, final OtsPoint3d segmentPoint2)
301 {
302 double dX = segmentPoint2.x - segmentPoint1.x;
303 double dY = segmentPoint2.y - segmentPoint1.y;
304 if ((0 == dX) && (0 == dY))
305 {
306 return segmentPoint1;
307 }
308 final double u = ((this.x - segmentPoint1.x) * dX + (this.y - segmentPoint1.y) * dY) / (dX * dX + dY * dY);
309 if (u < 0)
310 {
311 return segmentPoint1;
312 }
313 else if (u > 1)
314 {
315 return segmentPoint2;
316 }
317 else
318 {
319 return interpolate(u, segmentPoint1, segmentPoint2);
320 }
321 }
322
323
324
325
326
327
328
329
330 private OtsPoint3d internalClosestPointOnLine(final OtsLine3d line, final boolean useHorizontalDistance)
331 {
332 OtsPoint3d prevPoint = null;
333 double distance = Double.MAX_VALUE;
334 OtsPoint3d result = null;
335 for (OtsPoint3d nextPoint : line.getPoints())
336 {
337 if (null != prevPoint)
338 {
339 OtsPoint3d closest = closestPointOnSegment(prevPoint, nextPoint);
340 double thisDistance = useHorizontalDistance ? horizontalDistanceSI(closest) : distanceSI(closest);
341 if (thisDistance < distance)
342 {
343 result = closest;
344 distance = thisDistance;
345 }
346 }
347 prevPoint = nextPoint;
348 }
349 return result;
350 }
351
352
353
354
355
356
357 public final OtsPoint3d closestPointOnLine(final OtsLine3d line)
358 {
359 return internalClosestPointOnLine(line, false);
360 }
361
362
363
364
365
366
367
368 public final OtsPoint3d closestPointOnLine2D(final OtsLine3d line)
369 {
370 return internalClosestPointOnLine(line, true);
371 }
372
373
374
375
376
377 public final OtsPoint3d normalize()
378 {
379 double length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
380 return this.translate(length);
381 }
382
383
384
385
386
387
388 public final OtsPoint3d translate(final double factor)
389 {
390 return new OtsPoint3d(this.x / factor, this.y / factor, this.z / factor);
391 }
392
393
394
395
396
397
398
399
400
401
402 public static final List<OtsPoint3d> circleCenter(final OtsPoint3d point1, final OtsPoint3d point2, final double radius)
403 {
404 List<OtsPoint3d> result = new ArrayList<>();
405 OtsPoint3d m = interpolate(0.5, point1, point2);
406 double h = point1.distanceSI(m);
407 if (radius < h)
408 {
409 return result;
410 }
411 if (radius == h)
412 {
413 result.add(m);
414 return result;
415 }
416 OtsPoint3d p = new OtsPoint3d(point2.y - point1.y, point1.x - point2.x).normalize();
417 double d = Math.sqrt(radius * radius - h * h);
418 d = Math.sqrt(radius * radius - h * h);
419 result.add(new OtsPoint3d(m.x + d * p.x, m.y + d * p.y, m.z));
420 result.add(new OtsPoint3d(m.x - d * p.x, m.y - d * p.y, m.z));
421 return result;
422 }
423
424
425
426
427
428
429
430
431
432 public static final List<OtsPoint3d> circleIntersections(final OtsPoint3d center1, final double radius1,
433 final OtsPoint3d center2, final double radius2)
434 {
435 List<OtsPoint3d> center = new ArrayList<>();
436 OtsPoint3d m = interpolate(radius1 / (radius1 + radius2), center1, center2);
437 double h = center1.distanceSI(m);
438 if (radius1 < h)
439 {
440 return center;
441 }
442 if (radius1 == h)
443 {
444 center.add(m);
445 return center;
446 }
447 OtsPoint3d p = new OtsPoint3d(center2.y - center1.y, center1.x - center2.x).normalize();
448 double d = Math.sqrt(radius1 * radius1 - h * h);
449 center.add(new OtsPoint3d(m.x + d * p.x, m.y + d * p.y, m.z));
450 center.add(new OtsPoint3d(m.x - d * p.x, m.y - d * p.y, m.z));
451 return center;
452 }
453
454
455
456
457
458 public final double distanceSI(final OtsPoint3d point)
459 {
460 double dx = point.x - this.x;
461 double dy = point.y - this.y;
462 double dz = point.z - this.z;
463
464 return Math.sqrt(dx * dx + dy * dy + dz * dz);
465 }
466
467
468
469
470
471 public final double horizontalDistanceSI(final OtsPoint3d point)
472 {
473 double dx = point.x - this.x;
474 double dy = point.y - this.y;
475
476 return Math.sqrt(dx * dx + dy * dy);
477 }
478
479
480
481
482
483 public final Length horizontalDistance(final OtsPoint3d point)
484 {
485 return new Length(horizontalDistanceSI(point), LengthUnit.SI);
486 }
487
488
489
490
491
492
493 public final Length distance(final OtsPoint3d point)
494 {
495 return new Length(distanceSI(point), LengthUnit.SI);
496 }
497
498
499
500
501
502
503 public final double horizontalDirectionSI(final OtsPoint3d point)
504 {
505 return Math.atan2(point.y - this.y, point.x - this.x);
506 }
507
508
509
510
511
512
513 public final Direction horizontalDirection(final OtsPoint3d point)
514 {
515 return Direction.instantiateSI(Math.atan2(point.y - this.y, point.x - this.x));
516 }
517
518
519
520
521 public final Coordinate getCoordinate()
522 {
523 return new Coordinate(this.x, this.y, this.z);
524 }
525
526
527
528
529 public final DirectedPoint getDirectedPoint()
530 {
531 return new DirectedPoint(this.x, this.y, this.z);
532 }
533
534
535
536
537 public final Point2D getPoint2D()
538 {
539 return new Point2D.Double(this.x, this.y);
540 }
541
542
543 @Override
544 public final DirectedPoint getLocation()
545 {
546 return getDirectedPoint();
547 }
548
549
550
551
552 @Override
553 public final Bounds getBounds()
554 {
555 return new Bounds();
556 }
557
558
559
560
561
562
563
564
565
566 public <U extends Unit<U>, S extends DoubleScalarInterface<U, S>,
567 V extends DoubleVectorInterface<U, S, V>> V doubleVector(final U unit)
568 {
569 return DoubleVector.instantiate(new double[] {this.x, this.y, this.z}, unit, StorageType.DENSE);
570 }
571
572
573
574
575
576
577
578
579
580 public static Direction direction(final DirectedPoint directedPoint, final DirectionUnit directionUnit)
581 {
582 return new Direction(directedPoint.getRotZ(), directionUnit);
583 }
584
585
586 @Override
587 @SuppressWarnings("checkstyle:designforextension")
588 public String toString()
589 {
590 return String.format("(%.3f,%.3f,%.3f)", this.x, this.y, this.z);
591 }
592
593
594 @Override
595 @SuppressWarnings("checkstyle:designforextension")
596 public int hashCode()
597 {
598 final int prime = 31;
599 int result = 1;
600 long temp;
601 temp = Double.doubleToLongBits(this.x);
602 result = prime * result + (int) (temp ^ (temp >>> 32));
603 temp = Double.doubleToLongBits(this.y);
604 result = prime * result + (int) (temp ^ (temp >>> 32));
605 temp = Double.doubleToLongBits(this.z);
606 result = prime * result + (int) (temp ^ (temp >>> 32));
607 return result;
608 }
609
610
611 @Override
612 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
613 public boolean equals(final Object obj)
614 {
615 if (this == obj)
616 return true;
617 if (obj == null)
618 return false;
619 if (getClass() != obj.getClass())
620 return false;
621 OtsPoint3d other = (OtsPoint3d) obj;
622 if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x))
623 return false;
624 if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y))
625 return false;
626 if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z))
627 return false;
628 return true;
629 }
630
631 }