PhysicsBasedAnimationToolkit 0.0.10
Cross-platform C++20 library of algorithms and data structures commonly used in computer graphics research on physically-based simulation.
Loading...
Searching...
No Matches
ClosestPointQueries.h
Go to the documentation of this file.
1
10
11#ifndef PBAT_GEOMETRY_CLOSESTPOINTQUERIES_H
12#define PBAT_GEOMETRY_CLOSESTPOINTQUERIES_H
13
14#include "pbat/HostDevice.h"
16
17#include <algorithm>
18#include <cassert>
19
20namespace pbat {
21namespace geometry {
26
27namespace mini = math::linalg::mini;
28
39template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixP, mini::CMatrix TMatrixN>
40PBAT_HOST_DEVICE auto PointOnPlane(TMatrixX const& X, TMatrixP const& P, TMatrixN const& n)
41 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>;
42
53template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixP, mini::CMatrix TMatrixQ>
54PBAT_HOST_DEVICE auto PointOnLineSegment(TMatrixX const& X, TMatrixP const& P, TMatrixQ const& Q)
55 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>;
56
68template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixL, mini::CMatrix TMatrixU>
69PBAT_HOST_DEVICE auto
70PointOnAxisAlignedBoundingBox(TMatrixX const& X, TMatrixL const& L, TMatrixU const& U)
71 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>;
72
85template <
86 mini::CMatrix TMatrixP,
87 mini::CMatrix TMatrixA,
88 mini::CMatrix TMatrixB,
89 mini::CMatrix TMatrixC>
90PBAT_HOST_DEVICE auto
91UvwPointInTriangle(TMatrixP const& P, TMatrixA const& A, TMatrixB const& B, TMatrixC const& C)
92 -> mini::SVector<typename TMatrixP::ScalarType, 3>;
93
106template <
107 mini::CMatrix TMatrixP,
108 mini::CMatrix TMatrixA,
109 mini::CMatrix TMatrixB,
110 mini::CMatrix TMatrixC>
111PBAT_HOST_DEVICE auto
112PointInTriangle(TMatrixP const& P, TMatrixA const& A, TMatrixB const& B, TMatrixC const& C)
113 -> mini::SVector<typename TMatrixP::ScalarType, TMatrixP::kRows>;
114
131template <
132 mini::CMatrix TMatrixP,
133 mini::CMatrix TMatrixA,
134 mini::CMatrix TMatrixB,
135 mini::CMatrix TMatrixC,
136 mini::CMatrix TMatrixD>
137PBAT_HOST_DEVICE auto PointInTetrahedron(
138 TMatrixP const& P,
139 TMatrixA const& A,
140 TMatrixB const& B,
141 TMatrixC const& C,
142 TMatrixD const& D) -> mini::SVector<typename TMatrixP::ScalarType, TMatrixP::kRows>;
143
228template <
229 mini::CMatrix TMatrixP1,
230 mini::CMatrix TMatrixQ1,
231 mini::CMatrix TMatrixP2,
232 mini::CMatrix TMatrixQ2,
233 class TScalar = typename TMatrixP1::ScalarType>
234PBAT_HOST_DEVICE auto Lines(
235 TMatrixP1 const& P1,
236 TMatrixQ1 const& Q1,
237 TMatrixP2 const& P2,
238 TMatrixQ2 const& Q2,
239 TScalar eps = std::numeric_limits<TScalar>::min()) -> mini::SVector<TScalar, 2>;
240
241template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixP, mini::CMatrix TMatrixN>
242PBAT_HOST_DEVICE auto PointOnPlane(TMatrixX const& X, TMatrixP const& P, TMatrixN const& n)
243 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>
244{
245 using namespace std;
246 using ScalarType = typename TMatrixX::ScalarType;
247#ifndef NDEBUG
248 bool const bIsNormalUnit = abs(SquaredNorm(n) - ScalarType(1)) <= ScalarType(1e-15);
249 assert(bIsNormalUnit);
250#endif
254 ScalarType const t = Dot(n, X - P);
255 mini::SVector<ScalarType, TMatrixX::kRows> Xplane = X - t * n;
256 return Xplane;
257}
258
259template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixP, mini::CMatrix TMatrixQ>
260PBAT_HOST_DEVICE auto PointOnLineSegment(TMatrixX const& X, TMatrixP const& P, TMatrixQ const& Q)
261 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>
262{
263 using ScalarType = typename TMatrixX::ScalarType;
264 using namespace std;
268 mini::SVector<ScalarType, TMatrixX::kRows> const PQ = Q - P;
269 // Project X onto PQ, computing parameterized position R(t) = P + t*(Q � P)
270 ScalarType t = Dot(X - P, PQ) / SquaredNorm(PQ);
271 // If outside segment, clamp t (and therefore d) to the closest endpoint
272 t = min(max(t, ScalarType(0)), ScalarType(1));
273 // Compute projected position from the clamped t
274 auto const Xpq = P + t * PQ;
275 return Xpq;
276}
277
278template <mini::CMatrix TMatrixX, mini::CMatrix TMatrixL, mini::CMatrix TMatrixU>
279PBAT_HOST_DEVICE auto
280PointOnAxisAlignedBoundingBox(TMatrixX const& P, TMatrixL const& L, TMatrixU const& U)
281 -> mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows>
282{
283 using namespace std;
287 mini::SVector<typename TMatrixX::ScalarType, TMatrixX::kRows> X = P;
289 [&]<auto i>() { X(i) = min(max(X(i), L(i)), U(i)); });
290 return X;
291}
292
293template <
294 mini::CMatrix TMatrixP,
295 mini::CMatrix TMatrixA,
296 mini::CMatrix TMatrixB,
297 mini::CMatrix TMatrixC>
298PBAT_HOST_DEVICE auto
299UvwPointInTriangle(TMatrixP const& P, TMatrixA const& A, TMatrixB const& B, TMatrixC const& C)
300 -> mini::SVector<typename TMatrixP::ScalarType, 3>
301{
302 using ScalarType = typename TMatrixP::ScalarType;
306
307 // Check if P in vertex region outside A
308 auto constexpr kRows = TMatrixP::kRows;
309 mini::SVector<ScalarType, kRows> const AB = B - A;
310 mini::SVector<ScalarType, kRows> const AC = C - A;
311 mini::SVector<ScalarType, kRows> const AP = P - A;
312 ScalarType const d1 = Dot(AB, AP);
313 ScalarType const d2 = Dot(AC, AP);
314 if (d1 <= ScalarType(0) and d2 <= ScalarType(0))
315 {
316 return mini::Unit<ScalarType, 3>(0); // barycentric coordinates (1,0,0)
317 }
318
319 // Check if P in vertex region outside B
320 mini::SVector<ScalarType, kRows> const BP = P - B;
321 ScalarType const d3 = Dot(AB, BP);
322 ScalarType const d4 = Dot(AC, BP);
323 if (d3 >= ScalarType(0) and d4 <= d3)
324 {
325 return mini::Unit<ScalarType, 3>(1); // barycentric coordinates (0,1,0)
326 }
327
328 // Check if P in edge region of AB, if so return projection of P onto AB
329 ScalarType const vc = d1 * d4 - d3 * d2;
330 if (vc <= ScalarType(0) and d1 >= ScalarType(0) and d3 <= ScalarType(0))
331 {
332 ScalarType const v = d1 / (d1 - d3);
333 return mini::SVector<ScalarType, 3>{
334 ScalarType(1) - v,
335 v,
336 ScalarType(0)}; // barycentric coordinates (1-v,v,0)
337 }
338
339 // Check if P in vertex region outside C
340 mini::SVector<ScalarType, kRows> const CP = P - C;
341 ScalarType const d5 = Dot(AB, CP);
342 ScalarType const d6 = Dot(AC, CP);
343 if (d6 >= ScalarType(0) and d5 <= d6)
344 {
345 return mini::Unit<ScalarType, 3>(2); // barycentric coordinates (0,0,1)
346 }
347
348 // Check if P in edge region of AC, if so return projection of P onto AC
349 ScalarType const vb = d5 * d2 - d1 * d6;
350 if (vb <= ScalarType(0) and d2 >= ScalarType(0) and d6 <= ScalarType(0))
351 {
352 ScalarType const w = d2 / (d2 - d6);
353 return mini::SVector<ScalarType, 3>{
354 ScalarType(1) - w,
355 ScalarType(0),
356 w}; // barycentric coordinates (1-w,0,w)
357 }
358 // Check if P in edge region of BC, if so return projection of P onto BC
359 ScalarType const va = d3 * d6 - d5 * d4;
360 if (va <= ScalarType(0) and (d4 - d3) >= ScalarType(0) and (d5 - d6) >= ScalarType(0))
361 {
362 ScalarType const w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
363 return mini::SVector<ScalarType, 3>{
364 ScalarType(0),
365 ScalarType(1) - w,
366 w}; // barycentric coordinates (0,1-w,w)
367 }
368 // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
369 ScalarType const denom = ScalarType(1) / (va + vb + vc);
370 ScalarType const v = vb * denom;
371 ScalarType const w = vc * denom;
372 return mini::SVector<ScalarType, 3>{
373 ScalarType(1) - v - w,
374 v,
375 w}; // = u*a + v*b + w*c, u = va * denom = ScalarType(1)0f-v-w
376}
377
378template <
379 mini::CMatrix TMatrixP,
380 mini::CMatrix TMatrixA,
381 mini::CMatrix TMatrixB,
382 mini::CMatrix TMatrixC>
383PBAT_HOST_DEVICE auto
384PointInTriangle(TMatrixP const& P, TMatrixA const& A, TMatrixB const& B, TMatrixC const& C)
385 -> mini::SVector<typename TMatrixP::ScalarType, TMatrixP::kRows>
386{
387 auto uvw = UvwPointInTriangle(P, A, B, C);
388 return A * uvw(0) + B * uvw(1) + C * uvw(2);
389}
390
391template <
392 mini::CMatrix TMatrixP,
393 mini::CMatrix TMatrixA,
394 mini::CMatrix TMatrixB,
395 mini::CMatrix TMatrixC,
396 mini::CMatrix TMatrixD>
397PBAT_HOST_DEVICE auto PointInTetrahedron(
398 TMatrixP const& P,
399 TMatrixA const& A,
400 TMatrixB const& B,
401 TMatrixC const& C,
402 TMatrixD const& D) -> mini::SVector<typename TMatrixP::ScalarType, TMatrixP::kRows>
403{
404 using ScalarType = typename TMatrixP::ScalarType;
405 auto constexpr kRows = TMatrixP::kRows;
406 auto constexpr kDims = 3;
407 static_assert(kRows == kDims, "This overlap test is specialized for 3D");
408
412
413 // Start out assuming point inside all halfspaces, so closest to itself
414 mini::SVector<ScalarType, kDims> X = P;
415 ScalarType d2min = std::numeric_limits<ScalarType>::max();
416
417 auto const PointOutsidePlane = [](auto const& p, auto const& a, auto const& b, auto const& c) {
418 ScalarType const d = Dot(p - a, Cross(b - a, c - a));
419 return d > ScalarType(0);
420 };
421 auto const TestFace = [&](auto const& a, auto const& b, auto const& c) {
422 // If point outside face abc then compute closest point on abc
423 if (PointOutsidePlane(P, a, b, c))
424 {
425 auto const Q = PointInTriangle(P, a, b, c);
426 ScalarType const d2 = SquaredNorm(Q - P);
427 // Update best closest point if (squared) distance is less than current best
428 if (d2 < d2min)
429 {
430 d2min = d2;
431 X = Q;
432 }
433 }
434 };
435 TestFace(A, B, D);
436 TestFace(B, C, D);
437 TestFace(C, A, D);
438 TestFace(A, C, B);
439 return X;
440}
441
442template <
443 mini::CMatrix TMatrixP1,
444 mini::CMatrix TMatrixQ1,
445 mini::CMatrix TMatrixP2,
446 mini::CMatrix TMatrixQ2,
447 class TScalar>
448PBAT_HOST_DEVICE auto Lines(
449 TMatrixP1 const& P1,
450 TMatrixQ1 const& Q1,
451 TMatrixP2 const& P2,
452 TMatrixQ2 const& Q2,
453 TScalar eps) -> mini::SVector<TScalar, 2>
454{
455 auto constexpr kDims = TMatrixP1::kRows;
456 mini::SVector<TScalar, kDims> const d1 = Q1 - P1;
457 mini::SVector<TScalar, kDims> const d2 = Q2 - P2;
458 mini::SVector<TScalar, kDims> const P2P1 = P1 - P2;
459 TScalar const d1sq = SquaredNorm(d1);
460 TScalar const d2sq = SquaredNorm(d2);
461 TScalar const d1d2 = Dot(d1, d2);
462 TScalar const similarity = d1sq * d2sq - d1d2 * d1d2;
463 bool const bIsLine1Degenerate = d1sq <= eps;
464 bool const bIsLine2Degenerate = d2sq <= eps;
465 bool const bAreParallel = (-eps <= similarity) and (similarity <= eps);
466 TScalar alpha{0}, beta{0};
467 if (not(bIsLine1Degenerate and bIsLine2Degenerate))
468 {
469 if (bIsLine1Degenerate)
470 {
471 beta = Dot(P2P1, d2) / d2sq;
472 }
473 else if (bIsLine2Degenerate)
474 {
475 alpha = -Dot(P2P1, d1) / d1sq;
476 }
477 else
478 {
479 if (not bAreParallel)
480 alpha = Dot(P2P1, d2 * d1d2 - d2sq * d1) / similarity;
481 beta = Dot(P2P1 + alpha * d1, d2) / d2sq;
482 }
483 }
484 return mini::SVector<TScalar, 2>{alpha, beta};
485}
486
487} // namespace ClosestPointQueries
488} // namespace geometry
489} // namespace pbat
490
491#endif // PBAT_GEOMETRY_CLOSESTPOINTQUERIES_H
This file includes all the mini linear algebra headers.
constexpr void ForRange(F &&f)
Compile-time for loop over a range of values.
Definition ConstexprFor.h:55
This namespace contains functions to answer closest point queries.
Definition ClosestPointQueries.h:25
PBAT_HOST_DEVICE auto PointOnAxisAlignedBoundingBox(TMatrixX const &X, TMatrixL const &L, TMatrixU const &U) -> mini::SVector< typename TMatrixX::ScalarType, TMatrixX::kRows >
Obtain the point on the axis-aligned bounding box (AABB) defined by the lower and upper corners close...
Definition ClosestPointQueries.h:280
PBAT_HOST_DEVICE auto PointOnLineSegment(TMatrixX const &X, TMatrixP const &P, TMatrixQ const &Q) -> mini::SVector< typename TMatrixX::ScalarType, TMatrixX::kRows >
Obtain the point on the line segment PQ closest to the point X.
Definition ClosestPointQueries.h:260
PBAT_HOST_DEVICE auto UvwPointInTriangle(TMatrixP const &P, TMatrixA const &A, TMatrixB const &B, TMatrixC const &C) -> mini::SVector< typename TMatrixP::ScalarType, 3 >
Obtain the point on the triangle ABC closest to the point P in barycentric coordinates.
Definition ClosestPointQueries.h:299
PBAT_HOST_DEVICE auto Lines(TMatrixP1 const &P1, TMatrixQ1 const &Q1, TMatrixP2 const &P2, TMatrixQ2 const &Q2, TScalar eps=std::numeric_limits< TScalar >::min()) -> mini::SVector< TScalar, 2 >
Find closest points on two lines defined by points P1, Q1 and P2, Q2.
Definition ClosestPointQueries.h:448
PBAT_HOST_DEVICE auto PointInTetrahedron(TMatrixP const &P, TMatrixA const &A, TMatrixB const &B, TMatrixC const &C, TMatrixD const &D) -> mini::SVector< typename TMatrixP::ScalarType, TMatrixP::kRows >
Obtain the point in the tetrahedron ABCD closest to the point P. The order of ABCD must be such that ...
Definition ClosestPointQueries.h:397
PBAT_HOST_DEVICE auto PointOnPlane(TMatrixX const &X, TMatrixP const &P, TMatrixN const &n) -> mini::SVector< typename TMatrixX::ScalarType, TMatrixX::kRows >
Obtain the point on the plane (P,n) closest to the point X.
Definition ClosestPointQueries.h:242
PBAT_HOST_DEVICE auto PointInTriangle(TMatrixP const &P, TMatrixA const &A, TMatrixB const &B, TMatrixC const &C) -> mini::SVector< typename TMatrixP::ScalarType, TMatrixP::kRows >
Obtain the point on the triangle ABC closest to the point P.
Definition ClosestPointQueries.h:384
Geometric queries, quantities and data structures.
Definition AabbKdTreeHierarchy.h:23
Mini linear algebra related functionality.
Definition Assign.h:12
The main namespace of the library.
Definition Aliases.h:15