Geogram Version 1.9.6
A programming library of geometric algorithms
Loading...
Searching...
No Matches
geometry_nd.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40#ifndef GEOGRAM_BASIC_GEOMETRY_ND
41#define GEOGRAM_BASIC_GEOMETRY_ND
42
46
52namespace GEO {
53
54 namespace Geom {
55
64 template <class COORD_T>
65 inline double distance2(
66 const COORD_T* p1, const COORD_T* p2, coord_index_t dim
67 ) {
68 double result = 0.0;
69 for(coord_index_t i = 0; i < dim; i++) {
70 result += GEO::geo_sqr(double(p2[i]) - double(p1[i]));
71 }
72 return result;
73 }
74
83 template <class COORD_T>
84 inline double distance(
85 const COORD_T* p1, const COORD_T* p2, coord_index_t dim
86 ) {
87 return ::sqrt(distance2(p1, p2, dim));
88 }
89
99 template <class VEC>
100 inline double distance2(
101 const VEC& p1, const VEC& p2
102 ) {
103 geo_debug_assert(p1.dimension() == p2.dimension());
104 return distance2(
105 p1.data(), p2.data(), coord_index_t(p1.dimension())
106 );
107 }
108
119 template <class VEC>
120 inline double distance(
121 const VEC& p1, const VEC& p2
122 ) {
123 geo_debug_assert(p1.dimension() == p2.dimension());
124 return distance(p1.data(), p2.data(), coord_index_t(p1.dimension()));
125 }
126
142 template <class COORD_T>
143 inline double triangle_area(
144 const COORD_T* p1,
145 const COORD_T* p2,
146 const COORD_T* p3,
147 coord_index_t dim
148 ) {
149 double a = distance(p1, p2, dim);
150 double b = distance(p2, p3, dim);
151 double c = distance(p3, p1, dim);
152 double s = double(0.5) * (a + b + c);
153 double A2 = s * (s - a) * (s - b) * (s - c);
154 // the max is there to avoid some numerical problems.
155 return ::sqrt(std::max(A2, 0.0));
156 }
157
177 template <class COORD_T>
178 inline void triangle_centroid(
179 const COORD_T* p,
180 const COORD_T* q,
181 const COORD_T* r,
182 COORD_T a, COORD_T b, COORD_T c,
183 double* Vg,
184 double& V,
185 coord_index_t dim
186 ) {
187 double abc = a + b + c;
188 double area = Geom::triangle_area(p, q, r, dim);
189 V = area / 3.0 * abc;
190 double wp = a + abc;
191 double wq = b + abc;
192 double wr = c + abc;
193 double s = area / 12.0;
194 for(coord_index_t i = 0; i < dim; i++) {
195 Vg[i] = s * (wp * p[i] + wq * q[i] + wr * r[i]);
196 }
197 }
198
199 /********************************************************************/
200
211 template <class VEC>
212 inline double triangle_area(
213 const VEC& p1, const VEC& p2, const VEC& p3
214 ) {
215 // Heron formula
216 double a = distance(p1, p2);
217 double b = distance(p2, p3);
218 double c = distance(p3, p1);
219 double s = double(0.5) * (a + b + c);
220 return ::sqrt(s * (s - a) * (s - b) * (s - c));
221 }
222
236 template <class VEC>
237 inline double triangle_mass(
238 const VEC& p, const VEC& q, const VEC& r,
239 double a, double b, double c
240 ) {
241 // TODO: try to better understand the formula and
242 // determine why there are these sqrt's
243 // (probably due to the relation between the
244 // user-provided density and the one achieved
245 // by CVT), but I'm pretty sure that the formula
246 // is correct (at least, dimensions match).
247 // Note: the ::fabs() are there to avoid numerical
248 // errors.
249 return Geom::triangle_area(p, q, r) / 3.0 * (
250 ::sqrt(::fabs(a)) + sqrt(::fabs(b)) + sqrt(::fabs(c))
251 );
252 }
253
266 template <class POINT>
268 const POINT& Q1,
269 const POINT& Q2,
270 const POINT& Q3,
271 double* denom = nullptr
272 ) {
273 const POINT q2 = Q2 - Q1;
274 const POINT q3 = Q3 - Q1;
275
276 double l2 = length2(q2);
277 double l3 = length2(q3);
278
279 double a12 = -2.0 * dot(q2, q2);
280 double a13 = -2.0 * dot(q3, q2);
281 double a22 = -2.0 * dot(q2, q3);
282 double a23 = -2.0 * dot(q3, q3);
283
284 double c31 = (a23 * a12 - a22 * a13);
285 double d = c31;
286 double s = 1.0 / d;
287 double lambda1 = s * ((a23 - a22) * l2 + (a12 - a13) * l3 + c31);
288 double lambda2 = s * ((-a23) * l2 + (a13) * l3);
289 double lambda3 = s * ((a22) * l2 + (-a12) * l3);
290 if(denom != nullptr) {
291 *denom = d;
292 }
293 return lambda1 * Q1 + lambda2 * Q2 + lambda3 * Q3;
294 }
295
310 template <class VEC>
311 inline void triangle_centroid(
312 const VEC& p, const VEC& q, const VEC& r,
313 double a, double b, double c,
314 VEC& Vg, double& V
315 ) {
316 double abc = a + b + c;
317 double area = Geom::triangle_area(p, q, r);
318 V = area / 3.0 * abc;
319 double wp = a + abc;
320 double wq = b + abc;
321 double wr = c + abc;
322 double s = area / 12.0;
323 Vg = s * (wp * p + wq * q + wr * r);
324 }
325
337 template <class VEC>
339 const VEC& p1, const VEC& p2, const VEC& p3
340 ) {
341 double l1 = Numeric::random_float64();
342 double l2 = Numeric::random_float64();
343 if(l1 + l2 > 1.0) {
344 l1 = 1.0 - l1;
345 l2 = 1.0 - l2;
346 }
347 double l3 = 1.0 - l1 - l2;
348 return l1 * p1 + l2 * p2 + l3 * p3;
349 }
350
363 template <class VEC>
365 const VEC& p1, const VEC& p2, const VEC& p3, const VEC& p4
366 ) {
367 double s = Numeric::random_float64();
368 double t = Numeric::random_float64();
369 double u = Numeric::random_float64();
370 if(s + t > 1.0) {
371 s = 1.0 - s;
372 t = 1.0 - t;
373 }
374 if(t + u > 1.0) {
375 double tmp = u;
376 u = 1.0 - s - t;
377 t = 1.0 - tmp;
378 } else if(s + t + u > 1.0) {
379 double tmp = u;
380 u = s + t + u - 1.0;
381 s = 1.0 - t - tmp;
382 }
383 double a = 1.0 - s - t - u;
384 return a * p1 + s * p2 + t * p3 + u * p4;
385 }
386
402 template <class VEC>
404 const VEC& point,
405 const VEC& V0,
406 const VEC& V1,
407 VEC& closest_point,
408 double& lambda0,
409 double& lambda1
410 ) {
411 double l2 = distance2(V0,V1);
412 double t = dot(point - V0, V1 - V0);
413 if(t <= 0.0 || l2 == 0.0) {
414 closest_point = V0;
415 lambda0 = 1.0;
416 lambda1 = 0.0;
417 return distance2(point, V0);
418 } else if(t > l2) {
419 closest_point = V1;
420 lambda0 = 0.0;
421 lambda1 = 1.0;
422 return distance2(point, V1);
423 }
424 lambda1 = t / l2;
425 lambda0 = 1.0-lambda1;
426 closest_point = lambda0 * V0 + lambda1 * V1;
427 return distance2(point, closest_point);
428 }
429
430
440 template <class VEC>
442 const VEC& point,
443 const VEC& V0,
444 const VEC& V1
445 ) {
446 VEC closest_point;
447 double lambda0;
448 double lambda1;
450 point, V0, V1, closest_point, lambda0, lambda1
451 );
452 }
453
474 template <class VEC>
476 const VEC& point,
477 const VEC& V0,
478 const VEC& V1,
479 const VEC& V2,
480 VEC& closest_point,
481 double& lambda0, double& lambda1, double& lambda2
482 ) {
483 VEC diff = V0 - point;
484 VEC edge0 = V1 - V0;
485 VEC edge1 = V2 - V0;
486 double a00 = length2(edge0);
487 double a01 = dot(edge0, edge1);
488 double a11 = length2(edge1);
489 double b0 = dot(diff, edge0);
490 double b1 = dot(diff, edge1);
491 double c = length2(diff);
492 double det = ::fabs(a00 * a11 - a01 * a01);
493 double s = a01 * b1 - a11 * b0;
494 double t = a01 * b0 - a00 * b1;
495 double sqrDistance;
496
497 // If the triangle is degenerate
498 if(det < 1e-30) {
499 double cur_l1, cur_l2;
500 VEC cur_closest;
501 double result;
502 double cur_dist = point_segment_squared_distance(
503 point, V0, V1, cur_closest, cur_l1, cur_l2
504 );
505 result = cur_dist;
506 closest_point = cur_closest;
507 lambda0 = cur_l1;
508 lambda1 = cur_l2;
509 lambda2 = 0.0;
511 point, V0, V2, cur_closest, cur_l1, cur_l2
512 );
513 if(cur_dist < result) {
514 result = cur_dist;
515 closest_point = cur_closest;
516 lambda0 = cur_l1;
517 lambda2 = cur_l2;
518 lambda1 = 0.0;
519 }
521 point, V1, V2, cur_closest, cur_l1, cur_l2
522 );
523 if(cur_dist < result) {
524 result = cur_dist;
525 closest_point = cur_closest;
526 lambda1 = cur_l1;
527 lambda2 = cur_l2;
528 lambda0 = 0.0;
529 }
530 return result;
531 }
532
533 if(s + t <= det) {
534 if(s < 0.0) {
535 if(t < 0.0) { // region 4
536 if(b0 < 0.0) {
537 t = 0.0;
538 if(-b0 >= a00) {
539 s = 1.0;
540 sqrDistance = a00 + 2.0 * b0 + c;
541 } else {
542 s = -b0 / a00;
543 sqrDistance = b0 * s + c;
544 }
545 } else {
546 s = 0.0;
547 if(b1 >= 0.0) {
548 t = 0.0;
549 sqrDistance = c;
550 } else if(-b1 >= a11) {
551 t = 1.0;
552 sqrDistance = a11 + 2.0 * b1 + c;
553 } else {
554 t = -b1 / a11;
555 sqrDistance = b1 * t + c;
556 }
557 }
558 } else { // region 3
559 s = 0.0;
560 if(b1 >= 0.0) {
561 t = 0.0;
562 sqrDistance = c;
563 } else if(-b1 >= a11) {
564 t = 1.0;
565 sqrDistance = a11 + 2.0 * b1 + c;
566 } else {
567 t = -b1 / a11;
568 sqrDistance = b1 * t + c;
569 }
570 }
571 } else if(t < 0.0) { // region 5
572 t = 0.0;
573 if(b0 >= 0.0) {
574 s = 0.0;
575 sqrDistance = c;
576 } else if(-b0 >= a00) {
577 s = 1.0;
578 sqrDistance = a00 + 2.0 * b0 + c;
579 } else {
580 s = -b0 / a00;
581 sqrDistance = b0 * s + c;
582 }
583 } else { // region 0
584 // minimum at interior point
585 double invDet = double(1.0) / det;
586 s *= invDet;
587 t *= invDet;
588 sqrDistance = s * (a00 * s + a01 * t + 2.0 * b0) +
589 t * (a01 * s + a11 * t + 2.0 * b1) + c;
590 }
591 } else {
592 double tmp0, tmp1, numer, denom;
593
594 if(s < 0.0) { // region 2
595 tmp0 = a01 + b0;
596 tmp1 = a11 + b1;
597 if(tmp1 > tmp0) {
598 numer = tmp1 - tmp0;
599 denom = a00 - 2.0 * a01 + a11;
600 if(numer >= denom) {
601 s = 1.0;
602 t = 0.0;
603 sqrDistance = a00 + 2.0 * b0 + c;
604 } else {
605 s = numer / denom;
606 t = 1.0 - s;
607 sqrDistance = s * (a00 * s + a01 * t + 2.0 * b0) +
608 t * (a01 * s + a11 * t + 2.0 * b1) + c;
609 }
610 } else {
611 s = 0.0;
612 if(tmp1 <= 0.0) {
613 t = 1.0;
614 sqrDistance = a11 + 2.0 * b1 + c;
615 }
616 else if(b1 >= 0.0) {
617 t = 0.0;
618 sqrDistance = c;
619 } else {
620 t = -b1 / a11;
621 sqrDistance = b1 * t + c;
622 }
623 }
624 } else if(t < 0.0) { // region 6
625 tmp0 = a01 + b1;
626 tmp1 = a00 + b0;
627 if(tmp1 > tmp0) {
628 numer = tmp1 - tmp0;
629 denom = a00 - 2.0 * a01 + a11;
630 if(numer >= denom) {
631 t = 1.0;
632 s = 0.0;
633 sqrDistance = a11 + 2.0 * b1 + c;
634 } else {
635 t = numer / denom;
636 s = 1.0 - t;
637 sqrDistance = s * (a00 * s + a01 * t + 2.0 * b0) +
638 t * (a01 * s + a11 * t + 2.0 * b1) + c;
639 }
640 } else {
641 t = 0.0;
642 if(tmp1 <= 0.0) {
643 s = 1.0;
644 sqrDistance = a00 + 2.0 * b0 + c;
645 } else if(b0 >= 0.0) {
646 s = 0.0;
647 sqrDistance = c;
648 } else {
649 s = -b0 / a00;
650 sqrDistance = b0 * s + c;
651 }
652 }
653 } else { // region 1
654 numer = a11 + b1 - a01 - b0;
655 if(numer <= 0.0) {
656 s = 0.0;
657 t = 1.0;
658 sqrDistance = a11 + 2.0 * b1 + c;
659 } else {
660 denom = a00 - 2.0 * a01 + a11;
661 if(numer >= denom) {
662 s = 1.0;
663 t = 0.0;
664 sqrDistance = a00 + 2.0 * b0 + c;
665 } else {
666 s = numer / denom;
667 t = 1.0 - s;
668 sqrDistance = s * (a00 * s + a01 * t + 2.0 * b0) +
669 t * (a01 * s + a11 * t + 2.0 * b1) + c;
670 }
671 }
672 }
673 }
674
675 // Account for numerical round-off error.
676 if(sqrDistance < 0.0) {
677 sqrDistance = 0.0;
678 }
679
680 closest_point = V0 + s * edge0 + t * edge1;
681 lambda0 = 1.0 - s - t;
682 lambda1 = s;
683 lambda2 = t;
684 return sqrDistance;
685 }
686
701 template <class VEC>
703 const VEC& p, const VEC& q1, const VEC& q2, const VEC& q3
704 ) {
705 VEC closest_point;
706 double lambda1, lambda2, lambda3;
708 p, q1, q2, q3, closest_point, lambda1, lambda2, lambda3
709 );
710 }
711
727 double u, double U,
728 double v, double V,
729 double w, double W
730 ) {
731 double X = (w - U + v) * (U + v + w);
732 double x = (U - v + w) * (v - w + U);
733 double Y = (u - V + w) * (V + w + u);
734 double y = (V - w + u) * (w - u + V);
735 double Z = (v - W + u) * (W + u + v);
736 double z = (W - u + v) * (u - v + W);
737 double a = ::sqrt(::fabs(x * Y * Z));
738 double b = ::sqrt(::fabs(y * Z * X));
739 double c = ::sqrt(::fabs(z * X * Y));
740 double d = ::sqrt(::fabs(x * y * z));
741 return ::sqrt(::fabs(
742 (-a + b + c + d) *
743 (a - b + c + d) *
744 (a + b - c + d) *
745 (a + b + c - d)
746 )) / (192.0 * u * v * w);
747 }
748
762 template <class VEC>
763 inline double tetra_volume(
764 const VEC& p1, const VEC& p2, const VEC& p3, const VEC& p4
765 ) {
766 double U = distance(p1, p2);
767 double u = distance(p3, p4);
768 double V = distance(p2, p3);
769 double v = distance(p1, p4);
770 double W = distance(p3, p1);
771 double w = distance(p2, p4);
772 return tetra_volume_from_edge_lengths(u, U, v, V, w, W);
773 }
774
788 template <int DIM>
789 inline double tetra_volume(
790 const double* p1, const double* p2,
791 const double* p3, const double* p4
792 ) {
793 double U = distance(p1, p2, DIM);
794 double u = distance(p3, p4, DIM);
795 double V = distance(p2, p3, DIM);
796 double v = distance(p1, p4, DIM);
797 double W = distance(p3, p1, DIM);
798 double w = distance(p2, p4, DIM);
799 return tetra_volume_from_edge_lengths(u, U, v, V, w, W);
800 }
801
814 template <>
815 inline double tetra_volume<3>(
816 const double* p1, const double* p2,
817 const double* p3, const double* p4
818 ) {
819 return tetra_volume(
820 *reinterpret_cast<const vec3*>(p1),
821 *reinterpret_cast<const vec3*>(p2),
822 *reinterpret_cast<const vec3*>(p3),
823 *reinterpret_cast<const vec3*>(p4)
824 );
825 }
826 }
827}
828
829#endif
#define geo_debug_assert(x)
Verifies that a condition is met.
Definition assert.h:196
Common include file, providing basic definitions. Should be included before anything else by all head...
Geometric functions in 2d and 3d.
Types and functions for memory manipulation.
double point_segment_squared_distance(const VEC &point, const VEC &V0, const VEC &V1, VEC &closest_point, double &lambda0, double &lambda1)
Computes the point closest to a given point in a nd segment.
double distance(const COORD_T *p1, const COORD_T *p2, coord_index_t dim)
Computes the distance between two nd points.
Definition geometry_nd.h:84
double det(const vec2 &a, const vec2 &b)
Computes the determinant of two vectors.
Definition geometry.h:312
double tetra_volume< 3 >(const double *p1, const double *p2, const double *p3, const double *p4)
Computes the volume of a 3d tetrahedron.
vec3 random_point_in_triangle(const vec3 &p1, const vec3 &p2, const vec3 &p3)
Generates a random point in a 3d triangle.
Definition geometry.h:602
double distance2(const COORD_T *p1, const COORD_T *p2, coord_index_t dim)
Computes the squared distance between two nd points.
Definition geometry_nd.h:65
double tetra_volume(const vec3 &p1, const vec3 &p2, const vec3 &p3, const vec3 &p4)
Computes the volume of a 3d tetrahedron.
Definition geometry.h:520
double triangle_mass(const vec3 &p, const vec3 &q, const vec3 &r, double a, double b, double c)
Computes the mass of a 3d triangle with weighted points.
Definition geometry.h:583
double triangle_area(const vec3 &p1, const vec3 &p2, const vec3 &p3)
Computes the area of a 3d triangle.
Definition geometry.h:368
void triangle_centroid(const vec3 &p, const vec3 &q, const vec3 &r, double a, double b, double c, vec3 &Vg, double &V)
Computes the centroid of a 3d triangle with weighted points.
Definition geometry.h:554
double tetra_volume_from_edge_lengths(double u, double U, double v, double V, double w, double W)
Computes the volume of a tetrahedron from edge lengths.
double point_triangle_squared_distance(const VEC &point, const VEC &V0, const VEC &V1, const VEC &V2, VEC &closest_point, double &lambda0, double &lambda1, double &lambda2)
Computes the point closest to a given point in a nd triangle.
VEC random_point_in_tetra(const VEC &p1, const VEC &p2, const VEC &p3, const VEC &p4)
Generates a random point in a nd tetrahedron.
vec2 triangle_circumcenter(const vec2 &p1, const vec2 &p2, const vec2 &p3)
Computes the center of the circumscribed circle of a 2d triangle.
float64 random_float64()
Returns a 64 bits float between 0 and 1.
Global Vorpaline namespace.
Definition basic.h:55
T dot(const vecng< 3, T > &v1, const vecng< 3, T > &v2)
Computes the dot product of 2 vectors. vecng
Definition vecg.h:916
T geo_sqr(T x)
Gets the square value of a value.
Definition numeric.h:301
geo_coord_index_t coord_index_t
The type for storing coordinate indices, and iterating on the coordinates of a point.
Definition numeric.h:363