// Turn a span of N boxes into N/2 boxes, by pairing boxes // Except, of course, if N is odd, then we get N/2+1, where the +1 // box has a single child box (ie just a copy). // [TODO] instead merge that extra box into on of parents? Reduces tree depth by 1 int cluster_boxes(int iStart, int iCount, ref int iBoxCur, ref int iIndicesCur) { int[] indices = new int[iCount]; for (int i = 0; i < iCount; ++i) { indices[i] = iStart + i; } int nDim = 0; Array.Sort(indices, (a, b) => { float axis_min_a = box_centers[a][nDim] - box_extents[a][nDim]; float axis_min_b = box_centers[b][nDim] - box_extents[b][nDim]; return((axis_min_a == axis_min_b) ? 0 : (axis_min_a < axis_min_b) ? -1 : 1); }); int nPairs = iCount / 2; int nLeft = iCount - 2 * nPairs; // this is dumb! but lets us test the rest... for (int pi = 0; pi < nPairs; pi++) { int i0 = indices[2 * pi]; int i1 = indices[2 * pi + 1]; Vector3f center, extent; get_combined_box(i0, i1, out center, out extent); // append new box int iBox = iBoxCur++; box_to_index.insert(iIndicesCur, iBox); index_list.insert(i0 + 1, iIndicesCur++); index_list.insert(i1 + 1, iIndicesCur++); box_centers.insert(center, iBox); box_extents.insert(extent, iBox); } // [todo] could we merge with last other box? need a way to tell // that there are 3 children though...could use negative index for that? if (nLeft > 0) { if (nLeft > 1) { Util.gBreakToDebugger(); } int iLeft = indices[2 * nPairs]; duplicate_box(iLeft, ref iBoxCur, ref iIndicesCur); } return(nPairs + nLeft); }
// accumulate triangle counts and track each box-parent index. // also checks that triangles are contained in boxes private void test_coverage(int[] tri_counts, int[] parent_indices, int iBox) { int idx = box_to_index[iBox]; debug_check_child_tris_in_box(iBox); if (idx < triangles_end) { // triange-list case, array is [N t1 t2 ... tN] int n = index_list[idx]; AxisAlignedBox3f box = get_box(iBox); for (int i = 1; i <= n; ++i) { int ti = index_list[idx + i]; tri_counts[ti]++; Index3i tv = mesh.GetTriangle(ti); for (int j = 0; j < 3; ++j) { Vector3f v = (Vector3f)mesh.GetVertex(tv[j]); if (!box.Contains(v)) { Util.gBreakToDebugger(); } } } } else { int i0 = index_list[idx]; if (i0 < 0) { // negative index means we only have one 'child' box to descend into i0 = (-i0) - 1; parent_indices[i0] = iBox; test_coverage(tri_counts, parent_indices, i0); } else { // positive index, two sequential child box indices to descend into i0 = i0 - 1; parent_indices[i0] = iBox; test_coverage(tri_counts, parent_indices, i0); int i1 = index_list[idx + 1]; i1 = i1 - 1; parent_indices[i1] = iBox; test_coverage(tri_counts, parent_indices, i1); } } }
// do full tree traversal below iBox to make sure that all child points are contained void debug_check_child_points_in_box(int iBox) { AxisAlignedBox3d box = get_box(iBox); TreeTraversal t = new TreeTraversal() { NextPointF = (vID) => { Vector3d v = points.GetVertex(vID); if (box.Contains(v) == false) { Util.gBreakToDebugger(); } } }; tree_traversal(iBox, 0, t); }
// 1) make sure we can reach every tri in mesh through tree (also demo of how to traverse tree...) // 2) make sure that triangles are contained in parent boxes public void TestCoverage() { int[] tri_counts = new int[mesh.MaxTriangleID]; Array.Clear(tri_counts, 0, tri_counts.Length); int[] parent_indices = new int[box_to_index.Length]; Array.Clear(parent_indices, 0, parent_indices.Length); test_coverage(tri_counts, parent_indices, root_index); foreach (int ti in mesh.TriangleIndices()) { if (tri_counts[ti] != 1) { Util.gBreakToDebugger(); } } }
// 1) make sure we can reach every point through tree (also demo of how to traverse tree...) // 2) make sure that points are contained in parent boxes public void TestCoverage() { int[] point_counts = new int[points.MaxVertexID]; Array.Clear(point_counts, 0, point_counts.Length); int[] parent_indices = new int[box_to_index.Length]; Array.Clear(parent_indices, 0, parent_indices.Length); test_coverage(point_counts, parent_indices, root_index); foreach (int ti in points.VertexIndices()) { if (point_counts[ti] != 1) { Util.gBreakToDebugger(); } } }
// accumulate point counts and track each box-parent index. // also checks that points are contained in boxes private void test_coverage(int[] point_counts, int[] parent_indices, int iBox) { int idx = box_to_index[iBox]; debug_check_child_points_in_box(iBox); if (idx < points_end) { // point-list case, array is [N t1 t2 ... tN] int n = index_list[idx]; AxisAlignedBox3d box = get_box(iBox); for (int i = 1; i <= n; ++i) { int vi = index_list[idx + i]; point_counts[vi]++; Vector3d v = points.GetVertex(vi); if (!box.Contains(v)) { Util.gBreakToDebugger(); } } } else { int i0 = index_list[idx]; if (i0 < 0) { // negative index means we only have one 'child' box to descend into i0 = (-i0) - 1; parent_indices[i0] = iBox; test_coverage(point_counts, parent_indices, i0); } else { // positive index, two sequential child box indices to descend into i0 = i0 - 1; parent_indices[i0] = iBox; test_coverage(point_counts, parent_indices, i0); int i1 = index_list[idx + 1]; i1 = i1 - 1; parent_indices[i1] = iBox; test_coverage(point_counts, parent_indices, i1); } } }
// do full tree traversal below iBox to make sure that all child triangles are contained void debug_check_child_tris_in_box(int iBox) { AxisAlignedBox3f box = get_box(iBox); TreeTraversal t = new TreeTraversal() { NextTriangleF = (tID) => { Index3i tv = mesh.GetTriangle(tID); for (int j = 0; j < 3; ++j) { Vector3f v = (Vector3f)mesh.GetVertex(tv[j]); if (box.Contains(v) == false) { Util.gBreakToDebugger(); } } } }; tree_traversal(iBox, 0, t); }
// do full tree traversal below iBox and make sure that all triangles are further // than box-distance-sqr void debug_check_child_tri_distances(int iBox, Vector3d p) { double fBoxDistSqr = box_distance_sqr(iBox, p); TreeTraversal t = new TreeTraversal() { NextTriangleF = (tID) => { double fTriDistSqr = MeshQueries.TriDistanceSqr(mesh, tID, p); if (fTriDistSqr < fBoxDistSqr) { if (Math.Abs(fTriDistSqr - fBoxDistSqr) > MathUtil.ZeroTolerance * 100) { Util.gBreakToDebugger(); } } } }; tree_traversal(iBox, 0, t); }
protected virtual void DebugCheckVertexConstraints() { if (constraints == null) { return; } foreach (KeyValuePair <int, VertexConstraint> vc in constraints.VertexConstraintsItr()) { int vid = vc.Key; if (vc.Value.Target != null) { Vector3d curpos = mesh.GetVertex(vid); Vector3d projected = vc.Value.Target.Project(curpos, vid); if (curpos.DistanceSquared(projected) > 0.0001f) { Util.gBreakToDebugger(); } } } }
// do full tree traversal below iBox and make sure that all points are further // than box-distance-sqr void debug_check_child_point_distances(int iBox, Vector3d p) { double fBoxDistSqr = box_distance_sqr(iBox, ref p); TreeTraversal t = new TreeTraversal() { NextPointF = (vID) => { Vector3d v = points.GetVertex(vID); double fDistSqr = p.DistanceSquared(v); if (fDistSqr < fBoxDistSqr) { if (Math.Abs(fDistSqr - fBoxDistSqr) > MathUtil.ZeroTolerance * 100) { Util.gBreakToDebugger(); } } } }; tree_traversal(iBox, 0, t); }
// Turn a span of N boxes into N/2 boxes, by pairing boxes // Except, of course, if N is odd, then we get N/2+1, where the +1 // box has a single child box (ie just a copy). // [TODO] instead merge that extra box into on of parents? Reduces tree depth by 1 int cluster_boxes_nearsearch(int iStart, int iCount, ref int iBoxCur, ref int iIndicesCur) { int[] indices = new int[iCount]; for (int i = 0; i < iCount; ++i) { indices[i] = iStart + i; } Func <int, int, double> boxMetric = combined_box_volume; //Func<int, int, double> boxMetric = combined_box_length; // sort indices by x axis // cycling axes (ie at each depth) seems to produce much worse results... int nDim = 0; Array.Sort(indices, (a, b) => { float axis_min_a = box_centers[a][nDim] - box_extents[a][nDim]; float axis_min_b = box_centers[b][nDim] - box_extents[b][nDim]; return((axis_min_a == axis_min_b) ? 0 : (axis_min_a < axis_min_b) ? -1 : 1); }); int nPairs = iCount / 2; int nLeft = iCount - 2 * nPairs; // bounded greedy clustering. // Search ahead next N boxes in sorted-by-axis list, and find // the one that creates minimal box metric when combined with us. int N = BottomUpClusterLookahead; int[] nextNi = new int[N]; double[] nextNc = new double[N]; int pj; for (int pi = 0; pi < iCount - 1; pi++) { int i0 = indices[pi]; if (i0 < 0) { continue; } int nStop = Math.Min(N, iCount - pi - 1); for (int k = 0; k < nStop; ++k) { pj = pi + k + 1; nextNi[k] = pj; int ik = indices[pj]; if (ik < 0) { nextNc[k] = double.MaxValue; } else { nextNc[k] = boxMetric(i0, ik); } } Array.Sort(nextNc, nextNi, 0, nStop); if (nextNc[0] == double.MaxValue) { continue; } pj = nextNi[0]; int i1 = indices[pj]; if (i1 < 0) { Util.gBreakToDebugger(); } Vector3f center, extent; get_combined_box(i0, i1, out center, out extent); // append new box int iBox = iBoxCur++; box_to_index.insert(iIndicesCur, iBox); index_list.insert(i0 + 1, iIndicesCur++); index_list.insert(i1 + 1, iIndicesCur++); box_centers.insert(center, iBox); box_extents.insert(extent, iBox); indices[pi] = -(indices[pi] + 1); indices[pj] = -(indices[pj] + 1); } // [todo] could we merge with last other box? need a way to tell // that there are 3 children though...could use negative index for that? if (nLeft > 0) { int iLeft = -1; for (int i = 0; iLeft < 0 && i < indices.Length; ++i) { if (indices[i] >= 0) { iLeft = indices[i]; } } duplicate_box(iLeft, ref iBoxCur, ref iIndicesCur); } return(nPairs + nLeft); }