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 DenseGrid3i binarize(DenseGrid3f grid, float thresh = 0)
        {
            DenseGrid3i result = new DenseGrid3i();

            result.resize(grid.ni, grid.nj, grid.nk);
            int size = result.size;

            for (int k = 0; k < size; ++k)
            {
                result[k] = (grid[k] < thresh) ? 1 : 0;
            }
            return(result);
        }
        /// <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 GenerateContinuation(IEnumerable <Vector3d> seeds)
        {
            Mesh = new DMesh3();

            int nx = (int)(Bounds.Width / CubeSize) + 1;
            int ny = (int)(Bounds.Height / CubeSize) + 1;
            int nz = (int)(Bounds.Depth / CubeSize) + 1;

            CellDimensions = new Vector3i(nx, ny, nz);
            GridBounds     = new AxisAlignedBox3i(Vector3i.Zero, CellDimensions);

            if (LastGridBounds != GridBounds)
            {
                corner_values_grid = new DenseGrid3f(nx + 1, ny + 1, nz + 1, float.MaxValue);
                edge_vertices      = new Dictionary <long, int>();
                corner_values      = new Dictionary <long, double>();
                if (ParallelCompute)
                {
                    done_cells = new DenseGrid3i(CellDimensions.x, CellDimensions.y, CellDimensions.z, 0);
                }
            }
            else
            {
                edge_vertices.Clear();
                corner_values.Clear();
                corner_values_grid.assign(float.MaxValue);
                if (ParallelCompute)
                {
                    done_cells.assign(0);
                }
            }

            if (ParallelCompute)
            {
                generate_continuation_parallel(seeds);
            }
            else
            {
                generate_continuation(seeds);
            }

            LastGridBounds = GridBounds;
        }
        void process_version1(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, column by column
            for (int k = 0; k < nk; ++k)
            {
                for (int i = 0; i < ni; ++i)
                {
                    bool in_support = false;
                    for (int j = nj - 1; j >= 0; j--)
                    {
                        float fcur = supportGrid[i, j, k];
                        if (fcur >= 0)
                        {
                            Vector3d cell_center = new Vector3f(i * dx, j * dx, k * dx) + origin;
                            if (in_support)
                            {
                                bool is_inside = distanceField.Value(ref cell_center) < 0;
                                if (is_inside)
                                {
                                    supportGrid[i, j, k] = -3;
                                    in_support           = false;
                                }
                                else
                                {
                                    supportGrid[i, j, k] = -1;
                                }
                            }
                        }
                        else
                        {
                            in_support = true;
                        }
                    }
                }
            }


            // skeletonize each layer
            // todo: would be nice to skeletonize the 3D volume.. ?
            DenseGrid3i binary = new DenseGrid3i(ni, nj, nk, 0);

            foreach (Vector3i idx in binary.Indices())
            {
                binary[idx] = (supportGrid[idx] < 0) ? 1 : 0;
            }
            for (int j = 0; j < nj; ++j)
            {
                skeletonize_layer(binary, j);
            }


            // debug thing
            //VoxelSurfaceGenerator voxgen = new VoxelSurfaceGenerator() {
            //    Voxels = binary.get_bitmap()
            //};
            //voxgen.Generate();
            //Util.WriteDebugMesh(voxgen.makemesh(), "c:\\scratch\\binary.obj");


            // for skeleton voxels, we add some power
            for (int j = 0; j < nj; ++j)
            {
                for (int k = 1; k < nk - 1; ++k)
                {
                    for (int i = 1; i < ni - 1; ++i)
                    {
                        if (binary[i, j, k] > 0)
                        {
                            supportGrid[i, j, k] = -3;
                        }
                        //else
                        //    supportGrid[i, j, k] = 1;   // clear non-skeleton voxels
                    }
                }
            }


            // power up the ground-plane voxels
            for (int k = 0; k < nk; ++k)
            {
                for (int i = 0; i < ni; ++i)
                {
                    if (supportGrid[i, 0, k] < 0)
                    {
                        supportGrid[i, 0, k] = -5;
                    }
                }
            }


#if true
            DenseGrid3f smoothed   = new DenseGrid3f(supportGrid);
            float       nbr_weight = 0.5f;
            for (int iter = 0; iter < 15; ++iter)
            {
                // add some mass to skeleton voxels
                for (int j = 0; j < nj; ++j)
                {
                    for (int k = 1; k < nk - 1; ++k)
                    {
                        for (int i = 1; i < ni - 1; ++i)
                        {
                            if (binary[i, j, k] > 0)
                            {
                                supportGrid[i, j, k] = supportGrid[i, j, k] - nbr_weight / 25.0f;
                            }
                        }
                    }
                }

                for (int j = 0; j < nj; ++j)
                {
                    for (int k = 1; k < nk - 1; ++k)
                    {
                        for (int i = 1; i < ni - 1; ++i)
                        {
                            int   neg = 0;
                            float avg = 0, w = 0;
                            for (int n = 0; n < 8; ++n)
                            {
                                int   xi = i + gIndices.GridOffsets8[n].x;
                                int   zi = k + gIndices.GridOffsets8[n].y;
                                float f  = supportGrid[xi, j, zi];
                                if (f < 0)
                                {
                                    neg++;
                                }
                                avg += nbr_weight * f;
                                w   += nbr_weight;
                            }
                            if (neg > -1)
                            {
                                avg += supportGrid[i, j, k];
                                w   += 1.0f;
                                smoothed[i, j, k] = avg / w;
                            }
                            else
                            {
                                smoothed[i, j, k] = supportGrid[i, j, k];
                            }
                        }
                    }
                }
                supportGrid.swap(smoothed);
            }
#endif


            // hard-enforce that skeleton voxels stay inside
            //for (int j = 0; j < nj; ++j) {
            //    for (int k = 1; k < nk - 1; ++k) {
            //        for (int i = 1; i < ni - 1; ++i) {
            //            if (binary[i, j, k] > 0)
            //                supportGrid[i, j, k] = Math.Min(supportGrid[i, j, k], - 1);
            //        }
            //    }
            //}
        }