} // end make_level_set_3 void make_level_set3_parallel(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f distances, int exact_band) { distances.resize(ni, nj, nk); distances.assign((float)((ni + nj + nk) * dx)); // upper bound on distance // closest triangle id for each grid cell DenseGrid3i closest_tri = new DenseGrid3i(ni, nj, nk, -1); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} DenseGrid3i intersection_count = new DenseGrid3i(ni, nj, nk, 0); if (DebugPrint) { System.Console.WriteLine("start"); } double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; double invdx = 1.0 / dx; // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. // To compute in parallel, we need to safely update grid cells. Current strategy is // to use a spinlock to control access to grid. Partitioning the grid into a few regions, // each w/ a separate spinlock, improves performance somewhat. Have also tried having a // separate spinlock per-row, this resulted in a few-percent performance improvement. // Also tried pre-sorting triangles into disjoint regions, this did not help much except // on "perfect" cases like a sphere. int wi = ni / 2, wj = nj / 2, wk = nk / 2; SpinLock[] grid_locks = new SpinLock[8]; bool abort = false; gParallel.ForEach(Mesh.TriangleIndices(), (tid) => { if (tid % 100 == 0) { abort = CancelF(); } if (abort) { return; } Vector3d xp = Vector3d.Zero, xq = Vector3d.Zero, xr = Vector3d.Zero; Mesh.GetTriVertices(tid, ref xp, ref xq, ref xr); // real ijk coordinates of xp/xq/xr double fip = (xp[0] - ox) * invdx, fjp = (xp[1] - oy) * invdx, fkp = (xp[2] - oz) * invdx; double fiq = (xq[0] - ox) * invdx, fjq = (xq[1] - oy) * invdx, fkq = (xq[2] - oz) * invdx; double fir = (xr[0] - ox) * invdx, fjr = (xr[1] - oy) * invdx, fkr = (xr[2] - oz) * invdx; // clamped integer bounding box of triangle plus exact-band int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { int base_idx = ((j < wj) ? 0 : 1) | ((k < wk) ? 0 : 2); // construct index into spinlocks array for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)point_triangle_distance(ref gx, ref xp, ref xq, ref xr); if (d < distances[i, j, k]) { int lock_idx = base_idx | ((i < wi) ? 0 : 4); bool taken = false; grid_locks[lock_idx].Enter(ref taken); if (d < distances[i, j, k]) // have to check again in case grid changed in another thread... { distances[i, j, k] = d; closest_tri[i, j, k] = tid; } grid_locks[lock_idx].Exit(); } } } } }); if (CancelF()) { return; } if (ComputeSigns == true) { if (DebugPrint) { System.Console.WriteLine("done narrow-band"); } compute_intersections(origin, dx, ni, nj, nk, intersection_count); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done intersections"); } if (ComputeMode == ComputeModes.FullGrid) { // and now we fill in the rest of the distances with fast sweeping for (int pass = 0; pass < 2; ++pass) { sweep_pass(origin, dx, distances, closest_tri); if (CancelF()) { return; } } if (DebugPrint) { System.Console.WriteLine("done sweeping"); } } else { // nothing! if (DebugPrint) { System.Console.WriteLine("skipped sweeping"); } } if (DebugPrint) { System.Console.WriteLine("done sweeping"); } // then figure out signs (inside/outside) from intersection counts compute_signs(ni, nj, nk, distances, intersection_count); if (CancelF()) { return; } if (WantIntersectionsGrid) { intersections_grid = intersection_count; } if (DebugPrint) { System.Console.WriteLine("done signs"); } } if (WantClosestTriGrid) { closest_tri_grid = closest_tri; } } // end make_level_set_3
void make_level_set3(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f distances, int exact_band) { distances.resize(ni, nj, nk); distances.assign((ni + nj + nk) * dx); // upper bound on distance // closest triangle id for each grid cell DenseGrid3i closest_tri = new DenseGrid3i(ni, nj, nk, -1); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} DenseGrid3i intersection_count = new DenseGrid3i(ni, nj, nk, 0); if (DebugPrint) { System.Console.WriteLine("start"); } // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d xp = Vector3d.Zero, xq = Vector3d.Zero, xr = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && CancelF()) { break; } Mesh.GetTriVertices(tid, ref xp, ref xq, ref xr); // real ijk coordinates of xp/xq/xr double fip = (xp[0] - ox) / ddx, fjp = (xp[1] - oy) / ddx, fkp = (xp[2] - oz) / ddx; double fiq = (xq[0] - ox) / ddx, fjq = (xq[1] - oy) / ddx, fkq = (xq[2] - oz) / ddx; double fir = (xr[0] - ox) / ddx, fjr = (xr[1] - oy) / ddx, fkr = (xr[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)point_triangle_distance(ref gx, ref xp, ref xq, ref xr); if (d < distances[i, j, k]) { distances[i, j, k] = d; closest_tri[i, j, k] = tid; } } } } } if (CancelF()) { return; } if (ComputeSigns == true) { if (DebugPrint) { System.Console.WriteLine("done narrow-band"); } compute_intersections(origin, dx, ni, nj, nk, intersection_count); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done intersections"); } if (ComputeMode == ComputeModes.FullGrid) { // and now we fill in the rest of the distances with fast sweeping for (int pass = 0; pass < 2; ++pass) { sweep_pass(origin, dx, distances, closest_tri); if (CancelF()) { return; } } if (DebugPrint) { System.Console.WriteLine("done sweeping"); } } else { // nothing! if (DebugPrint) { System.Console.WriteLine("skipped sweeping"); } } // then figure out signs (inside/outside) from intersection counts compute_signs(ni, nj, nk, distances, intersection_count); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done signs"); } if (WantIntersectionsGrid) { intersections_grid = intersection_count; } } if (WantClosestTriGrid) { closest_tri_grid = closest_tri; } } // end make_level_set_3
void generate_support(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f supportGrid) { supportGrid.resize(ni, nj, nk); supportGrid.assign(1); // sentinel if (DebugPrint) System.Console.WriteLine("start"); bool CHECKERBOARD = false; System.Console.WriteLine("Computing SDF"); // compute unsigned SDF MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = true, ExactBandWidth = 3, /*,ComputeMode = MeshSignedDistanceGrid.ComputeModes.FullGrid*/ }; sdf.CancelF = Cancelled; sdf.Compute(); if (Cancelled()) return; var distanceField = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); double angle = MathUtil.Clamp(OverhangAngleDeg, 0.01, 89.99); double cos_thresh = Math.Cos(angle * MathUtil.Deg2Rad); System.Console.WriteLine("Marking overhangs"); // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && Cancelled()) break; Mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d normal = MathUtil.Normal(ref va, ref vb, ref vc); if (normal.Dot(-Vector3d.AxisY) < cos_thresh) continue; // real ijk coordinates of va/vb/vc double fip = (va[0] - ox) / ddx, fjp = (va[1] - oy) / ddx, fkp = (va[2] - oz) / ddx; double fiq = (vb[0] - ox) / ddx, fjq = (vb[1] - oy) / ddx, fkq = (vb[2] - oz) / ddx; double fir = (vc[0] - ox) / ddx, fjr = (vc[1] - oy) / ddx, fkr = (vc[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int exact_band = 0; int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); // don't put into y=0 plane if (j0 == 0) j0 = 1; // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)MeshSignedDistanceGrid.point_triangle_distance(ref gx, ref va, ref vb, ref vc); // vertical checkerboard pattern (eg 'tips') if (CHECKERBOARD) { int zz = (k % 2 == 0) ? 1 : 0; if (i % 2 == zz) continue; } if (d < dx / 2) { if (j > 1) { supportGrid[i, j, k] = SUPPORT_TIP_TOP; supportGrid[i, j - 1, k] = SUPPORT_TIP_BASE; } else { supportGrid[i, j, k] = SUPPORT_TIP_BASE; } } } } } } if (Cancelled()) return; //process_version1(supportGrid, distanceField); //process_version2(supportGrid, distanceField); generate_graph(supportGrid, distanceField); //Util.WriteDebugMesh(MakeDebugGraphMesh(), "c:\\scratch\\__LAST_GRAPH_INIT.obj"); postprocess_graph(); //Util.WriteDebugMesh(MakeDebugGraphMesh(), "c:\\scratch\\__LAST_GRAPH_OPT.obj"); }
void make_level_set3(DMesh3 mesh, /*const std::vector<Vec3ui> &tri, const std::vector<Vec3f> &x*/ Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f phi, int exact_band) { phi.resize(ni, nj, nk); phi.assign((ni + nj + nk) * dx); // upper bound on distance DenseGrid3i closest_tri = new DenseGrid3i(ni, nj, nk, -1); DenseGrid3i intersection_count = new DenseGrid3i(ni, nj, nk, 0); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} // we begin by initializing distances near the mesh, and figuring out intersection counts System.Console.WriteLine("start"); //Vector3f ijkmin, ijkmax; // [RMS] unused in original code double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; foreach (int t in mesh.TriangleIndices()) { Index3i triangle = mesh.GetTriangle(t); int p = triangle.a, q = triangle.b, r = triangle.c; Vector3d xp = mesh.GetVertex(p); Vector3d xq = mesh.GetVertex(q); Vector3d xr = mesh.GetVertex(r); // coordinates in grid to high precision double fip = (xp[0] - ox) / ddx, fjp = (xp[1] - oy) / ddx, fkp = (xp[2] - oz) / ddx; double fiq = (xq[0] - ox) / ddx, fjq = (xq[1] - oy) / ddx, fkq = (xq[2] - oz) / ddx; double fir = (xr[0] - ox) / ddx, fjr = (xr[1] - oy) / ddx, fkr = (xr[2] - oz) / ddx; // do distances nearby int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3f gx = new Vector3f((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = point_triangle_distance(gx, (Vector3f)xp, (Vector3f)xq, (Vector3f)xr); if (d < phi[i, j, k]) { phi[i, j, k] = d; closest_tri[i, j, k] = t; } } } } // and do intersection counts j0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fjp, fjq, fjr)), 0, nj - 1); j1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fjp, fjq, fjr)), 0, nj - 1); k0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fkp, fkq, fkr)), 0, nk - 1); k1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fkp, fkq, fkr)), 0, nk - 1); for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { double a, b, c; if (point_in_triangle_2d(j, k, fjp, fkp, fjq, fkq, fjr, fkr, out a, out b, out c)) { double fi = a * fip + b * fiq + c * fir; // intersection i coordinate int i_interval = (int)(Math.Ceiling(fi)); // intersection is in (i_interval-1,i_interval] if (i_interval < 0) { intersection_count.increment(0, j, k); // we enlarge the first interval to include everything to the -x direction } else if (i_interval < ni) { intersection_count.increment(i_interval, j, k); } // we ignore intersections that are beyond the +x side of the grid } } } } System.Console.WriteLine("done narrow-band"); // and now we fill in the rest of the distances with fast sweeping for (int pass = 0; pass < 2; ++pass) { sweep(mesh, phi, closest_tri, origin, dx, +1, +1, +1); sweep(mesh, phi, closest_tri, origin, dx, -1, -1, -1); sweep(mesh, phi, closest_tri, origin, dx, +1, +1, -1); sweep(mesh, phi, closest_tri, origin, dx, -1, -1, +1); sweep(mesh, phi, closest_tri, origin, dx, +1, -1, +1); sweep(mesh, phi, closest_tri, origin, dx, -1, +1, -1); sweep(mesh, phi, closest_tri, origin, dx, +1, -1, -1); sweep(mesh, phi, closest_tri, origin, dx, -1, +1, +1); } System.Console.WriteLine("done sweeping"); // then figure out signs (inside/outside) from intersection counts for (int k = 0; k < nk; ++k) { for (int j = 0; j < nj; ++j) { int total_count = 0; for (int i = 0; i < ni; ++i) { total_count += intersection_count[i, j, k]; if (total_count % 2 == 1) // if parity of intersections so far is odd, { phi[i, j, k] = -phi[i, j, k]; // we are inside the mesh } } } } System.Console.WriteLine("done signs"); } // end make_level_set_3
void generate_support(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f supportGrid) { supportGrid.resize(ni, nj, nk); supportGrid.assign(1); // sentinel bool CHECKERBOARD = false; // compute unsigned SDF int exact_band = 1; if (SubtractMesh && SubtractMeshOffset > 0) { int offset_band = (int)(SubtractMeshOffset / CellSize) + 1; exact_band = Math.Max(exact_band, offset_band); } sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = true, ExactBandWidth = exact_band }; sdf.CancelF = this.CancelF; sdf.Compute(); if (CancelF()) { return; } var distanceField = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); double angle = MathUtil.Clamp(OverhangAngleDeg, 0.01, 89.99); double cos_thresh = Math.Cos(angle * MathUtil.Deg2Rad); // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && CancelF()) { break; } Mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d normal = MathUtil.Normal(ref va, ref vb, ref vc); if (normal.Dot(-Vector3d.AxisY) < cos_thresh) { continue; } // real ijk coordinates of va/vb/vc double fip = (va[0] - ox) / ddx, fjp = (va[1] - oy) / ddx, fkp = (va[2] - oz) / ddx; double fiq = (vb[0] - ox) / ddx, fjq = (vb[1] - oy) / ddx, fkq = (vb[2] - oz) / ddx; double fir = (vc[0] - ox) / ddx, fjr = (vc[1] - oy) / ddx, fkr = (vc[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int extra_band = 0; int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - extra_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + extra_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - extra_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + extra_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - extra_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + extra_band + 1, 0, nk - 1); // don't put into y=0 plane //if (j0 == 0) // j0 = 1; // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)MeshSignedDistanceGrid.point_triangle_distance(ref gx, ref va, ref vb, ref vc); // vertical checkerboard pattern (eg 'tips') if (CHECKERBOARD) { int zz = (k % 2 == 0) ? 1 : 0; if (i % 2 == zz) { continue; } } if (d < dx / 2) { supportGrid[i, j, k] = SUPPORT_TIP_TOP; } } } } } if (CancelF()) { return; } fill_vertical_spans(supportGrid, distanceField); generate_mesh(supportGrid, distanceField); }