forked from gradientspace/geometry3Sharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDMeshAABBTree.cs
2184 lines (1769 loc) · 94 KB
/
DMeshAABBTree.cs
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
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
namespace g3
{
/// <summary>
/// Hierarchical Axis-Aligned-Bounding-Box tree for a DMesh3 mesh.
/// This class supports a variety of spatial queries, listed below.
///
/// Various construction strategies are also available, the default is the
/// fastest to build but if you are doing a *lot* of queries, you might experiment
/// with the others (eg TopDownMedian)
///
/// Available queries:
/// - FindNearestTriangle(point, maxdist)
/// - FindNearestHitTriangle(ray, maxdist)
/// - FindAllHitTriangles(ray, maxdist)
/// - TestIntersection(triangle)
/// - TestIntersection(mesh)
/// - TestIntersection(otherAABBTree)
/// - FindAllIntersections(otherAABBTree)
/// - FindNearestTriangles(otherAABBTree, maxdist)
/// - IsInside(point)
/// - WindingNumber(point)
/// - DoTraversal(generic_traversal_object)
///
/// </summary>
public class DMeshAABBTree3 : ISpatial
{
protected DMesh3 mesh;
protected int mesh_timestamp;
public DMeshAABBTree3(DMesh3 m, bool autoBuild = false)
{
mesh = m;
if (autoBuild)
Build();
}
public DMesh3 Mesh { get { return mesh; } }
/// <summary>
/// If non-null, only triangle IDs that pass this filter (ie filter is true) are considered
/// </summary>
public Func<int, bool> TriangleFilterF = null;
// Top-down build strategies will put at most this many triangles into a box.
// Larger value == shallower trees, but leaves cost more to test
public int TopDownLeafMaxTriCount = 4;
// bottom-up FastVolumeMetric cluster policy sorts available boxes along an axis
// and then proceeds from min to max, greedily grouping best-pairs. This value determines
// the range of the greedy search. Larger == slower but better bounding
// (at least in theory...)
public int BottomUpClusterLookahead = 10;
// how should we build the tree?
public enum BuildStrategy
{
Default, // currently TopDownMidpoint
TopDownMidpoint, // Recursively split triangle set by midpoint of axis interval.
// This is the fastest and usually produces lower total-volume trees than bottom-up.
// Resulting trees are unbalanced, though.
BottomUpFromOneRings, // Construct leaf layer based on triangle one-rings, and then
// cluster boxes to build tree upward. Various cluster policies (below).
// About 2.5x slower than TopDownMidpoint. Trees are basically balanced, although
// current approach to clustering odd-count layers is to duplicate the extra box.
TopDownMedian // Like TopDownMidpoint except we sort the triangle lists at each step and split on the median.
// 2-4x slower than TopDownMidpoint, but trees are generally more efficient and balanced.
}
public enum ClusterPolicy
{
Default, // currently FastVolumeMetric
Fastest, // sort list and then just cluster sequential boxes.
// Tree efficiency suffers, but fast.
FastVolumeMetric, // sort list and then check next N boxes for best cluster. Only slightly slower than
// sequential clustering but trees are often quite a bit more efficient.
MinimalVolume // compute full pair matrix at each step (N^2), and sequentially pick out best pairs.
// this usually does quite a bit better job, but it is unusable for large tri counts.
}
// Build the tree. Policy only matters for bottom-up strategies
public void Build(BuildStrategy eStrategy = BuildStrategy.TopDownMidpoint,
ClusterPolicy ePolicy = ClusterPolicy.Default)
{
if (eStrategy == BuildStrategy.BottomUpFromOneRings)
build_by_one_rings(ePolicy);
else if (eStrategy == BuildStrategy.TopDownMedian)
build_top_down(true);
else if (eStrategy == BuildStrategy.TopDownMidpoint)
build_top_down(false);
else if (eStrategy == BuildStrategy.Default)
build_top_down(false);
mesh_timestamp = mesh.ShapeTimestamp;
}
public bool IsValid { get { return mesh_timestamp == mesh.ShapeTimestamp; } }
/// <summary>
/// Does this ISpatial implementation support nearest-point query? (yes)
/// </summary>
public bool SupportsNearestTriangle { get { return true; } }
/// <summary>
/// Find the triangle closest to p, within distance fMaxDist, or return InvalidID
/// Use MeshQueries.TriangleDistance() to get more information
/// </summary>
public virtual int FindNearestTriangle(Vector3d p, double fMaxDist = double.MaxValue)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.FindNearestTriangle: mesh has been modified since tree construction");
double fNearestSqr = (fMaxDist < double.MaxValue) ? fMaxDist * fMaxDist : double.MaxValue;
int tNearID = DMesh3.InvalidID;
find_nearest_tri(root_index, p, ref fNearestSqr, ref tNearID);
return tNearID;
}
protected void find_nearest_tri(int iBox, Vector3d p, ref double fNearestSqr, ref int tID)
{
int idx = box_to_index[iBox];
if ( idx < triangles_end ) { // triange-list case, array is [N t1 t2 ... tN]
int num_tris = index_list[idx];
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
double fTriDistSqr = MeshQueries.TriDistanceSqr(mesh, ti, p);
if ( fTriDistSqr < fNearestSqr ) {
fNearestSqr = fTriDistSqr;
tID = ti;
}
}
} else { // internal node, either 1 or 2 child boxes
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
double fChild1DistSqr = box_distance_sqr(iChild1, p);
if ( fChild1DistSqr <= fNearestSqr )
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
} else { // 2 children, descend closest first
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
double fChild1DistSqr = box_distance_sqr(iChild1, p);
double fChild2DistSqr = box_distance_sqr(iChild2, p);
if (fChild1DistSqr < fChild2DistSqr) {
if (fChild1DistSqr < fNearestSqr) {
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
if (fChild2DistSqr < fNearestSqr)
find_nearest_tri(iChild2, p, ref fNearestSqr, ref tID);
}
} else {
if (fChild2DistSqr < fNearestSqr) {
find_nearest_tri(iChild2, p, ref fNearestSqr, ref tID);
if (fChild1DistSqr < fNearestSqr)
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
}
}
}
}
}
/// <summary>
/// Find the vertex closest to p, within distance fMaxDist, or return InvalidID
/// </summary>
public virtual int FindNearestVertex(Vector3d p, double fMaxDist = double.MaxValue)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.FindNearestVertex: mesh has been modified since tree construction");
double fNearestSqr = (fMaxDist < double.MaxValue) ? fMaxDist * fMaxDist : double.MaxValue;
int vNearID = DMesh3.InvalidID;
find_nearest_vtx(root_index, p, ref fNearestSqr, ref vNearID);
return vNearID;
}
protected void find_nearest_vtx(int iBox, Vector3d p, ref double fNearestSqr, ref int vid)
{
int idx = box_to_index[iBox];
if (idx < triangles_end) { // triange-list case, array is [N t1 t2 ... tN]
int num_tris = index_list[idx];
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
Vector3i tv = mesh.GetTriangle(ti);
for ( int j = 0; j < 3; ++j ) {
double dsqr = mesh.GetVertex(tv[j]).DistanceSquared(ref p);
if ( dsqr < fNearestSqr ) {
fNearestSqr = dsqr;
vid = tv[j];
}
}
}
} else { // internal node, either 1 or 2 child boxes
int iChild1 = index_list[idx];
if (iChild1 < 0) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
double fChild1DistSqr = box_distance_sqr(iChild1, p);
if (fChild1DistSqr <= fNearestSqr)
find_nearest_vtx(iChild1, p, ref fNearestSqr, ref vid);
} else { // 2 children, descend closest first
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
double fChild1DistSqr = box_distance_sqr(iChild1, p);
double fChild2DistSqr = box_distance_sqr(iChild2, p);
if (fChild1DistSqr < fChild2DistSqr) {
if (fChild1DistSqr < fNearestSqr) {
find_nearest_vtx(iChild1, p, ref fNearestSqr, ref vid);
if (fChild2DistSqr < fNearestSqr)
find_nearest_vtx(iChild2, p, ref fNearestSqr, ref vid);
}
} else {
if (fChild2DistSqr < fNearestSqr) {
find_nearest_vtx(iChild2, p, ref fNearestSqr, ref vid);
if (fChild1DistSqr < fNearestSqr)
find_nearest_vtx(iChild1, p, ref fNearestSqr, ref vid);
}
}
}
}
}
/// <summary>
/// Does this ISpatial implementation support ray-triangle intersection? (yes)
/// </summary>
public bool SupportsTriangleRayIntersection { get { return true; } }
/// <summary>
/// find id of first triangle that ray hits, within distance fMaxDist, or return DMesh3.InvalidID
/// Use MeshQueries.TriangleIntersection() to get more information
/// </summary>
public virtual int FindNearestHitTriangle(Ray3d ray, double fMaxDist = double.MaxValue)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.FindNearestHitTriangle: mesh has been modified since tree construction");
if (ray.Direction.IsNormalized == false)
throw new Exception("DMeshAABBTree3.FindNearestHitTriangle: ray direction is not normalized");
// [RMS] note: using float.MaxValue here because we need to use <= to compare box hit
// to fNearestT, and box hit returns double.MaxValue on no-hit. So, if we set
// nearestT to double.MaxValue, then we will test all boxes (!)
double fNearestT = (fMaxDist < double.MaxValue) ? fMaxDist : float.MaxValue;
int tNearID = DMesh3.InvalidID;
find_hit_triangle(root_index, ref ray, ref fNearestT, ref tNearID);
return tNearID;
}
protected void find_hit_triangle(int iBox, ref Ray3d ray, ref double fNearestT, ref int tID)
{
int idx = box_to_index[iBox];
if ( idx < triangles_end ) { // triange-list case, array is [N t1 t2 ... tN]
Triangle3d tri = new Triangle3d();
int num_tris = index_list[idx];
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2);
double rayt;
if (IntrRay3Triangle3.Intersects(ref ray, ref tri.V0, ref tri.V1, ref tri.V2, out rayt)) {
if (rayt < fNearestT) {
fNearestT = rayt;
tID = ti;
}
}
//IntrRay3Triangle3 ray_tri_hit = new IntrRay3Triangle3(ray, tri);
//if ( ray_tri_hit.Find() ) {
// if ( ray_tri_hit.RayParameter < fNearestT ) {
// fNearestT = ray_tri_hit.RayParameter;
// tID = ti;
// }
//}
}
} else { // internal node, either 1 or 2 child boxes
double e = MathUtil.ZeroTolerancef;
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
double fChild1T = box_ray_intersect_t(iChild1, ray);
if (fChild1T <= fNearestT + e) {
find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID);
}
} else { // 2 children, descend closest first
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
double fChild1T = box_ray_intersect_t(iChild1, ray);
double fChild2T = box_ray_intersect_t(iChild2, ray);
if (fChild1T < fChild2T) {
if (fChild1T <= fNearestT + e) {
find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID);
if (fChild2T <= fNearestT + e) {
find_hit_triangle(iChild2, ref ray, ref fNearestT, ref tID);
}
}
} else {
if (fChild2T <= fNearestT + e) {
find_hit_triangle(iChild2, ref ray, ref fNearestT, ref tID);
if (fChild1T <= fNearestT + e) {
find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID);
}
}
}
}
}
}
/// <summary>
/// Find the ids of all the triangles that they ray intersects, within distance fMaxDist from ray origin
/// Returns count of triangles.
/// </summary>
public virtual int FindAllHitTriangles(Ray3d ray, List<int> hitTriangles = null, double fMaxDist = double.MaxValue)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.FindNearestHitTriangle: mesh has been modified since tree construction");
if (ray.Direction.IsNormalized == false)
throw new Exception("DMeshAABBTree3.FindNearestHitTriangle: ray direction is not normalized");
// [RMS] note: using float.MaxValue here because we need to use <= to compare box hit
// to fNearestT, and box hit returns double.MaxValue on no-hit. So, if we set
// nearestT to double.MaxValue, then we will test all boxes (!)
double fUseMaxDist = (fMaxDist < double.MaxValue) ? fMaxDist : float.MaxValue;
int nCount = find_all_hit_triangles(root_index, hitTriangles, ref ray, fUseMaxDist);
return nCount;
}
protected int find_all_hit_triangles(int iBox, List<int> hitTriangles, ref Ray3d ray, double fMaxDist)
{
int hit_count = 0;
int idx = box_to_index[iBox];
if ( idx < triangles_end ) { // triange-list case, array is [N t1 t2 ... tN]
Triangle3d tri = new Triangle3d();
int num_tris = index_list[idx];
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2);
double rayt;
if (IntrRay3Triangle3.Intersects(ref ray, ref tri.V0, ref tri.V1, ref tri.V2, out rayt)) {
if (rayt < fMaxDist) {
if (hitTriangles != null)
hitTriangles.Add(ti);
hit_count++;
}
}
//IntrRay3Triangle3 ray_tri_hit = new IntrRay3Triangle3(ray, tri);
//if (ray_tri_hit.Find()) {
// if (ray_tri_hit.RayParameter < fMaxDist) {
// if (hitTriangles != null)
// hitTriangles.Add(ti);
// hit_count++;
// }
//}
}
} else { // internal node, either 1 or 2 child boxes
double e = MathUtil.ZeroTolerancef;
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
double fChild1T = box_ray_intersect_t(iChild1, ray);
if (fChild1T <= fMaxDist + e)
hit_count += find_all_hit_triangles(iChild1, hitTriangles, ref ray, fMaxDist);
} else { // 2 children, descend closest first
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
double fChild1T = box_ray_intersect_t(iChild1, ray);
if (fChild1T <= fMaxDist + e)
hit_count += find_all_hit_triangles(iChild1, hitTriangles, ref ray, fMaxDist);
double fChild2T = box_ray_intersect_t(iChild2, ray);
if (fChild2T <= fMaxDist + e)
hit_count += find_all_hit_triangles(iChild2, hitTriangles, ref ray, fMaxDist);
}
}
return hit_count;
}
/// <summary>
/// return true if *any* triangle of testMesh intersects with our tree.
/// Use TransformF to transform vertices of testMesh into space of this tree.
/// if boundsCheck is false, we skip bbox/bbox early-out
/// </summary>
public virtual bool TestIntersection(IMesh testMesh, Func<Vector3d, Vector3d> TransformF = null, bool bBoundsCheck = true)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.TestIntersection: mesh has been modified since tree construction");
if (bBoundsCheck) {
AxisAlignedBox3d meshBox = MeshMeasurements.Bounds(testMesh, TransformF);
if (box_box_intersect(root_index, ref meshBox) == false )
return false;
}
if (TransformF == null)
TransformF = (x) => { return x; };
Triangle3d test_tri = new Triangle3d();
foreach (int tid in testMesh.TriangleIndices()) {
Index3i tri = testMesh.GetTriangle(tid);
test_tri.V0 = TransformF(testMesh.GetVertex(tri.a));
test_tri.V1 = TransformF(testMesh.GetVertex(tri.b));
test_tri.V2 = TransformF(testMesh.GetVertex(tri.c));
if (TestIntersection(test_tri))
return true;
}
return false;
}
/// <summary>
/// Returns true if triangle intersects any triangle of our mesh
/// </summary>
public virtual bool TestIntersection(Triangle3d triangle)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.TestIntersection: mesh has been modified since tree construction");
AxisAlignedBox3d triBounds = BoundsUtil.Bounds(ref triangle);
int interTri = find_any_intersection(root_index, ref triangle, ref triBounds);
return (interTri >= 0);
}
protected int find_any_intersection(int iBox, ref Triangle3d triangle, ref AxisAlignedBox3d triBounds)
{
int idx = box_to_index[iBox];
if ( idx < triangles_end ) { // triange-list case, array is [N t1 t2 ... tN]
Triangle3d box_tri = new Triangle3d();
int num_tris = index_list[idx];
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref box_tri.V0, ref box_tri.V1, ref box_tri.V2);
if ( IntrTriangle3Triangle3.Intersects(ref triangle, ref box_tri))
return ti;
}
} else { // internal node, either 1 or 2 child boxes
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
if ( box_box_intersect(iChild1, ref triBounds) )
return find_any_intersection(iChild1, ref triangle, ref triBounds);
} else { // 2 children, descend closest first
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
int interTri = -1;
if ( box_box_intersect(iChild1, ref triBounds) )
interTri = find_any_intersection(iChild1, ref triangle, ref triBounds);
if ( interTri == -1 && box_box_intersect(iChild2, ref triBounds) )
interTri = find_any_intersection(iChild2, ref triangle, ref triBounds);
return interTri;
}
}
return -1;
}
/// <summary>
/// Returns true if there is *any* intersection between our mesh and 'other' mesh.
/// TransformF takes vertices of otherTree into our tree - can be null if in same coord space
/// </summary>
public virtual bool TestIntersection(DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF = null)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.TestIntersection: mesh has been modified since tree construction");
if (find_any_intersection(root_index, otherTree, TransformF, otherTree.root_index, 0))
return true;
return false;
}
protected bool find_any_intersection(int iBox, DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF, int oBox, int depth)
{
int idx = box_to_index[iBox];
int odx = otherTree.box_to_index[oBox];
if (idx < triangles_end && odx < otherTree.triangles_end) {
// ok we are at triangles for both trees, do triangle-level testing
Triangle3d tri = new Triangle3d(), otri = new Triangle3d();
int num_tris = index_list[idx], onum_tris = otherTree.index_list[odx];
// can re-use because Test() doesn't cache anything
//IntrTriangle3Triangle3 intr = new IntrTriangle3Triangle3(new Triangle3d(), new Triangle3d());
// outer iteration is "other" tris that need to be transformed (more expensive)
for (int j = 1; j <= onum_tris; ++j) {
int tj = otherTree.index_list[odx + j];
if (otherTree.TriangleFilterF != null && otherTree.TriangleFilterF(tj) == false)
continue;
otherTree.mesh.GetTriVertices(tj, ref otri.V0, ref otri.V1, ref otri.V2);
if (TransformF != null) {
otri.V0 = TransformF(otri.V0);
otri.V1 = TransformF(otri.V1);
otri.V2 = TransformF(otri.V2);
}
// inner iteration over "our" triangles
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2);
if (IntrTriangle3Triangle3.Intersects(ref otri, ref tri))
return true;
}
}
return false;
}
// we either descend "our" tree or the other tree
// - if we have hit triangles on "our" tree, we have to descend other
// - if we hit triangles on "other", we have to descend ours
// - otherwise, we alternate at each depth. This produces wider
// branching but is significantly faster (~10x) for both hits and misses
bool bDescendOther = (idx < triangles_end || depth % 2 == 0);
if (bDescendOther && odx < otherTree.triangles_end)
bDescendOther = false; // can't
if (bDescendOther) {
// ok we hit triangles on our side but we need to still reach triangles on
// the other side, so we descend "their" children
// [TODO] could we do efficient box.intersects(transform(box)) test?
// ( Contains() on each xformed point? )
AxisAlignedBox3d bounds = get_boxd(iBox);
int oChild1 = otherTree.index_list[odx];
if ( oChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
oChild1 = (-oChild1) - 1;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
if (oChild1Box.Intersects(bounds) )
return find_any_intersection(iBox, otherTree, TransformF, oBox, depth + 1);
} else { // 2 children
oChild1 = oChild1 - 1; // [TODO] could descend one w/ larger overlap volume first??
int oChild2 = otherTree.index_list[odx + 1] - 1;
bool intersects = false;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
if ( oChild1Box.Intersects(bounds) )
intersects = find_any_intersection(iBox, otherTree, TransformF, oChild1, depth + 1);
if (intersects == false) {
AxisAlignedBox3d oChild2Box = otherTree.get_boxd(oChild2, TransformF);
if ( oChild2Box.Intersects(bounds) )
intersects = find_any_intersection(iBox, otherTree, TransformF, oChild2, depth + 1);
}
return intersects;
}
} else {
// descend our tree nodes if they intersect w/ current bounds of other tree
AxisAlignedBox3d oBounds = otherTree.get_boxd(oBox, TransformF);
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
if ( box_box_intersect(iChild1, ref oBounds) )
return find_any_intersection(iChild1, otherTree, TransformF, oBox, depth + 1);
} else { // 2 children
iChild1 = iChild1 - 1; // [TODO] could descend one w/ larger overlap volume first??
int iChild2 = index_list[idx + 1] - 1;
bool intersects = false;
if ( box_box_intersect(iChild1, ref oBounds) )
intersects = find_any_intersection(iChild1, otherTree, TransformF, oBox, depth + 1);
if ( intersects == false && box_box_intersect(iChild2, ref oBounds) )
intersects = find_any_intersection(iChild2, otherTree, TransformF, oBox, depth + 1);
return intersects;
}
}
return false;
}
public struct PointIntersection
{
public int t0, t1;
public Vector3d point;
}
public struct SegmentIntersection
{
public int t0, t1;
public Vector3d point0, point1;
}
public class IntersectionsQueryResult
{
public List<PointIntersection> Points;
public List<SegmentIntersection> Segments;
}
/// <summary>
/// Compute all intersections between two Meshes.
/// TransformF argument transforms vertices of otherTree to our tree (can be null if in same coord space)
/// Returns pairs of intersecting triangles, which could intersect in either point or segment
/// Currently *does not* return coplanar intersections.
/// </summary>
public virtual IntersectionsQueryResult FindAllIntersections(DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF = null)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.FindIntersections: mesh has been modified since tree construction");
IntersectionsQueryResult result = new IntersectionsQueryResult();
result.Points = new List<PointIntersection>();
result.Segments = new List<SegmentIntersection>();
IntrTriangle3Triangle3 intr = new IntrTriangle3Triangle3(new Triangle3d(), new Triangle3d());
find_intersections(root_index, otherTree, TransformF, otherTree.root_index, 0, intr, result);
return result;
}
protected void find_intersections(int iBox, DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF,
int oBox, int depth,
IntrTriangle3Triangle3 intr, IntersectionsQueryResult result)
{
int idx = box_to_index[iBox];
int odx = otherTree.box_to_index[oBox];
if (idx < triangles_end && odx < otherTree.triangles_end) {
// ok we are at triangles for both trees, do triangle-level testing
Triangle3d tri = new Triangle3d(), otri = new Triangle3d();
int num_tris = index_list[idx], onum_tris = otherTree.index_list[odx];
// outer iteration is "other" tris that need to be transformed (more expensive)
for (int j = 1; j <= onum_tris; ++j) {
int tj = otherTree.index_list[odx + j];
if (otherTree.TriangleFilterF != null && otherTree.TriangleFilterF(tj) == false)
continue;
otherTree.mesh.GetTriVertices(tj, ref otri.V0, ref otri.V1, ref otri.V2);
if (TransformF != null) {
otri.V0 = TransformF(otri.V0);
otri.V1 = TransformF(otri.V1);
otri.V2 = TransformF(otri.V2);
}
intr.Triangle0 = otri;
// inner iteration over "our" triangles
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2);
intr.Triangle1 = tri;
// [RMS] Test() is much faster than Find() so it makes sense to call it first, as most
// triangles will not intersect (right?)
if (intr.Test()) {
if ( intr.Find() ) {
if (intr.Quantity == 1) {
result.Points.Add(new PointIntersection()
{ t0 = ti, t1 = tj, point = intr.Points[0] });
} else if (intr.Quantity == 2) {
result.Segments.Add( new SegmentIntersection()
{ t0 = ti, t1 = tj, point0 = intr.Points[0], point1 = intr.Points[1] });
} else {
throw new Exception("DMeshAABBTree.find_intersections: found quantity " + intr.Quantity );
}
}
}
}
}
// done these nodes
return;
}
// we either descend "our" tree or the other tree
// - if we have hit triangles on "our" tree, we have to descend other
// - if we hit triangles on "other", we have to descend ours
// - otherwise, we alternate at each depth. This produces wider
// branching but is significantly faster (~10x) for both hits and misses
bool bDescendOther = (idx < triangles_end || depth % 2 == 0);
if (bDescendOther && odx < otherTree.triangles_end)
bDescendOther = false; // can't
if (bDescendOther) {
// ok we hit triangles on our side but we need to still reach triangles on
// the other side, so we descend "their" children
// [TODO] could we do efficient box.intersects(transform(box)) test?
// ( Contains() on each xformed point? )
AxisAlignedBox3d bounds = get_boxd(iBox);
int oChild1 = otherTree.index_list[odx];
if ( oChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
oChild1 = (-oChild1) - 1;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
if (oChild1Box.Intersects(bounds) )
find_intersections(iBox, otherTree, TransformF, oChild1, depth + 1, intr, result);
} else { // 2 children
oChild1 = oChild1 - 1;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
if ( oChild1Box.Intersects(bounds) )
find_intersections(iBox, otherTree, TransformF, oChild1, depth + 1, intr, result);
int oChild2 = otherTree.index_list[odx + 1] - 1;
AxisAlignedBox3d oChild2Box = otherTree.get_boxd(oChild2, TransformF);
if ( oChild2Box.Intersects(bounds) )
find_intersections(iBox, otherTree, TransformF, oChild2, depth + 1, intr, result);
}
} else {
// descend our tree nodes if they intersect w/ current bounds of other tree
AxisAlignedBox3d oBounds = otherTree.get_boxd(oBox, TransformF);
int iChild1 = index_list[idx];
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
if ( box_box_intersect(iChild1, ref oBounds) )
find_intersections(iChild1, otherTree, TransformF, oBox, depth + 1, intr, result);
} else { // 2 children
iChild1 = iChild1 - 1;
if ( box_box_intersect(iChild1, ref oBounds) )
find_intersections(iChild1, otherTree, TransformF, oBox, depth + 1, intr, result);
int iChild2 = index_list[idx + 1] - 1;
if ( box_box_intersect(iChild2, ref oBounds) )
find_intersections(iChild2, otherTree, TransformF, oBox, depth + 1, intr, result);
}
}
}
/// <summary>
/// Find nearest pair of triangles on this tree with otherTree, within max_dist.
/// TransformF transforms vertices of otherTree into our coordinates. can be null.
/// returns triangle-id pair (my_tri,other_tri), or Index2i.Max if not found within max_dist
/// Use MeshQueries.TrianglesDistance() to get more information
/// </summary>
public virtual Index2i FindNearestTriangles(DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF, out double distance, double max_dist = double.MaxValue)
{
if (mesh_timestamp != mesh.ShapeTimestamp)
throw new Exception("DMeshAABBTree3.TestIntersection: mesh has been modified since tree construction");
double nearest_sqr = double.MaxValue;
if (max_dist < double.MaxValue)
nearest_sqr = max_dist * max_dist;
Index2i nearest_pair = Index2i.Max;
find_nearest_triangles(root_index, otherTree, TransformF, otherTree.root_index, 0, ref nearest_sqr, ref nearest_pair);
distance = (nearest_sqr < double.MaxValue) ? Math.Sqrt(nearest_sqr) : double.MaxValue;
return nearest_pair;
}
protected void find_nearest_triangles(int iBox, DMeshAABBTree3 otherTree, Func<Vector3d, Vector3d> TransformF, int oBox, int depth, ref double nearest_sqr, ref Index2i nearest_pair)
{
int idx = box_to_index[iBox];
int odx = otherTree.box_to_index[oBox];
if (idx < triangles_end && odx < otherTree.triangles_end) {
// ok we are at triangles for both trees, do triangle-level testing
Triangle3d tri = new Triangle3d(), otri = new Triangle3d();
int num_tris = index_list[idx], onum_tris = otherTree.index_list[odx];
DistTriangle3Triangle3 dist = new DistTriangle3Triangle3(new Triangle3d(), new Triangle3d());
// outer iteration is "other" tris that need to be transformed (more expensive)
for (int j = 1; j <= onum_tris; ++j) {
int tj = otherTree.index_list[odx + j];
if (otherTree.TriangleFilterF != null && otherTree.TriangleFilterF(tj) == false)
continue;
otherTree.mesh.GetTriVertices(tj, ref otri.V0, ref otri.V1, ref otri.V2);
if (TransformF != null) {
otri.V0 = TransformF(otri.V0);
otri.V1 = TransformF(otri.V1);
otri.V2 = TransformF(otri.V2);
}
dist.Triangle0 = otri;
// inner iteration over "our" triangles
for (int i = 1; i <= num_tris; ++i) {
int ti = index_list[idx + i];
if (TriangleFilterF != null && TriangleFilterF(ti) == false)
continue;
mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2);
dist.Triangle1 = tri;
double dist_sqr = dist.GetSquared();
if ( dist_sqr < nearest_sqr ) {
nearest_sqr = dist_sqr;
nearest_pair = new Index2i(ti, tj);
}
}
}
return;
}
// we either descend "our" tree or the other tree
// - if we have hit triangles on "our" tree, we have to descend other
// - if we hit triangles on "other", we have to descend ours
// - otherwise, we alternate at each depth. This produces wider
// branching but is significantly faster (~10x) for both hits and misses
bool bDescendOther = (idx < triangles_end || depth % 2 == 0);
if (bDescendOther && odx < otherTree.triangles_end)
bDescendOther = false; // can't
if (bDescendOther) {
// ok we reached triangles on our side but we need to still reach triangles on
// the other side, so we descend "their" children
AxisAlignedBox3d bounds = get_boxd(iBox);
int oChild1 = otherTree.index_list[odx];
if (oChild1 < 0) { // 1 child, descend if nearer than cur min-dist
oChild1 = (-oChild1) - 1;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
if (oChild1Box.DistanceSquared(ref bounds) < nearest_sqr)
find_nearest_triangles(iBox, otherTree, TransformF, oChild1, depth + 1, ref nearest_sqr, ref nearest_pair);
} else { // 2 children
oChild1 = oChild1 - 1;
int oChild2 = otherTree.index_list[odx + 1] - 1;
AxisAlignedBox3d oChild1Box = otherTree.get_boxd(oChild1, TransformF);
AxisAlignedBox3d oChild2Box = otherTree.get_boxd(oChild2, TransformF);
// descend closer box first
double d1Sqr = oChild1Box.DistanceSquared(ref bounds);
double d2Sqr = oChild2Box.DistanceSquared(ref bounds);
if (d2Sqr < d1Sqr) {
if (d2Sqr < nearest_sqr)
find_nearest_triangles(iBox, otherTree, TransformF, oChild2, depth + 1, ref nearest_sqr, ref nearest_pair);
if (d1Sqr < nearest_sqr)
find_nearest_triangles(iBox, otherTree, TransformF, oChild1, depth + 1, ref nearest_sqr, ref nearest_pair);
} else {
if (d1Sqr < nearest_sqr)
find_nearest_triangles(iBox, otherTree, TransformF, oChild1, depth + 1, ref nearest_sqr, ref nearest_pair);
if (d2Sqr < nearest_sqr)
find_nearest_triangles(iBox, otherTree, TransformF, oChild2, depth + 1, ref nearest_sqr, ref nearest_pair);
}
}
} else {
// descend our tree nodes if they intersect w/ current bounds of other tree
AxisAlignedBox3d oBounds = otherTree.get_boxd(oBox, TransformF);
int iChild1 = index_list[idx];
if (iChild1 < 0) { // 1 child, descend if nearer than cur min-dist
iChild1 = (-iChild1) - 1;
if (box_box_distsqr(iChild1, ref oBounds) < nearest_sqr)
find_nearest_triangles(iChild1, otherTree, TransformF, oBox, depth + 1, ref nearest_sqr, ref nearest_pair);
} else { // 2 children
iChild1 = iChild1 - 1;
int iChild2 = index_list[idx + 1] - 1;
// descend closer box first
double d1Sqr = box_box_distsqr(iChild1, ref oBounds);
double d2Sqr = box_box_distsqr(iChild2, ref oBounds);
if ( d2Sqr < d1Sqr ) {
if ( d2Sqr < nearest_sqr )
find_nearest_triangles(iChild2, otherTree, TransformF, oBox, depth + 1, ref nearest_sqr, ref nearest_pair);
if ( d1Sqr < nearest_sqr )
find_nearest_triangles(iChild1, otherTree, TransformF, oBox, depth + 1, ref nearest_sqr, ref nearest_pair);
} else {
if (d1Sqr < nearest_sqr)
find_nearest_triangles(iChild1, otherTree, TransformF, oBox, depth + 1, ref nearest_sqr, ref nearest_pair);
if (d2Sqr < nearest_sqr)
find_nearest_triangles(iChild2, otherTree, TransformF, oBox, depth + 1, ref nearest_sqr, ref nearest_pair);
}
}
}
}
/// <summary>
/// Does this ISpatial support IsInside() test (yes!)
/// </summary>
public bool SupportsPointContainment { get { return true; } }
/// <summary>
/// Returns true if point p is inside this mesh.
/// </summary>
public virtual bool IsInside(Vector3d p)
{
// This is a raycast crossing-count test, which is not ideal!
// Only works for closed meshes.
//AxisAlignedBox3f bounds = get_box(root_index);
//Vector3d outside = bounds.Center + 2 * bounds.Diagonal;
//Vector3d rayDir = Vector3d.AxisX;
// [RMS] this is just a random direction I picked...
Vector3d rayDir = new Vector3d(0.331960519038825,0.462531727525156,0.822111072077288);
//Vector3d rayOrigin = p - 2 * bounds.Width * rayDir;
Vector3d rayOrigin = p;
Ray3d ray = new Ray3d(rayOrigin, rayDir);
int nHits = FindAllHitTriangles(ray, null);
return (nHits % 2) != 0;
}
/// <summary>
/// Instances of this class can be passed in to the DoTraversal() function to implement your
/// own tree-traversal queries.
/// NextBoxF() is called for each box node. Return false from this function to halt terminate
/// that branch of the traversal, or true to descend into that box's children (boxes or triangles).
/// NextTriangleF() is called for each triangle.
/// </summary>
public class TreeTraversal
{
// return false to terminate this branch
// arguments are box and depth in tree
public Func<AxisAlignedBox3f, int, bool> NextBoxF = (box,depth) => { return true; };
public Action<int> NextTriangleF = (tID) => { };
}
/// <summary>
/// Hierarchically descend through the tree nodes, calling the TreeTrversal functions at each level