Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
ClosestPointQueries.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <optional>
6#include <type_traits>
7
17
18namespace mv::math
19{
20 namespace detail
21 {
22 template <typename T>
24 const Point3<T>& first, const Point3<T>& second) noexcept
25 {
26 return LengthSquared(first - second);
27 }
28
29 template <typename T>
31 std::conditional_t<std::is_same_v<T, float>, double, long double>;
32
33 template <typename T>
35 const Vec3<T>& first, const Vec3<T>& second) noexcept
36 {
38 return static_cast<Calculation>(first.X()) *
39 static_cast<Calculation>(second.X()) +
40 static_cast<Calculation>(first.Y()) *
41 static_cast<Calculation>(second.Y()) +
42 static_cast<Calculation>(first.Z()) *
43 static_cast<Calculation>(second.Z());
44 }
45
46 template <typename T>
56 } // namespace detail
57
58 // Orthogonal projection specialized for a unit direction; equations and
59 // parameter semantics follow Geometric Tools DistPointLine.h (BSL-1.0).
60 template <typename T>
62 const Point3<T>& point, const Line3<T>& line) noexcept
63 {
64 const T parameter =
65 Dot(line.Direction().Vector(), point - line.Origin());
66 const Point3<T> pointOnLine = line.PointAt(parameter);
70 }
71
72 // The line projection above, clamped to the ray domain [0,+infinity),
73 // follows Geometric Tools DistPointRay.h (BSL-1.0).
74 template <typename T>
76 const Point3<T>& point, const Ray3<T>& ray) noexcept
77 {
78 const T parameter =
79 std::max(Dot(ray.Direction().Vector(), point - ray.Origin()), T(0));
80 const Point3<T> pointOnRay = ray.PointAt(parameter);
84 }
85
86 // Endpoint tests and the interior projection follow Geometric Tools
87 // DistPointSegment.h (BSL-1.0); a zero-length segment returns its start.
88 template <typename T>
90 const Point3<T>& point, const Segment3<T>& segment) noexcept
91 {
92 const Vec3<T> displacement = segment.Displacement();
94 T fraction = T(0);
95 if (lengthSquared > T(0))
96 {
97 fraction = std::clamp(
99 T(0), T(1));
100 }
101 const Point3<T> pointOnSegment = segment.PointAtFraction(fraction);
105 }
106
107 // Unit-normal orthogonal projection; see Ericson, Real-Time Collision
108 // Detection (2005), section 5.1.1.
109 template <typename T>
111 const Point3<T>& point, const Plane3<T>& plane) noexcept
112 {
113 const T signedDistance = plane.SignedDistance(point);
115 point - plane.Normal().Vector() * signedDistance, signedDistance,
117 }
118
119 // Voronoi-region point/triangle query from Ericson, Real-Time Collision
120 // Detection (2005), section 5.1.5, cross-checked against Eberly,
121 // "Distance Between Point and Triangle in 3D" (1999, CC BY 4.0).
122 // Move resolves degenerate triangles by testing their three edges.
123 template <typename T>
125 const Point3<T>& point, const Triangle3<T>& triangle) noexcept
126 {
127 const Point3<T>& first = triangle.First();
128 const Point3<T>& second = triangle.Second();
129 const Point3<T>& third = triangle.Third();
130 const Vec3<T> edge01 = second - first;
131 const Vec3<T> edge02 = third - first;
132
133 if (!(LengthSquared(Cross(edge01, edge02)) > T(0)))
134 {
138 point, edge01Closest.PointOnSegment,
139 Vec3<T>(T(1) - edge01Closest.SegmentFraction,
140 edge01Closest.SegmentFraction, T(0)));
141
144 if (edge02Closest.SquaredDistance < closest.SquaredDistance)
145 {
147 point, edge02Closest.PointOnSegment,
148 Vec3<T>(T(1) - edge02Closest.SegmentFraction, T(0),
149 edge02Closest.SegmentFraction));
150 }
151
154 if (edge12Closest.SquaredDistance < closest.SquaredDistance)
155 {
157 point, edge12Closest.PointOnSegment,
158 Vec3<T>(T(0), T(1) - edge12Closest.SegmentFraction,
159 edge12Closest.SegmentFraction));
160 }
161 return closest;
162 }
163
164 const Vec3<T> fromFirst = point - first;
167 if (firstAlong01 <= T(0) && firstAlong02 <= T(0))
168 {
170 Vec3<T>(T(1), T(0), T(0)));
171 }
172
173 const Vec3<T> fromSecond = point - second;
177 {
179 Vec3<T>(T(0), T(1), T(0)));
180 }
181
182 const T edge01Region =
184 if (edge01Region <= T(0) && firstAlong01 >= T(0) &&
185 secondAlong01 <= T(0))
186 {
190 Vec3<T>(T(1) - fraction, fraction, T(0)));
191 }
192
193 const Vec3<T> fromThird = point - third;
196 if (thirdAlong02 >= T(0) && thirdAlong01 <= thirdAlong02)
197 {
199 Vec3<T>(T(0), T(0), T(1)));
200 }
201
202 const T edge02Region =
204 if (edge02Region <= T(0) && firstAlong02 >= T(0) &&
205 thirdAlong02 <= T(0))
206 {
210 Vec3<T>(T(1) - fraction, T(0), fraction));
211 }
212
213 const T edge12Region =
217 if (edge12Region <= T(0) && secondTowardThird >= T(0) &&
218 thirdTowardSecond >= T(0))
219 {
220 const T fraction =
224 Vec3<T>(T(0), T(1) - fraction, fraction));
225 }
226
227 const T reciprocalSum =
234 thirdWeight));
235 }
236
237 // Component clamping is the point/AABB distance construction in Ericson,
238 // Real-Time Collision Detection (2005), section 5.1.3. Empty boxes and
239 // non-finite query points preserve Aabb3's existing fallible contract.
240 template <typename T>
241 [[nodiscard]] inline std::optional<PointAabbClosest3<T>> TryClosestPoints(
242 const Point3<T>& point, const Aabb3<T>& box) noexcept
243 {
244 const std::optional<Point3<T>> closest = box.TryClosestPoint(point);
245 if (!closest)
246 {
247 return std::nullopt;
248 }
251 }
252
253 // Radial projection is the solid-sphere specialization of Ericson,
254 // Real-Time Collision Detection (2005), section 5.1.4. A contained point
255 // is already the nearest member of the bounding volume and has distance 0.
256 template <typename T>
258 const Point3<T>& point, const Sphere3<T>& sphere) noexcept
259 {
260 const Point3<T> center = sphere.Center();
261 const Vec3<T> offset = point - center;
263 const T squaredRadius = sphere.Radius() * sphere.Radius();
265 {
266 return PointSphereClosest3<T>{point, T(0)};
267 }
268
269 const T scale = sphere.Radius() / std::sqrt(squaredCenterDistance);
273 }
274
275 // A capsule is a segment swept by a sphere (Ericson, Real-Time Collision
276 // Detection, 2005, sections 4.5 and 4.5.1). Project to the center line,
277 // then apply the same radial solid-volume rule as point/sphere.
278 template <typename T>
280 const Point3<T>& point, const Capsule3<T>& capsule) noexcept
281 {
283 ClosestPoints(point, capsule.CenterLine());
284 if (centerLineClosest.SquaredDistance <=
285 capsule.Radius() * capsule.Radius())
286 {
288 point, centerLineClosest.SegmentFraction, T(0)};
289 }
290
291 const Vec3<T> radialOffset = point - centerLineClosest.PointOnSegment;
292 const T scale =
293 capsule.Radius() / std::sqrt(centerLineClosest.SquaredDistance);
295 centerLineClosest.PointOnSegment + radialOffset * scale;
297 pointInCapsule, centerLineClosest.SegmentFraction,
299 }
300
301 // ClosestPtSegmentSegment from Ericson, Real-Time Collision Detection
302 // (2005), section 5.1.9. Wider intermediates reduce cancellation for
303 // nearly parallel segments; degenerate segments remain valid.
304 template <typename T>
306 const Segment3<T>& first, const Segment3<T>& second) noexcept
307 {
309 const Vec3<T> firstDirection = first.Displacement();
310 const Vec3<T> secondDirection = second.Displacement();
311 const Vec3<T> startOffset = first.Start() - second.Start();
318
323 {
324 // Both segments are points; the initialized fractions are final.
325 }
326 else if (firstLengthSquared == Calculation(0))
327 {
329 Calculation(0), Calculation(1));
330 }
331 else
332 {
336 {
339 Calculation(0), Calculation(1));
340 }
341 else
342 {
348 if (denominator > Calculation(0))
349 {
351 std::clamp((directionsDot * secondProjection -
354 Calculation(0), Calculation(1));
355 }
356
361 {
365 Calculation(0), Calculation(1));
366 }
367 else if (secondFraction > Calculation(1))
368 {
370 firstFraction = std::clamp(
372 Calculation(0), Calculation(1));
373 }
374 }
375 }
376
377 const T firstValue = static_cast<T>(firstFraction);
378 const T secondValue = static_cast<T>(secondFraction);
379 const Point3<T> pointOnFirst = first.PointAtFraction(firstValue);
380 const Point3<T> pointOnSecond = second.PointAtFraction(secondValue);
384 }
385
386 template <typename T>
388 const Line3<T>& line) noexcept
389 {
390 return ClosestPoints(point, line).PointOnLine;
391 }
392
393 template <typename T>
395 const Ray3<T>& ray) noexcept
396 {
397 return ClosestPoints(point, ray).PointOnRay;
398 }
399
400 template <typename T>
402 const Point3<T>& point, const Segment3<T>& segment) noexcept
403 {
404 return ClosestPoints(point, segment).PointOnSegment;
405 }
406
407 template <typename T>
409 const Plane3<T>& plane) noexcept
410 {
411 return ClosestPoints(point, plane).PointOnPlane;
412 }
413
414 template <typename T>
416 const Point3<T>& point, const Triangle3<T>& triangle) noexcept
417 {
418 return ClosestPoints(point, triangle).PointOnTriangle;
419 }
420
421 template <typename T>
422 [[nodiscard]] inline std::optional<Point3<T>> TryClosestPoint(
423 const Point3<T>& point, const Aabb3<T>& box) noexcept
424 {
425 const auto result = TryClosestPoints(point, box);
426 return result ? std::optional<Point3<T>>(result->PointInAabb)
427 : std::nullopt;
428 }
429
430 template <typename T>
432 const Point3<T>& point, const Sphere3<T>& sphere) noexcept
433 {
434 return ClosestPoints(point, sphere).PointInSphere;
435 }
436
437 template <typename T>
439 const Point3<T>& point, const Capsule3<T>& capsule) noexcept
440 {
441 return ClosestPoints(point, capsule).PointInCapsule;
442 }
443
444 template <typename T>
446 const Line3<T>& line) noexcept
447 {
448 return ClosestPoints(point, line).SquaredDistance;
449 }
450
451 template <typename T>
453 const Ray3<T>& ray) noexcept
454 {
455 return ClosestPoints(point, ray).SquaredDistance;
456 }
457
458 template <typename T>
460 const Segment3<T>& segment) noexcept
461 {
462 return ClosestPoints(point, segment).SquaredDistance;
463 }
464
465 template <typename T>
467 const Plane3<T>& plane) noexcept
468 {
469 return ClosestPoints(point, plane).SquaredDistance;
470 }
471
472 template <typename T>
474 const Point3<T>& point, const Triangle3<T>& triangle) noexcept
475 {
476 return ClosestPoints(point, triangle).SquaredDistance;
477 }
478
479 template <typename T>
480 [[nodiscard]] inline std::optional<T> TryDistanceSquared(
481 const Point3<T>& point, const Aabb3<T>& box) noexcept
482 {
483 const auto result = TryClosestPoints(point, box);
484 return result ? std::optional<T>(result->SquaredDistance)
485 : std::nullopt;
486 }
487
488 template <typename T>
490 const Sphere3<T>& sphere) noexcept
491 {
492 return ClosestPoints(point, sphere).SquaredDistance;
493 }
494
495 template <typename T>
497 const Capsule3<T>& capsule) noexcept
498 {
499 return ClosestPoints(point, capsule).SquaredDistance;
500 }
501
502 template <typename T>
504 const Segment3<T>& second) noexcept
505 {
506 return ClosestPoints(first, second).SquaredDistance;
507 }
508
509 template <typename T>
510 [[nodiscard]] inline T Distance(const Point3<T>& point,
511 const Line3<T>& line) noexcept
512 {
513 return ClosestPoints(point, line).Distance();
514 }
515
516 template <typename T>
517 [[nodiscard]] inline T Distance(const Point3<T>& point,
518 const Ray3<T>& ray) noexcept
519 {
520 return ClosestPoints(point, ray).Distance();
521 }
522
523 template <typename T>
524 [[nodiscard]] inline T Distance(const Point3<T>& point,
525 const Segment3<T>& segment) noexcept
526 {
527 return ClosestPoints(point, segment).Distance();
528 }
529
530 template <typename T>
531 [[nodiscard]] inline T Distance(const Point3<T>& point,
532 const Plane3<T>& plane) noexcept
533 {
534 return ClosestPoints(point, plane).Distance();
535 }
536
537 template <typename T>
538 [[nodiscard]] inline T Distance(const Point3<T>& point,
539 const Triangle3<T>& triangle) noexcept
540 {
541 return ClosestPoints(point, triangle).Distance();
542 }
543
544 template <typename T>
545 [[nodiscard]] inline std::optional<T> TryDistance(
546 const Point3<T>& point, const Aabb3<T>& box) noexcept
547 {
548 const auto result = TryClosestPoints(point, box);
549 return result ? std::optional<T>(result->Distance()) : std::nullopt;
550 }
551
552 template <typename T>
553 [[nodiscard]] inline T Distance(const Point3<T>& point,
554 const Sphere3<T>& sphere) noexcept
555 {
556 return ClosestPoints(point, sphere).Distance();
557 }
558
559 template <typename T>
560 [[nodiscard]] inline T Distance(const Point3<T>& point,
561 const Capsule3<T>& capsule) noexcept
562 {
563 return ClosestPoints(point, capsule).Distance();
564 }
565
566 template <typename T>
567 [[nodiscard]] inline bool Contains(const Capsule3<T>& capsule,
568 const Point3<T>& point) noexcept
569 {
570 return ClosestPoints(point, capsule).SquaredDistance == T(0);
571 }
572
573 template <typename T>
575 const Segment3<T>& second) noexcept
576 {
577 return ClosestPoints(first, second).Distance();
578 }
579} // namespace mv::math
PointTriangleClosest3< T > MakePointTriangleClosest(const Point3< T > &point, const Point3< T > &pointOnTriangle, const Vec3< T > &barycentric) noexcept
T PointDistanceSquared(const Point3< T > &first, const Point3< T > &second) noexcept
std::conditional_t< std::is_same_v< T, float >, double, long double > QueryCalculation
QueryCalculation< T > DotPrecise(const Vec3< T > &first, const Vec3< T > &second) noexcept
constexpr T Dot(const Quat< T > &left, const Quat< T > &right) noexcept
Definition Quat.hpp:102
Vec3< T > Cross(const Vec3< T > &left, const Vec3< T > &right) noexcept
Definition Vec3.hpp:367
std::optional< PointAabbClosest3< T > > TryClosestPoints(const Point3< T > &point, const Aabb3< T > &box) noexcept
std::optional< Point3< T > > TryClosestPoint(const Point3< T > &point, const Aabb3< T > &box) noexcept
T Distance(const Point3< T > &point, const Line3< T > &line) noexcept
bool Contains(const Capsule3< T > &capsule, const Point3< T > &point) noexcept
constexpr T LengthSquared(const Quat< T > &value) noexcept
Definition Quat.hpp:110
Point3< T > ClosestPoint(const Point3< T > &point, const Line3< T > &line) noexcept
std::optional< T > TryDistance(const Point3< T > &point, const Aabb3< T > &box) noexcept
PointLineClosest3< T > ClosestPoints(const Point3< T > &point, const Line3< T > &line) noexcept
std::optional< T > TryDistanceSquared(const Point3< T > &point, const Aabb3< T > &box) noexcept
T DistanceSquared(const Point3< T > &point, const Line3< T > &line) noexcept