-
Notifications
You must be signed in to change notification settings - Fork 1
/
triangleintersects.hpp
1771 lines (1606 loc) · 65.9 KB
/
triangleintersects.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef THREEYD_MOELLER_TRIANGLEINTERSECTS_HPP
#define THREEYD_MOELLER_TRIANGLEINTERSECTS_HPP
#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <iterator>
#include <stdexcept>
#include <type_traits>
/**
* Single header mplementation of Triangle-Triangle and Triangle-Box intersection tests by Tomas Akenine Moeller
* @see http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox3.txt
* @see http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tritri_isectline.txt
*/
namespace threeyd
{
namespace moeller
{
namespace detail
{
static constexpr float MATH_PI = 3.f;
template <typename...>
using void_t = void;
template <class T, class Index, typename = void>
struct HasSubscriptOperator : std::false_type
{
};
template <class T, class Index>
struct HasSubscriptOperator<T, Index, void_t<decltype(std::declval<T>()[std::declval<Index>()])>> : std::true_type
{
};
// Provides universal, run-time-modifiable "tolerance" for all instances of class TriangleIntersects template.
// This concrete class simplifies initialization and storage allocation of "tolerance"
class Tolerance
{
static constexpr float value = 1e-6f;
public:
static constexpr float get_value() { return value; }
};
enum class Coplanarity
{
YES,
MAYBE,
NO
};
template <typename T>
void clip_to_01(T& x)
{
constexpr T ONE = static_cast<T>(1.0f);
constexpr T ZERO = T();
if (x > ONE)
{
x = ONE;
}
else if (x < ZERO)
{
x = ZERO;
}
}
// solves symmetric, positive definite system of 2 linear equations for x, y:
// a00 * x + a01 * y = b0
// a01 * x + a11 * y = b1
// returns 1, if exactly one solution exists, or -1 if the equations have infinite nuber of solutions
// Caveat: in WTA, the equations solved with this function always have at least one solution
template <typename T>
void solve_spd_linear_equations(T& x, T& y, bool& is_solution_unique, double a00, double a01, double a11, double b0,
double b1)
{
// epsilon corresponds to two lines considered parallel if the angle between them is less than 1 tenth of degree
// 1/10 degree = 1.75e-3 radian, and 1.75e-3 squared yields 3e-6
const double epsilon = 3e-6;
assert(a00 >= 1e-8); // 1e-8 is the square of the minimal length of a face side
assert(a11 >= 1e-8);
// scale the equations so that the matrix diagonal elements = 1.0
auto a10 = a01;
a01 /= a00;
b0 /= a00;
a10 /= a11;
b1 /= a11;
// now det(A) = 1 - cos(alpha)^2 \approx alpha^2 > 0
// where alpha is the angle between the two lines whose intersection is searched for
double det = 1.0 - a01 * a10;
double enum_x = b0 - b1 * a01;
double enum_y = b1 - b0 * a10;
// in exact arithmetics, 'det' cannot be negative; but we must take into account floating-point errors
if (det < -1e-4)
{
throw std::logic_error{"det is negative"};
}
if (det < 0.0)
{
det = 0.0;
}
is_solution_unique = (det >= epsilon);
if (is_solution_unique)
{
x = enum_x / det;
y = enum_y / det;
}
else
{
// perhaps the lines are not strictly parallel: let's try to find the "exact" solution anyway
if (det > 1e-20)
{
x = enum_x / det;
y = enum_y / det;
}
else // don't use det, as it is too close to 0
{
x = 0;
y = b0 / a01;
}
}
}
} // namespace detail
/**
* @param TemplatedVec is any random-access 3-element container with operator[] returning a floating-point type
*
* Provides bool moeller:TriangleIntersects<T>::triangle(T v1, T v2, T v3, T u1, T u2, T u3);
* Provides bool moeller:TriangleIntersects<T>::triangle(T v1, T v2, T v3, T u1, T u2, T u3, T out_inters_endpoint1,
* T out_inters_endpoint2, bool out_is_coplanar);
* Provides bool moeller:TriangleIntersects<T>::box(T v1, T v2, T v3, T boxCenter, T boxHalfSize);
*/
template <typename TemplatedVec>
class TriangleIntersects
{
using Triangle = std::array<TemplatedVec, 3>;
static_assert(detail::HasSubscriptOperator<TemplatedVec, size_t>::value, "TemplatedVec must implement operator[]");
using declfloat = typename std::decay<decltype(std::declval<TemplatedVec>()[0])>::type;
public:
// distances smaller than get_tolerance() may be treated as 0
static declfloat get_tolerance() { return detail::Tolerance::get_value(); }
// angles smaller than this constant are candidates for coplanarity test
static constexpr declfloat DEFAULT_COPLANARITY_THRESHOLD_ANGLE = 0.1f; // wilde guess, so far
// Angles smaller than the constant below are used for additional test for not self intersecting
// The value of 60 degress reduces the number of self-intersecting faces reported by this module.
// whether 60, 85 or 45 or other value is better is a question of priorities.
// The difference is in the accuracy of the software to detect that two triangle *nearly* touch each other
static constexpr declfloat DEFAULT_INTERSECTION_TEST_THRESHOLD_ANGLE = 60.0f; // wilde guess, so far
static_assert(
DEFAULT_COPLANARITY_THRESHOLD_ANGLE < 5,
"default_coplanarity_threshold_angle must be small enough so that that it doesn't differ much from its sinus ");
static_assert(DEFAULT_INTERSECTION_TEST_THRESHOLD_ANGLE < 89,
"default_intersection_test_threshold_angle out of range");
static constexpr double COPLANARITY_THRESHOLD_IN_DEGREES = DEFAULT_COPLANARITY_THRESHOLD_ANGLE;
static constexpr double COPLANARITY_THRESHOLD = detail::MATH_PI / 180.0 * COPLANARITY_THRESHOLD_IN_DEGREES;
static constexpr double COPLANARITY_THRESHOLD_SQUARED = COPLANARITY_THRESHOLD * COPLANARITY_THRESHOLD;
static constexpr double sin_approximated(double x)
{
return x - x * x * x / 6.0 + x * x * x * x * x / 120.0; // approximates sin(x)
}
static constexpr double INTERSECTION_TEST_THRESHOLD =
sin_approximated(detail::MATH_PI / 180.0 * DEFAULT_INTERSECTION_TEST_THRESHOLD_ANGLE);
static constexpr double INTERSECTION_TEST_THRESHOLD_SQUARED =
INTERSECTION_TEST_THRESHOLD * INTERSECTION_TEST_THRESHOLD;
// returns true iff two triangles are intersecting or touching
// actually does not seem to be used in WTA
static bool triangle(const TemplatedVec& firstV1, const TemplatedVec& firstV2, const TemplatedVec& firstV3,
const TemplatedVec& secondV1, const TemplatedVec& secondV2, const TemplatedVec& secondV3)
{
TemplatedVec intersection_line_end_point1;
TemplatedVec intersection_line_end_point2;
detail::Coplanarity coplanarity;
return tri_tri_intersect_with_isectline(firstV1, firstV2, firstV3, secondV1, secondV2, secondV3, coplanarity,
intersection_line_end_point1, intersection_line_end_point2, false,
0); // assuming that no vertices are shared
}
// similar to the previous function, but returns additional information
// on the intersection line in the case of non-parllel triangles
// and whether the two triangles are coplanar (in which case the intersection line is undefined)
static bool triangle(const TemplatedVec& firstV1, const TemplatedVec& firstV2, const TemplatedVec& firstV3,
const TemplatedVec& secondV1, const TemplatedVec& secondV2, const TemplatedVec& secondV3,
TemplatedVec& IntersectionLineEndPoint1, TemplatedVec& IntersectionLineEndPoint2,
bool& coplanar)
{
detail::Coplanarity coplanarity;
bool result =
tri_tri_intersect_with_isectline(firstV1, firstV2, firstV3, secondV1, secondV2, secondV3, coplanarity,
IntersectionLineEndPoint1, IntersectionLineEndPoint2, true, 0);
if (detail::Coplanarity::YES == coplanarity)
{
coplanar = true;
}
else if (detail::Coplanarity::NO == coplanarity)
{
coplanar = false;
}
return result;
}
// tests if a triangle intersects a box with its faces parallel to the x-y-z Cartesian axis
static bool box(const TemplatedVec& triangleV1, const TemplatedVec& triangleV2, const TemplatedVec& triangleV3,
const TemplatedVec& boxCenter, const TemplatedVec& boxHalfSize)
{
return tri_box_overlap(triangleV1, triangleV2, triangleV3, boxCenter, boxHalfSize);
}
bool static is_wedge_colinear(const TemplatedVec& EU1, const TemplatedVec& EU2,
float tolerance = detail::Tolerance::get_value() / 10.0f)
{
auto surface_u = 0.5f * EU1.cross(EU2).abs();
auto max_side_u = std::max({(EU2 - EU1).abs(), EU1.abs(), EU2.abs()});
auto min_height_u = surface_u / max_side_u;
return min_height_u < tolerance;
}
// The function returns a vector to a line defined by LineVec
// N is a vector normal to another (reference) plane
// N is the preferred result
static TemplatedVec normal_to_line_and_within_plane(TemplatedVec N, TemplatedVec LineVec)
{
normalize(LineVec);
auto d = std::abs(dot(LineVec, N));
if (d < 1e-8)
{ // N is orthogonal do LineVec
return N;
}
int idx = index_into_smallest_component_abs(LineVec);
TemplatedVec n0{static_cast<float>(idx == 0), static_cast<float>(idx == 1), static_cast<float>(idx == 2)};
TemplatedVec result;
cross(result, n0, LineVec);
return result;
}
private:
struct Triplet
{
declfloat x; // x-coordinate
declfloat w; // weight
int idx; // identifier of the segment's end, 0 or 1
bool operator<(const Triplet& rhs) const { return x < rhs.x; }
};
// Constants definitions
static constexpr size_t X = 0;
static constexpr size_t Y = 1;
static constexpr size_t Z = 2;
// Helper methods
// returns the index into the the largest-magnitude component of the argument
inline static unsigned index_into_largest_component_abs(const TemplatedVec& D)
{
declfloat a = fabs(D[0]);
declfloat b = fabs(D[1]);
declfloat c = fabs(D[2]);
if (a < b)
{
if (b < c)
{
return 2; // c is largest
}
{
return 1; // b is largest
}
}
else
{
if (a < c)
{
return 2; // c is largest
}
{
return 0; // a is largest
}
}
}
// returns the index into the the largest-magnitude component of the argument
inline static unsigned index_into_smallest_component_abs(const TemplatedVec& D)
{
declfloat a = fabs(D[0]);
declfloat b = fabs(D[1]);
declfloat c = fabs(D[2]);
if (a < b)
{
if (a < c)
{
return 0; // a is largest
}
{
return 2; // c is largest
}
}
else
{
if (b < c)
{
return 1; // b is smallest
}
{
return 2; // c is smallest
}
}
}
static unsigned index_into_largest_component_abs(std::array<declfloat, 3> V)
{
return index_into_largest_component_abs(TemplatedVec{V[0], V[1], V[2]});
}
// cross product
inline static void cross(TemplatedVec& dest, const TemplatedVec& v1, const TemplatedVec& v2)
{
dest[X] = v1[Y] * v2[Z] - v1[Z] * v2[Y];
dest[Y] = v1[Z] * v2[X] - v1[X] * v2[Z];
dest[Z] = v1[X] * v2[Y] - v1[Y] * v2[X];
}
inline static TemplatedVec guarded_cross_product(const TemplatedVec& v1, const TemplatedVec& v2)
{
constexpr declfloat MACHINE_EPSILON_F = std::numeric_limits<declfloat>::epsilon();
TemplatedVec product;
cross(product, v1, v2);
using std::abs;
if (abs(product[0]) < (abs(v1[1] * v2[2]) + abs(v2[1] * v1[2])) * MACHINE_EPSILON_F)
{
product[0] = 0;
}
if (abs(product[1]) < (abs(v1[2] * v2[0]) + abs(v2[2] * v1[0])) * MACHINE_EPSILON_F)
{
product[1] = 0;
}
if (abs(product[2]) < (abs(v1[0] * v2[1]) + abs(v2[0] * v1[1])) * MACHINE_EPSILON_F)
{
product[2] = 0;
}
return product;
}
// dot product
inline static declfloat dot(const TemplatedVec& v1, const TemplatedVec& v2)
{
return v1[X] * v2[X] + v1[Y] * v2[Y] + v1[Z] * v2[Z];
}
inline static declfloat norm(const TemplatedVec& v) { return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); }
// vector normalization
inline static bool normalize(TemplatedVec& v)
{
constexpr double ZERO_LENGTH_THRESHOLD = 1e-20;
double d_norm = norm(v);
if (d_norm > ZERO_LENGTH_THRESHOLD)
{
v[0] /= d_norm;
v[1] /= d_norm;
v[2] /= d_norm;
return true;
}
return false;
}
// vector subtraction
inline static void sub(TemplatedVec& dest, const TemplatedVec& v1, const TemplatedVec& v2)
{
dest[X] = v1[X] - v2[X];
dest[Y] = v1[Y] - v2[Y];
dest[Z] = v1[Z] - v2[Z];
}
// vector addition
inline static void add(TemplatedVec& dest, const TemplatedVec& v1, const TemplatedVec& v2)
{
dest[X] = v1[X] + v2[X];
dest[Y] = v1[Y] + v2[Y];
dest[Z] = v1[Z] + v2[Z];
}
// vector product by scalar
inline static void mult(TemplatedVec& dest, const TemplatedVec& v, const declfloat factor)
{
dest[X] = factor * v[X];
dest[Y] = factor * v[Y];
dest[Z] = factor * v[Z];
}
// assignment
inline static void set(TemplatedVec& dest, const TemplatedVec& src)
{
dest[X] = src[X];
dest[Y] = src[Y];
dest[Z] = src[Z];
}
inline static void find_min_max(const declfloat x0, const declfloat x1, const declfloat x2, declfloat& min,
declfloat& max)
{
min = std::min(std::min(x0, x1), x2);
max = std::max(std::max(x0, x1), x2);
}
// Tests for Box-Triangle
inline static bool axis_test_x01(const TemplatedVec& v0, const TemplatedVec& v2, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p0 = a * v0[Y] - b * v0[Z];
declfloat p2 = a * v2[Y] - b * v2[Z];
if (p0 < p2)
{
min = p0;
max = p2;
}
else
{
min = p2;
max = p0;
}
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z];
return !static_cast<bool>(min > rad || max < -rad);
}
inline static bool axis_test_x2(const TemplatedVec& v0, const TemplatedVec& v1, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p0 = a * v0[Y] - b * v0[Z];
declfloat p1 = a * v1[Y] - b * v1[Z];
if (p0 < p1)
{
min = p0;
max = p1;
}
else
{
min = p1;
max = p0;
}
rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z];
return !static_cast<bool>(min > rad || max < -rad);
}
inline static bool axis_test_y02(const TemplatedVec& v0, const TemplatedVec& v2, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p0 = -a * v0[X] + b * v0[Z];
declfloat p2 = -a * v2[X] + b * v2[Z];
if (p0 < p2)
{
min = p0;
max = p2;
}
else
{
min = p2;
max = p0;
}
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z];
return !static_cast<bool>(min > rad || max < -rad);
}
inline static bool axis_test_y1(const TemplatedVec& v0, const TemplatedVec& v1, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p0 = -a * v0[X] + b * v0[Z];
declfloat p1 = -a * v1[X] + b * v1[Z];
if (p0 < p1)
{
min = p0;
max = p1;
}
else
{
min = p1;
max = p0;
}
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z];
return !static_cast<bool>(min > rad || max < -rad);
}
inline static bool axis_test_z12(const TemplatedVec& v1, const TemplatedVec& v2, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p1 = a * v1[X] - b * v1[Y];
declfloat p2 = a * v2[X] - b * v2[Y];
if (p2 < p1)
{
min = p2;
max = p1;
}
else
{
min = p1;
max = p2;
}
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y];
return !static_cast<bool>(min > rad || max < -rad);
}
inline static bool axis_test_z0(const TemplatedVec& v0, const TemplatedVec& v1, const TemplatedVec& boxhalfsize,
const declfloat a, const declfloat b, const declfloat fa, const declfloat fb,
declfloat& min, declfloat& max, declfloat& rad)
{
declfloat p0 = a * v0[X] - b * v0[Y];
declfloat p1 = a * v1[X] - b * v1[Y];
if (p0 < p1)
{
min = p0;
max = p1;
}
else
{
min = p1;
max = p0;
}
rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y];
return !static_cast<bool>(min > rad || max < -rad);
}
/********************************************
* Tests for Triangle-Triangle intersection *
********************************************/
// Tests whether edge U0, U1 intersects with the edge whose origin is V0 and the coordinates
// of the vector pointing towards the second vertex are Ax (along i0 axis) and Ay (along i1 axis).
// A = V1 - V0
// i0 stands for the "x" axis and "i1" for the "y" axis in the new (local) coordinate system.
// The vertices are projected onto the i0-i1 plane before the actual intersection detection is performed
//
// Caveat! This test most likely fails if the two edges ar colinear
// But this doesn't disturb the final result (other tests should detect the intersection)
inline static bool edge_edge_test(const TemplatedVec& V0, const TemplatedVec& U0, const TemplatedVec& U1,
const size_t i0, const size_t i1, declfloat Ax, declfloat Ay)
{
constexpr declfloat TOLERANCE = 1e-10;
declfloat bx, by, cx, cy, f, d, e;
bx = U0[i0] - U1[i0]; // B = U0 - U1 (projected onto the i0-i1 plane)
by = U0[i1] - U1[i1];
cx = V0[i0] - U0[i0]; // C = V0 - U0 (projected onto the i0-i1 plane)
cy = V0[i1] - U0[i1];
// if the edges intersect, |f| is twice the area of the convex quadrilateral spanned by the vertices
f = Ay * bx - Ax * by;
// if the edges intersect, |d| is twice the area of the triangle U0, U1, V0
d = by * cx - bx * cy;
if ((f > 0 && d >= 0 && d <= f) || (f < 0 && d <= 0 && d >= f))
{
// if the edges intersect, |e| is twice the area of the triangle V0, V1, U0
e = Ax * cy - Ay * cx;
if (f > 0)
{
if (e >= 0 && e <= f)
{
return true;
}
}
else
{
if (e <= 0 && e >= f)
{
return true;
}
}
}
// all Vertices are colinear iff f == 0 and d == 0
if (std::abs(d) < TOLERANCE && std::abs(f) < TOLERANCE)
{
// Let B = U1 - U0
bx = -bx;
by = -by;
// Let A = V1 - U0;
Ax += cx;
Ay += cy;
// Now B = U1 - U0, A = V1 - U0, C = V0 - U0, so we have 3 points realtive to U0.
// Let's test if [(0,0), B] overlaps with [A, C]
if (std::abs(by) > std::abs(bx))
{
std::swap(Ax, Ay);
std::swap(bx, by);
std::swap(cx, cy);
}
// Now it suffices to inspect the projection on the x axis
if (cx < Ax)
{
std::swap(Ax, cx);
}
return Ax < bx && cx > 0;
}
return false;
}
// V0, V1 define an edge
// U0, U1, U2 define a triangle
// i0, i1 \in {0,1,2} define the plane (x-y, x-z or y-z) onto which all five vertices are projected
inline static bool edge_against_tri_edge(const TemplatedVec& V0, const TemplatedVec& V1, const TemplatedVec& U0,
const TemplatedVec& U1, const TemplatedVec& U2, const size_t i0,
const size_t i1)
{
// don't count exact edge matches or a single shared vertex as intersection
int count = 0;
if (V0[0] == U0[0] && V0[1] == U0[1] && V0[2] == U0[2])
count++;
if (V0[0] == U1[0] && V0[1] == U1[1] && V0[2] == U1[2])
count++;
if (V0[0] == U2[0] && V0[1] == U2[1] && V0[2] == U2[2])
count++;
if (V1[0] == U0[0] && V1[1] == U0[1] && V1[2] == U0[2])
count++;
if (V1[0] == U1[0] && V1[1] == U1[1] && V1[2] == U1[2])
count++;
if (V1[0] == U2[0] && V1[1] == U2[1] && V1[2] == U2[2])
count++;
if (count == 1 || count == 2)
return false;
declfloat ax, ay; // coordinates of the edge relative to V0
ax = V1[i0] - V0[i0];
ay = V1[i1] - V0[i1];
/* test intersection of edge U0, U1 with edge V0, V1 */
if (edge_edge_test(V0, U0, U1, i0, i1, ax, ay))
{
return true;
}
/* test edge U1,U2 against V1 - V0 */
if (edge_edge_test(V0, U1, U2, i0, i1, ax, ay))
{
return true;
}
/* test edge U2,U1 against V1 - V0 */
if (edge_edge_test(V0, U2, U0, i0, i1, ax, ay))
{
return true;
}
return false;
}
// Computes extreme intersection points (endpoints) between a triangle and the plane of another triangle.
// It must have been already established that such intersection exists.
// It is assumed that V0 is on the other side of the plane than V1 and V2
// so that segments V0-V1 and V0-V2 intersect the plane (some, but not all of them may lie on the plane itself)
// The algorithm is based on similarity of triangles (Thales theorem)
//
// CAVEAT! How this function can possibly work correctly if d0, d1 and d2 can hold the incorrect value of 0
// if their actual value magnitudes are < EPSILON?
// Unmodified triangle A "sees" modified triangle B, and unmodified triangle B "sees" modified triangle A
// how can this be correct for general triangles A,B?
inline static void isect2(const TemplatedVec& V0, // vertex 0
const TemplatedVec& V1, // vertex 1
const TemplatedVec& V2, // vertex 2
const declfloat x0, // projection of vetrex 0 on the "safe" axis (x, y, or z)
const declfloat x1, // -,,- vertex 1
const declfloat x2, // -,,- vertex 2
const declfloat d0, // signed distance of vertex 0 from the other plane
const declfloat d1, // -,,- vertex 1
const declfloat d2, // -,,- vertex 2
declfloat& endpoint_x_0, // intersection endpoint 0 on the "safe" axsis
declfloat& endpoint_x_1, // intersection endpoint 1 on the "safe" axsis
TemplatedVec& Endpoint_0, // intersection endpoint 0 in 3D
TemplatedVec& Endpoint_1) // intersection endpoint 1 in 3D
{
assert(d0 != d1); // moreover, d0 and d1 must have different signs: +, 0 or -
assert(d0 != d2); // moreover, d0 and d2 must have different signs: +, 0 or -
std::array<Triplet, 2> x_w_pairs;
declfloat w = d0 / (d0 - d1);
detail::clip_to_01(w);
declfloat x = x0 + (x1 - x0) * w;
x_w_pairs[0] = Triplet{x, w, 0};
w = d0 / (d0 - d2);
detail::clip_to_01(w);
x = x0 + (x2 - x0) * w;
x_w_pairs[1] = Triplet{x, w, 1};
if (x_w_pairs[1] < x_w_pairs[0])
{
std::swap(x_w_pairs[0], x_w_pairs[1]);
}
endpoint_x_0 = x_w_pairs[0].x;
endpoint_x_1 = x_w_pairs[1].x;
w = x_w_pairs[0].w;
int idx = x_w_pairs[0].idx;
TemplatedVec v_other = (idx == 0) ? V1 : V2;
TemplatedVec displacement;
sub(displacement, v_other, V0);
mult(displacement, displacement, w); // Displacement = w * (V1 - V0)
add(Endpoint_0, displacement, V0); // Endpoint_0 = V0 + w * (V1 - V0)
w = x_w_pairs[1].w;
idx = x_w_pairs[1].idx;
v_other = (idx == 0) ? V1 : V2;
sub(displacement, v_other, V0);
mult(displacement, displacement, w);
add(Endpoint_1, V0, displacement); // Endpoint_1 = V0 + w *(V2 - V0)
}
// Similar to isect2, but called when it is certain that V0 is the shared vertex
// and neither V1 nor V2 lies closer to the target plane than detail::Tolerance::get_value()
// There's no way to verify this condition within the function
inline static void isect2_shared_at_v0 //
(const TemplatedVec& V0, // vertex 0
const declfloat x0, // projection of vertex 0 on the "safe" axis (x, y, or z)
declfloat& endpoint_x_0, // intersection endpoint 0 on the "safe" axsis
declfloat& endpoint_x_1, // intersection endpoint 1 on the "safe" axsis
TemplatedVec& Endpoint_0, // intersection endpoint 0 in 3D
TemplatedVec& Endpoint_1 // intersection endpoint 1 in 3D
)
{
endpoint_x_0 = endpoint_x_1 = x0;
Endpoint_0 = Endpoint_1 = V0;
}
// Similar to isect2, but called when it is certain that V1 is the shared vertex
// There's no way to verify this condition within the function
inline static void isect2_shared_at_v1 //
(const TemplatedVec& V0, // vertex 0
const TemplatedVec& V1, // vertex 1
const TemplatedVec& V2, // vertex 2
const declfloat x0, // projection of vertex 0 on the "safe" axis (x, y, or z)
const declfloat x1, // -,,- vertex 1
const declfloat x2, // -,,- vertex 2
declfloat d0, // signed distance of vertex 0 from the other plane
declfloat d2, // -,,- vertex 2
declfloat& endpoint_x_0, // intersection endpoint 0 on the "safe" axsis
declfloat& endpoint_x_1, // intersection endpoint 1 on the "safe" axsis
TemplatedVec& Endpoint_0, // intersection endpoint 0 in 3D
TemplatedVec& Endpoint_1) // intersection endpoint 1 in 3D
{
// d0 and d2 must have different signs: +, 0 or -
if (d0 * d2 > 0)
{
// here either d0 or d2 must be small, hardly distingusihable from 0;
assert(std::abs(d0) <= detail::Tolerance::get_value() || std::abs(d2) <= detail::Tolerance::get_value());
if (fabs(d0) < fabs(d2))
{
d0 = 0;
}
else
{
d2 = 0;
}
}
std::array<Triplet, 3> x_w_pairs;
unsigned arr_idx = 0;
x_w_pairs[arr_idx++] = Triplet{x1, 1.0, 0};
if (std::abs(d0) < detail::Tolerance::get_value())
{
x_w_pairs[arr_idx++] = Triplet{x0, 0.0, 0};
}
if (std::abs(d2) < detail::Tolerance::get_value())
{
x_w_pairs[arr_idx++] = Triplet{x2, 1.0, 1};
}
else
{
declfloat w = d0 / (d0 - d2); // weight, between 0 and 1
detail::clip_to_01(w);
declfloat x = x0 + (x2 - x0) * w;
x_w_pairs[arr_idx++] = Triplet{x, w, 1};
}
assert(arr_idx <= 3);
auto p = std::minmax_element(x_w_pairs.begin(), x_w_pairs.begin() + arr_idx);
endpoint_x_0 = p.first->x;
endpoint_x_1 = p.second->x;
declfloat w = p.first->w;
int idx = p.first->idx;
TemplatedVec v_other = (idx == 0) ? V1 : V2;
TemplatedVec displacement;
sub(displacement, v_other, V0);
mult(displacement, displacement, w); // Displacement = w * (V1 - V0)
add(Endpoint_0, displacement, V0); // Endpoint_0 = V0 + w * (V1 - V0)
w = p.second->w;
idx = p.second->idx;
v_other = (idx == 0) ? V1 : V2;
sub(displacement, v_other, V0);
mult(displacement, displacement, w);
add(Endpoint_1, V0, displacement); // Endpoint_1 = V0 + w *(V2 - V0)
}
inline static bool point_in_tri(const TemplatedVec& V0, const TemplatedVec& U0, const TemplatedVec& U1,
const TemplatedVec& U2, const size_t i0, const size_t i1)
{
declfloat a, b, c, d0, d1, d2;
/* is T1 completly inside T2? */
/* check if V0 is inside tri(U0,U1,U2) */
a = U1[i1] - U0[i1];
b = -(U1[i0] - U0[i0]);
c = -a * U0[i0] - b * U0[i1];
d0 = a * V0[i0] + b * V0[i1] + c;
a = U2[i1] - U1[i1];
b = -(U2[i0] - U1[i0]);
c = -a * U1[i0] - b * U1[i1];
d1 = a * V0[i0] + b * V0[i1] + c;
a = U0[i1] - U2[i1];
b = -(U0[i0] - U2[i0]);
c = -a * U2[i0] - b * U2[i1];
d2 = a * V0[i0] + b * V0[i1] + c;
if (d0 * d1 > 0.0)
{
if (d0 * d2 > 0.0)
{
return true;
}
}
return false;
}
// Private methods
static bool plane_box_overlap(const TemplatedVec& normal, const TemplatedVec& vert,
const TemplatedVec& maxbox) // -NJMP-
{
size_t q;
declfloat v;
TemplatedVec vmin, vmax;
for (q = X; q <= Z; q++)
{
v = vert[q];
if (normal[q] > 0.0f)
{
vmin[q] = -maxbox[q] - v;
vmax[q] = maxbox[q] - v;
}
else
{
vmin[q] = maxbox[q] - v;
vmax[q] = -maxbox[q] - v;
}
}
if (dot(normal, vmin) > 0.0f)
{
return false;
}
return static_cast<bool>(dot(normal, vmax) >= 0.0f);
}
static bool tri_box_overlap(const TemplatedVec& trivert0, const TemplatedVec& trivert1,
const TemplatedVec& trivert2, const TemplatedVec& boxcenter,
const TemplatedVec& boxhalfsize)
{
/* use separating axis theorem to test overlap between triangle and box */
/* need to test for overlap in these directions: */
/* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
/* we do not even need to test these) */
/* 2) normal of the triangle */
/* 3) crossproduct(edge from tri, {x,y,z}-directin) */
/* this gives 3x3=9 more tests */
TemplatedVec v0, v1, v2;
declfloat min, max, rad, fex, fey, fez;
TemplatedVec normal, e0, e1, e2;
/* This is the fastest branch on Sun */
/* move everything so that the boxcenter is in (0,0,0) */
sub(v0, trivert0, boxcenter);
sub(v1, trivert1, boxcenter);
sub(v2, trivert2, boxcenter);
/* compute triangle edges */
sub(e0, v1, v0); /* tri edge 0 */
sub(e1, v2, v1); /* tri edge 1 */
sub(e2, v0, v2); /* tri edge 2 */
/* Bullet 3: */
/* test the 9 tests first (this was faster) */
fex = fabsf(e0[X]);
fey = fabsf(e0[Y]);
fez = fabsf(e0[Z]);
if (!axis_test_x01(v0, v2, boxhalfsize, e0[Z], e0[Y], fez, fey, min, max, rad))
{
return false;
}
if (!axis_test_y02(v0, v2, boxhalfsize, e0[Z], e0[X], fez, fex, min, max, rad))
{
return false;
}
if (!axis_test_z12(v1, v2, boxhalfsize, e0[Y], e0[X], fey, fex, min, max, rad))
{
return false;
}
fex = fabsf(e1[X]);
fey = fabsf(e1[Y]);
fez = fabsf(e1[Z]);
if (!axis_test_x01(v0, v2, boxhalfsize, e1[Z], e1[Y], fez, fey, min, max, rad))
{
return false;
}
if (!axis_test_y02(v0, v2, boxhalfsize, e1[Z], e1[X], fez, fex, min, max, rad))
{
return false;
}
if (!axis_test_z0(v0, v1, boxhalfsize, e1[Y], e1[X], fey, fex, min, max, rad))
{
return false;
}
fex = fabsf(e2[X]);
fey = fabsf(e2[Y]);
fez = fabsf(e2[Z]);
if (!axis_test_x2(v0, v1, boxhalfsize, e2[Z], e2[Y], fez, fey, min, max, rad))
{
return false;
}
if (!axis_test_y1(v0, v1, boxhalfsize, e2[Z], e2[X], fez, fex, min, max, rad))
{
return false;
}
if (!axis_test_z12(v1, v2, boxhalfsize, e2[Y], e2[X], fey, fex, min, max, rad))
{
return false;
}
/* Bullet 1: */
/* first test overlap in the {x,y,z}-directions */
/* find min, max of the triangle each direction, and test for overlap in */
/* that direction -- this is equivalent to testing a minimal AABB around */
/* the triangle against the AABB */
/* test in X-direction */
find_min_max(v0[X], v1[X], v2[X], min, max);
if (min > boxhalfsize[X] || max < -boxhalfsize[X])
{
return false;
}
/* test in Y-direction */
find_min_max(v0[Y], v1[Y], v2[Y], min, max);
if (min > boxhalfsize[Y] || max < -boxhalfsize[Y])
{
return false;
}
/* test in Z-direction */
find_min_max(v0[Z], v1[Z], v2[Z], min, max);
if (min > boxhalfsize[Z] || max < -boxhalfsize[Z])
{
return false;
}
/* Bullet 2: */
/* test if the box intersects the plane of the triangle */
/* compute plane equation of triangle: normal*x+d=0 */
cross(normal, e0, e1);
return static_cast<bool>(plane_box_overlap(normal, v0, boxhalfsize)); /* box and triangle overlaps */
}
public:
// looks for the largest value of orig_du and copies it to the corresponding value: either du0, du1 or du2.
static void restore_true_value_of_dx_max(declfloat& du0, declfloat& du1, declfloat& du2,
const std::array<declfloat, 3>& orig_du)
{
auto it = std::max_element(orig_du.begin(), orig_du.end(),
[](auto x, auto y)
{
return std::abs(x) < std::abs(y);
});
auto dist = std::distance(orig_du.begin(), it);
switch (dist)
{
case 0:
du0 = orig_du[0];
break;
case 1:
du1 = orig_du[1];
break;
case 2:
du2 = orig_du[2];
break;
default: