static void skeletonize_layer(DenseGrid3i grid, int j, int dilation_rounds = 1) { DenseGrid2i layer = grid.get_slice(j, 1); DenseGrid2i tmp = new DenseGrid2i(layer.ni, layer.nj, 0); for (int k = 0; k < dilation_rounds; ++k) { tmp.assign(0); dilate(layer, tmp); } bool done = false; while (!done) { int sum_before = layer.sum(); tmp.assign(0); skeletonize_pass(layer, tmp, 0); tmp.assign(0); skeletonize_pass(layer, tmp, 1); int sum_after = layer.sum(); if (sum_before == sum_after) { break; } } for (int i = 0; i < grid.ni; ++i) { for (int k = 0; k < grid.nk; ++k) { grid[i, j, k] = layer[i, k]; } } }
static void skeletonize(DenseGrid2i grid, DenseGrid2i tmp, int dilation_rounds = 1) { if (tmp == null) { tmp = new DenseGrid2i(grid.ni, grid.nj, 0); } for (int k = 0; k < dilation_rounds; ++k) { tmp.clear(); dilate(grid, tmp); } bool done = false; while (!done) { int sum_before = grid.sum(); tmp.clear(); skeletonize_pass(grid, tmp, 0); tmp.clear(); skeletonize_pass(grid, tmp, 1); int sum_after = grid.sum(); if (sum_before == sum_after) { break; } } }
static void dilate(DenseGrid2i grid, DenseGrid2i tmp, bool corners = true) { if (tmp == null) { tmp = new DenseGrid2i(grid.ni, grid.nj, 0); } int ni = grid.ni, nj = grid.nj; for (int i = 1; i < ni - 1; i++) { for (int j = 1; j < nj - 1; j++) { if (grid[i, j] == 1) { tmp[i, j] = 1; tmp[i - 1, j] = 1; tmp[i, j + 1] = 1; tmp[i + 1, j] = 1; tmp[i, j - 1] = 1; if (corners) { tmp[i - 1, j + 1] = 1; tmp[i + 1, j + 1] = 1; tmp[i + 1, j - 1] = 1; tmp[i - 1, j - 1] = 1; } } } } grid.copy(tmp); }
static DenseGrid2i binarize(DenseGrid2f grid, float thresh = 0) { DenseGrid2i result = new DenseGrid2i(); result.resize(grid.ni, grid.nj); int size = result.size; for (int k = 0; k < size; ++k) { result[k] = (grid[k] < thresh) ? 1 : 0; } return(result); }
bool is_loner(DenseGrid2i grid, int i, int j) { if (grid[i, j] == 0) { return(false); } int nbrs = grid[i - 1, j] + grid[i - 1, j + 1] + grid[i, j + 1] + grid[i + 1, j + 1] + grid[i + 1, j] + grid[i + 1, j - 1] + grid[i, j - 1] + grid[i - 1, j - 1]; return(nbrs == 0); }
static void dilate_loners(DenseGrid2i grid, DenseGrid2i tmp, int mode) { if (tmp == null) { tmp = new DenseGrid2i(grid.ni, grid.nj, 0); } int ni = grid.ni, nj = grid.nj; for (int i = 1; i < ni - 1; i++) { for (int j = 1; j < nj - 1; j++) { if (grid[i, j] == 1) { tmp[i, j] = 1; int nbrs = grid[i - 1, j] + grid[i - 1, j + 1] + grid[i, j + 1] + grid[i + 1, j + 1] + grid[i + 1, j] + grid[i + 1, j - 1] + grid[i, j - 1] + grid[i - 1, j - 1]; if (nbrs == 0) { if (mode != 3) { tmp[i - 1, j] = 1; tmp[i + 1, j] = 1; tmp[i, j + 1] = 1; tmp[i, j - 1] = 1; } if (mode == 2 || mode == 3) { tmp[i - 1, j + 1] = 1; tmp[i + 1, j + 1] = 1; tmp[i + 1, j - 1] = 1; tmp[i - 1, j - 1] = 1; } } } } } grid.copy(tmp); }
static void skeletonize_pass(DenseGrid2i grid, DenseGrid2i tmp, int iter) { int ni = grid.ni, nj = grid.nj; for (int i = 1; i < ni - 1; i++) { for (int j = 1; j < nj - 1; j++) { int p2 = grid[i - 1, j]; int p3 = grid[i - 1, j + 1]; int p4 = grid[i, j + 1]; int p5 = grid[i + 1, j + 1]; int p6 = grid[i + 1, j]; int p7 = grid[i + 1, j - 1]; int p8 = grid[i, j - 1]; int p9 = grid[i - 1, j - 1]; int A = ((p2 == 0 && p3 == 1) ? 1 : 0) + ((p3 == 0 && p4 == 1) ? 1 : 0) + ((p4 == 0 && p5 == 1) ? 1 : 0) + ((p5 == 0 && p6 == 1) ? 1 : 0) + ((p6 == 0 && p7 == 1) ? 1 : 0) + ((p7 == 0 && p8 == 1) ? 1 : 0) + ((p8 == 0 && p9 == 1) ? 1 : 0) + ((p9 == 0 && p2 == 1) ? 1 : 0); int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8); int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8); if (A == 1 && B >= 2 && B <= 6 && m1 == 0 && m2 == 0) { tmp[i, j] = 1; } } } for (int i = 0; i < ni; ++i) { for (int j = 0; j < nj; ++j) { grid[i, j] = grid[i, j] & ~tmp[i, j]; } } }
void process_version2(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField) { int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk; float dx = (float)CellSize; Vector3f origin = this.GridOrigin; // sweep values down layer by layer DenseGrid2f prev = supportGrid.get_slice(nj - 1, 1); DenseGrid2f tmp = new DenseGrid2f(prev); Bitmap3 bmp = new Bitmap3(new Vector3i(ni, nj, nk)); for (int j = nj - 2; j >= 0; j--) { // skeletonize prev layer DenseGrid2i prev_skel = binarize(prev, 0.0f); skeletonize(prev_skel, null, 2); //dilate_loners(prev_skel, null, 2); if (j == 0) { dilate(prev_skel, null, true); dilate(prev_skel, null, true); } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { bmp[new Vector3i(i, j, k)] = (prev_skel[i, k] == 1) ? true : false; } } smooth(prev, tmp, 0.5f, 5); DenseGrid2f cur = supportGrid.get_slice(j, 1); cur.set_min(prev); for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { float skelf = prev_skel[i, k] > 0 ? -1.0f : int.MaxValue; cur[i, k] = Math.Min(cur[i, k], skelf); if (cur[i, k] < 0) { Vector3d cell_center = new Vector3f(i * dx, j * dx, k * dx) + origin; if (distanceField.Value(ref cell_center) < -CellSize) { cur[i, k] = 1; } } } } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { if (is_loner(prev_skel, i, k)) { foreach (Vector2i d in gIndices.GridOffsets8) { float f = 1.0f / (float)Math.Sqrt(d.x * d.x + d.y * d.y); cur[i + d.x, k + d.y] += -0.25f * f; } } } } for (int k = 1; k < nk - 1; ++k) { for (int i = 1; i < ni - 1; ++i) { supportGrid[i, j, k] = cur[i, k]; } } prev.swap(cur); } VoxelSurfaceGenerator gen = new VoxelSurfaceGenerator() { Voxels = bmp }; gen.Generate(); Util.WriteDebugMesh(gen.Meshes[0], "c:\\scratch\\binary.obj"); }