/// <summary> /// count 6-nbrs of each voxel, discard if count <= minNbrs /// </summary> public void Filter(int nMinNbrs) { AxisAlignedBox3i bounds = GridBounds; bounds.Max -= Vector3i.One; for (int i = 0; i < Bits.Length; ++i) { if (Bits[i] == false) { continue; } Vector3i idx = ToIndex(i); int nc = 0; for (int k = 0; k < 6 && nc <= nMinNbrs; ++k) { Vector3i nbr = idx + gIndices.GridOffsets6[k]; if (bounds.Contains(nbr) == false) { continue; } if (Get(nbr)) { nc++; } } if (nc <= nMinNbrs) { Bits[i] = false; } } }
/// <summary> /// fully sequential version, no threading /// </summary> void generate_continuation(IEnumerable <Vector3d> seeds) { var cell = new GridCell(); int[] vertlist = new int[12]; done_cells = new DenseGrid3i(CellDimensions.x, CellDimensions.y, CellDimensions.z, 0); var stack = new List <Vector3i>(); foreach (Vector3d seed in seeds) { Vector3i seed_idx = cell_index(seed); if (done_cells[seed_idx] == 1) { continue; } stack.Add(seed_idx); done_cells[seed_idx] = 1; while (stack.Count > 0) { Vector3i idx = stack[stack.Count - 1]; stack.RemoveAt(stack.Count - 1); if (CancelF()) { return; } initialize_cell(cell, ref idx); if (polygonize_cell(cell, vertlist)) { // found crossing foreach (Vector3i o in gIndices.GridOffsets6) { Vector3i nbr_idx = idx + o; if (GridBounds.Contains(nbr_idx) && done_cells[nbr_idx] == 0) { stack.Add(nbr_idx); done_cells[nbr_idx] = 1; } } } } } }
public void Generate() { Append_mesh(); AxisAlignedBox3i bounds = Voxels.GridBounds; bounds.Max -= Vector3i.One; int[] vertices = new int[4]; foreach (Vector3i nz in Voxels.NonZeros()) { Check_counts_or_append(6, 2); Box3d cube = Box3d.UnitZeroCentered; cube.Center = (Vector3D)nz; for (int fi = 0; fi < 6; ++fi) { // checks dependent on neighbours Index3i nbr = nz + gIndices.GridOffsets6[fi]; if (bounds.Contains(nbr)) { if (SkipInteriorFaces && Voxels.Get(nbr)) { continue; } } else if (CapAtBoundary == false) { continue; } int ni = gIndices.BoxFaceNormals[fi]; Vector3F n = (Vector3F)(Math.Sign(ni) * cube.Axis(Math.Abs(ni) - 1)); NewVertexInfo vi = new NewVertexInfo(Vector3D.Zero, n); if (ColorSourceF != null) { vi.c = ColorSourceF(nz); vi.bHaveC = true; } for (int j = 0; j < 4; ++j) { vi.v = cube.Corner(gIndices.BoxFaces[fi, j]); vertices[j] = cur_mesh.AppendVertex(vi); } Index3i t0 = new Index3i(vertices[0], vertices[1], vertices[2], Clockwise); Index3i t1 = new Index3i(vertices[0], vertices[2], vertices[3], Clockwise); cur_mesh.AppendTriangle(t0); cur_mesh.AppendTriangle(t1); } } }
void make_grid(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f scalars) { scalars.resize(ni, nj, nk); scalars.assign(float.MaxValue); // sentinel if (DebugPrint) { System.Console.WriteLine("start"); } // Ok, because the whole idea is that the surface might have holes, we are going to // compute values along known triangles and then propagate the computed region outwards // until any iso-sign-change is surrounded. // To seed propagation, we compute unsigned SDF and then compute values for any voxels // containing surface (ie w/ distance smaller than cellsize) // compute unsigned SDF var sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = false }; sdf.CancelF = this.CancelF; sdf.Compute(); if (CancelF()) { return; } DenseGrid3f distances = sdf.Grid; if (WantMeshSDFGrid) { mesh_sdf = sdf; } if (DebugPrint) { System.Console.WriteLine("done initial sdf"); } // compute values at surface voxels double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; gParallel.ForEach(gIndices.Grid3IndicesYZ(nj, nk), (jk) => { if (CancelF()) { return; } for (int i = 0; i < ni; ++i) { var ijk = new Vector3i(i, jk.y, jk.z); float dist = distances[ijk]; // this could be tighter? but I don't think it matters... if (dist < CellSize) { var gx = new Vector3d((float)ijk.x * dx + origin[0], (float)ijk.y * dx + origin[1], (float)ijk.z * dx + origin[2]); scalars[ijk] = (float)ScalarF(gx); } } }); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done narrow-band"); } // Now propagate outwards from computed voxels. // Current procedure is to check 26-neighbours around each 'front' voxel, // and if there are any sign changes, that neighbour is added to front. // Front is initialized w/ all voxels we computed above AxisAlignedBox3i bounds = scalars.Bounds; bounds.Max -= Vector3i.One; // since we will be computing new values as necessary, we cannot use // grid to track whether a voxel is 'new' or not. // So, using 3D bitmap intead - is updated at end of each pass. var bits = new Bitmap3(new Vector3i(ni, nj, nk)); var cur_front = new List <Vector3i>(); foreach (Vector3i ijk in scalars.Indices()) { if (scalars[ijk] != float.MaxValue) { cur_front.Add(ijk); bits[ijk] = true; } } if (CancelF()) { return; } // Unique set of 'new' voxels to compute in next iteration. var queue = new HashSet <Vector3i>(); var queue_lock = new SpinLock(); while (true) { if (CancelF()) { return; } // can process front voxels in parallel bool abort = false; int iter_count = 0; gParallel.ForEach(cur_front, (ijk) => { Interlocked.Increment(ref iter_count); if (iter_count % 100 == 0) { abort = CancelF(); } if (abort) { return; } float val = scalars[ijk]; // check 26-neighbours to see if we have a crossing in any direction for (int k = 0; k < 26; ++k) { Vector3i nijk = ijk + gIndices.GridOffsets26[k]; if (bounds.Contains(nijk) == false) { continue; } float val2 = scalars[nijk]; if (val2 == float.MaxValue) { var gx = new Vector3d((float)nijk.x * dx + origin[0], (float)nijk.y * dx + origin[1], (float)nijk.z * dx + origin[2]); val2 = (float)ScalarF(gx); scalars[nijk] = val2; } if (bits[nijk] == false) { // this is a 'new' voxel this round. // If we have an iso-crossing, add it to the front next round bool crossing = (val <IsoValue && val2> IsoValue) || (val > IsoValue && val2 < IsoValue); if (crossing) { bool taken = false; queue_lock.Enter(ref taken); queue.Add(nijk); queue_lock.Exit(); } } } }); if (DebugPrint) { System.Console.WriteLine("front has {0} voxels", queue.Count); } if (queue.Count == 0) { break; } // update known-voxels list and create front for next iteration foreach (Vector3i idx in queue) { bits[idx] = true; } cur_front.Clear(); cur_front.AddRange(queue); queue.Clear(); } if (DebugPrint) { System.Console.WriteLine("done front-prop"); } if (DebugPrint) { int filled = 0; foreach (Vector3i ijk in scalars.Indices()) { if (scalars[ijk] != float.MaxValue) { filled++; } } System.Console.WriteLine("filled: {0} / {1} - {2}%", filled, ni * nj * nk, (double)filled / (double)(ni * nj * nk) * 100.0); } if (CancelF()) { return; } // fill in the rest of the grid by propagating know values fill_spans(ni, nj, nk, scalars); if (DebugPrint) { System.Console.WriteLine("done sweep"); } }