Skip to content

Commit 36d4e76

Browse files
committed
added AABBTree.TreeTraversal, FindNearestTriangle. Works in randomized testing w/ one mesh. Fixed a bug in tree construction. improved testing coverage, now verifies that internal boxes contain triangles.
1 parent 76064bc commit 36d4e76

File tree

1 file changed

+188
-13
lines changed

1 file changed

+188
-13
lines changed

spatial/DMeshAABBTree.cs

Lines changed: 188 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,133 @@ public DMeshAABBTree3(DMesh3 m)
1414
}
1515

1616

17+
18+
19+
public void Build()
20+
{
21+
build_by_one_rings();
22+
}
23+
24+
25+
26+
27+
public int FindNearestTriangle(Vector3d p)
28+
{
29+
double fNearestSqr = double.MaxValue;
30+
int tNearID = -1;
31+
find_nearest_tri(root_index, p, ref fNearestSqr, ref tNearID);
32+
return tNearID;
33+
}
34+
void find_nearest_tri(int iBox, Vector3d p, ref double fNearestSqr, ref int tID)
35+
{
36+
int idx = box_to_index[iBox];
37+
if ( idx < triangles_end ) { // triange-list case, array is [N t1 t2 ... tN]
38+
int num_tris = index_list[idx];
39+
for (int i = 1; i <= num_tris; ++i) {
40+
int ti = index_list[idx + i];
41+
double fTriDistSqr = MeshQueries.TriDistanceSqr(mesh, ti, p);
42+
if ( fTriDistSqr < fNearestSqr ) {
43+
fNearestSqr = fTriDistSqr;
44+
tID = ti;
45+
}
46+
}
47+
48+
} else { // internal node, either 1 or 2 child boxes
49+
int iChild1 = index_list[idx];
50+
if ( iChild1 < 0 ) { // 1 child, descend if nearer than cur min-dist
51+
iChild1 = (-iChild1) - 1;
52+
double fChild1DistSqr = box_distance_sqr(iChild1, p);
53+
if ( fChild1DistSqr <= fNearestSqr )
54+
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
55+
56+
} else { // 2 children, descend closest first
57+
iChild1 = iChild1 - 1;
58+
int iChild2 = index_list[idx + 1] - 1;
59+
60+
double fChild1DistSqr = box_distance_sqr(iChild1, p);
61+
double fChild2DistSqr = box_distance_sqr(iChild2, p);
62+
if (fChild1DistSqr < fChild2DistSqr) {
63+
if (fChild1DistSqr < fNearestSqr) {
64+
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
65+
if (fChild2DistSqr < fNearestSqr)
66+
find_nearest_tri(iChild2, p, ref fNearestSqr, ref tID);
67+
}
68+
} else {
69+
if (fChild2DistSqr < fNearestSqr) {
70+
find_nearest_tri(iChild2, p, ref fNearestSqr, ref tID);
71+
if (fChild1DistSqr < fNearestSqr)
72+
find_nearest_tri(iChild1, p, ref fNearestSqr, ref tID);
73+
}
74+
}
75+
76+
}
77+
}
78+
}
79+
80+
81+
82+
83+
84+
85+
// DoTraversal function will walk through tree and call NextBoxF for each
86+
// internal box node, and NextTriangleF for each triangle.
87+
// You can prune branches by returning false from NextBoxF
88+
public class TreeTraversal
89+
{
90+
// return false to terminate this branch
91+
public Func<AxisAlignedBox3f, bool> NextBoxF = (x) => { return true; };
92+
93+
public Action<int> NextTriangleF = (tID) => { };
94+
}
95+
96+
97+
// walk over tree, calling functions in TreeTraversal object for internal nodes and triangles
98+
public void DoTraversal(TreeTraversal traversal)
99+
{
100+
tree_traversal(root_index, traversal);
101+
}
102+
103+
// traversal implementation
104+
private void tree_traversal(int iBox, TreeTraversal traversal)
105+
{
106+
int idx = box_to_index[iBox];
107+
108+
if ( idx < triangles_end ) {
109+
// triange-list case, array is [N t1 t2 ... tN]
110+
int n = index_list[idx];
111+
for ( int i = 1; i <= n; ++i ) {
112+
int ti = index_list[idx + i];
113+
traversal.NextTriangleF(ti);
114+
}
115+
} else {
116+
int i0 = index_list[idx];
117+
if ( i0 < 0 ) {
118+
// negative index means we only have one 'child' box to descend into
119+
i0 = (-i0) - 1;
120+
if ( traversal.NextBoxF(get_box(i0)) )
121+
tree_traversal(i0, traversal);
122+
} else {
123+
// positive index, two sequential child box indices to descend into
124+
i0 = i0 - 1;
125+
if ( traversal.NextBoxF(get_box(i0)) )
126+
tree_traversal(i0, traversal);
127+
int i1 = index_list[idx + 1] - 1;
128+
if ( traversal.NextBoxF(get_box(i1)) )
129+
tree_traversal(i0, traversal);
130+
}
131+
}
132+
}
133+
134+
135+
136+
137+
//
138+
// Internals - data structures, construction, etc
139+
//
140+
141+
142+
143+
17144
// storage for box nodes.
18145
// - box_to_index is a pointer into index_list
19146
// - box_centers and box_extents are the centers/extents of the bounding boxes
@@ -50,7 +177,7 @@ public DMeshAABBTree3(DMesh3 m)
50177
// 1b) second pass where we handle any missed tris
51178
// 2) sequentially combine N leaf boxes into (N/2 + N%2) layer 2 boxes
52179
// 3) repeat until layer K has only 1 box, which is root of tree
53-
public void BuildByOneRings()
180+
void build_by_one_rings()
54181
{
55182
box_to_index = new DVector<int>();
56183
box_centers = new DVector<Vector3f>();
@@ -120,7 +247,7 @@ public void BuildByOneRings()
120247
// Appends a box that contains free triangles in one-ring of vertex vid.
121248
// If tri count is < spill threshold, push onto spill list instead.
122249
// Returns # of free tris found.
123-
public int add_one_ring_box(int vid, byte[] used_triangles, int[] temp_tris,
250+
int add_one_ring_box(int vid, byte[] used_triangles, int[] temp_tris,
124251
ref int iBoxCur, ref int iIndicesCur,
125252
DVector<int> spill, int nSpillThresh )
126253
{
@@ -167,7 +294,7 @@ public int add_one_ring_box(int vid, byte[] used_triangles, int[] temp_tris,
167294
// Except, of course, if N is odd, then we get N/2+1, where the +1
168295
// box has a single child box (ie just a copy).
169296
// [TODO] instead merge that extra box into on of parents? Reduces tree depth by 1
170-
public int cluster_boxes(int iStart, int iCount, ref int iBoxCur, ref int iIndicesCur)
297+
int cluster_boxes(int iStart, int iCount, ref int iBoxCur, ref int iIndicesCur)
171298
{
172299
int[] indices = new int[iCount];
173300
for (int i = 0; i < iCount; ++i)
@@ -200,7 +327,7 @@ public int cluster_boxes(int iStart, int iCount, ref int iBoxCur, ref int iIndic
200327
index_list.insert(i1+1, iIndicesCur++);
201328

202329
box_centers.insert(center, iBox);
203-
box_extents.insert(center, iBox);
330+
box_extents.insert(extent, iBox);
204331
}
205332

206333
// [todo] could we merge with last other box? need a way to tell
@@ -231,7 +358,7 @@ public int cluster_boxes(int iStart, int iCount, ref int iBoxCur, ref int iIndic
231358

232359

233360
// construct box that contains two boxes
234-
public void get_combined_box(int b0, int b1, out Vector3f center, out Vector3f extent)
361+
void get_combined_box(int b0, int b1, out Vector3f center, out Vector3f extent)
235362
{
236363
Vector3f c0 = box_centers[b0];
237364
Vector3f e0 = box_extents[b0];
@@ -250,7 +377,7 @@ public void get_combined_box(int b0, int b1, out Vector3f center, out Vector3f e
250377
}
251378

252379

253-
public AxisAlignedBox3f get_box(int iBox)
380+
AxisAlignedBox3f get_box(int iBox)
254381
{
255382
Vector3f c = box_centers[iBox];
256383
Vector3f e = box_extents[iBox];
@@ -261,10 +388,21 @@ public AxisAlignedBox3f get_box(int iBox)
261388

262389

263390

391+
double box_distance_sqr(int iBox, Vector3d p)
392+
{
393+
Vector3d c = box_centers[iBox];
394+
Vector3d e = box_extents[iBox];
395+
AxisAlignedBox3d box = new AxisAlignedBox3d(c - e, c + e);
396+
return box.DistanceSquared(p);
397+
}
398+
399+
400+
264401

265402

266403

267-
// make sure we can reach every tri in mesh through tree (also demo of how to traverse tree...)
404+
// 1) make sure we can reach every tri in mesh through tree (also demo of how to traverse tree...)
405+
// 2) make sure that triangles are contained in parent boxes
268406
public void TestCoverage()
269407
{
270408
int[] tri_counts = new int[mesh.MaxTriangleID];
@@ -278,14 +416,19 @@ public void TestCoverage()
278416
if (tri_counts[ti] != 1)
279417
Util.gBreakToDebugger();
280418
}
281-
private void test_coverage(int[] tri_counts, int[] parent_indices, int iCur)
419+
420+
// accumulate triangle counts and track each box-parent index.
421+
// also checks that triangles are contained in boxes
422+
private void test_coverage(int[] tri_counts, int[] parent_indices, int iBox)
282423
{
283-
int idx = box_to_index[iCur];
424+
int idx = box_to_index[iBox];
425+
426+
debug_check_child_tris_in_box(iBox);
284427

285428
if ( idx < triangles_end ) {
286429
// triange-list case, array is [N t1 t2 ... tN]
287430
int n = index_list[idx];
288-
AxisAlignedBox3f box = get_box(iCur);
431+
AxisAlignedBox3f box = get_box(iBox);
289432
for ( int i = 1; i <= n; ++i ) {
290433
int ti = index_list[idx + i];
291434
tri_counts[ti]++;
@@ -303,21 +446,53 @@ private void test_coverage(int[] tri_counts, int[] parent_indices, int iCur)
303446
if ( i0 < 0 ) {
304447
// negative index means we only have one 'child' box to descend into
305448
i0 = (-i0) - 1;
306-
parent_indices[i0] = iCur;
449+
parent_indices[i0] = iBox;
307450
test_coverage(tri_counts, parent_indices, i0);
308451
} else {
309452
// positive index, two sequential child box indices to descend into
310453
i0 = i0 - 1;
311-
parent_indices[i0] = iCur;
454+
parent_indices[i0] = iBox;
312455
test_coverage(tri_counts, parent_indices, i0);
313456
int i1 = index_list[idx + 1];
314457
i1 = i1 - 1;
315-
parent_indices[i1] = iCur;
458+
parent_indices[i1] = iBox;
316459
test_coverage(tri_counts, parent_indices, i1);
317460
}
318461
}
319462
}
463+
// do full tree traversal below iBox and make sure that all triangles are further
464+
// than box-distance-sqr
465+
void debug_check_child_tri_distances(int iBox, Vector3d p)
466+
{
467+
double fBoxDistSqr = box_distance_sqr(iBox, p);
468+
469+
TreeTraversal t = new TreeTraversal() {
470+
NextTriangleF = (tID) => {
471+
double fTriDistSqr = MeshQueries.TriDistanceSqr(mesh, tID, p);
472+
if (fTriDistSqr < fBoxDistSqr)
473+
if ( Math.Abs(fTriDistSqr - fBoxDistSqr) > MathUtil.ZeroTolerance*100 )
474+
Util.gBreakToDebugger();
475+
}
476+
};
477+
tree_traversal(iBox, t);
478+
}
320479

480+
// do full tree traversal below iBox to make sure that all child triangles are contained
481+
void debug_check_child_tris_in_box(int iBox)
482+
{
483+
AxisAlignedBox3f box = get_box(iBox);
484+
TreeTraversal t = new TreeTraversal() {
485+
NextTriangleF = (tID) => {
486+
Index3i tv = mesh.GetTriangle(tID);
487+
for (int j = 0; j < 3; ++j) {
488+
Vector3f v = (Vector3f)mesh.GetVertex(tv[j]);
489+
if (box.Contains(v) == false)
490+
Util.gBreakToDebugger();
491+
}
492+
}
493+
};
494+
tree_traversal(iBox, t);
495+
}
321496

322497

323498

0 commit comments

Comments
 (0)