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;
 }
Пример #3
0
        // iterate through each x-row of grid and set unsigned distances to be negative
        // inside the mesh, based on the intersection_counts
        void compute_signs(int ni, int nj, int nk, DenseGrid3f distances, DenseGrid3i intersection_counts)
        {
            Func <int, bool> isInsideF = (count) => { return(count % 2 == 1); };

            if (InsideMode == InsideModes.ParityCount)
            {
                isInsideF = (count) => { return(count > 0); }
            }
            ;

            if (UseParallel)
            {
                // can process each x-row in parallel
                AxisAlignedBox2i box = new AxisAlignedBox2i(0, 0, nj, nk);
                gParallel.ForEach(box.IndicesExclusive(), (vi) => {
                    if (CancelF())
                    {
                        return;
                    }

                    int j           = vi.x, k = vi.y;
                    int total_count = 0;
                    for (int i = 0; i < ni; ++i)
                    {
                        total_count += intersection_counts[i, j, k];
                        if (isInsideF(total_count))                   // if parity of intersections so far is odd,
                        {
                            distances[i, j, k] = -distances[i, j, k]; // we are inside the mesh
                        }
                    }
                });
            }
            else
            {
                for (int k = 0; k < nk; ++k)
                {
                    if (CancelF())
                    {
                        return;
                    }

                    for (int j = 0; j < nj; ++j)
                    {
                        int total_count = 0;
                        for (int i = 0; i < ni; ++i)
                        {
                            total_count += intersection_counts[i, j, k];
                            if (isInsideF(total_count))                   // if parity of intersections so far is odd,
                            {
                                distances[i, j, k] = -distances[i, j, k]; // we are inside the mesh
                            }
                        }
                    }
                }
            }
        }
Пример #4
0
        }   // end make_level_set_3

        // sweep through grid in different directions, distances and closest tris
        void sweep_pass(Vector3f origin, float dx,
                        DenseGrid3f distances, DenseGrid3i closest_tri)
        {
            sweep(distances, closest_tri, origin, dx, +1, +1, +1);
            sweep(distances, closest_tri, origin, dx, -1, -1, -1);
            sweep(distances, closest_tri, origin, dx, +1, +1, -1);
            sweep(distances, closest_tri, origin, dx, -1, -1, +1);
            sweep(distances, closest_tri, origin, dx, +1, -1, +1);
            sweep(distances, closest_tri, origin, dx, -1, +1, -1);
            sweep(distances, closest_tri, origin, dx, +1, -1, -1);
            sweep(distances, closest_tri, origin, dx, -1, +1, +1);
        }
Пример #5
0
        // single sweep pass
        void sweep(DenseGrid3f phi, DenseGrid3i closest_tri,
                   Vector3f origin, float dx,
                   int di, int dj, int dk)
        {
            int i0, i1;

            if (di > 0)
            {
                i0 = 1; i1 = phi.ni;
            }
            else
            {
                i0 = phi.ni - 2; i1 = -1;
            }
            int j0, j1;

            if (dj > 0)
            {
                j0 = 1; j1 = phi.nj;
            }
            else
            {
                j0 = phi.nj - 2; j1 = -1;
            }
            int k0, k1;

            if (dk > 0)
            {
                k0 = 1; k1 = phi.nk;
            }
            else
            {
                k0 = phi.nk - 2; k1 = -1;
            }
            for (int k = k0; k != k1; k += dk)
            {
                for (int j = j0; j != j1; j += dj)
                {
                    for (int i = i0; i != i1; i += di)
                    {
                        Vector3d gx = new Vector3d(i * dx + origin[0], j * dx + origin[1], k * dx + origin[2]);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i - di, j, k);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i, j - dj, k);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i - di, j - dj, k);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i, j, k - dk);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i - di, j, k - dk);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i, j - dj, k - dk);
                        check_neighbour(phi, closest_tri, ref gx, i, j, k, i - di, j - dj, k - dk);
                    }
                }
            }
        }
Пример #6
0
        public void Initialize()
        {
            // figure out origin & dimensions
            AxisAlignedBox3d bounds = Mesh.CachedBounds;

            float fBufferWidth = (float)Math.Max(4 * CellSize, 2 * MaxOffsetDistance + 2 * CellSize);

            grid_origin = (Vector3f)bounds.Min - fBufferWidth * Vector3f.One - (Vector3f)ExpandBounds;
            Vector3f max = (Vector3f)bounds.Max + fBufferWidth * Vector3f.One + (Vector3f)ExpandBounds;
            int      ni  = (int)((max.x - grid_origin.x) / CellSize) + 1;
            int      nj  = (int)((max.y - grid_origin.y) / CellSize) + 1;
            int      nk  = (int)((max.z - grid_origin.z) / CellSize) + 1;

            UpperBoundDistance = (float)((ni + nj + nk) * CellSize);
            grid = new DenseGrid3f(ni, nj, nk, UpperBoundDistance);

            MaxDistQueryDist = MaxOffsetDistance + (2 * CellSize * MathUtil.SqrtTwo);

            // closest triangle id for each grid cell
            if (WantClosestTriGrid)
            {
                closest_tri_grid = 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 (ComputeSigns == true)
            {
                compute_intersections(grid_origin, CellSize, ni, nj, nk, intersection_count);
                if (CancelF())
                {
                    return;
                }

                // then figure out signs (inside/outside) from intersection counts
                compute_signs(ni, nj, nk, grid, intersection_count);
                if (CancelF())
                {
                    return;
                }

                if (WantIntersectionsGrid)
                {
                    intersections_grid = intersection_count;
                }
            }
        }
Пример #7
0
 void check_neighbour(DenseGrid3f phi, DenseGrid3i closest_tri,
                      ref Vector3d gx, int i0, int j0, int k0, int i1, int j1, int k1)
 {
     if (closest_tri[i1, j1, k1] >= 0)
     {
         Vector3d xp = Vector3f.Zero, xq = Vector3f.Zero, xr = Vector3f.Zero;
         Mesh.GetTriVertices(closest_tri[i1, j1, k1], ref xp, ref xq, ref xr);
         float d = (float)point_triangle_distance(ref gx, ref xp, ref xq, ref xr);
         if (d < phi[i0, j0, k0])
         {
             phi[i0, j0, k0]         = d;
             closest_tri[i0, j0, k0] = closest_tri[i1, j1, k1];
         }
     }
 }
Пример #8
0
 static void check_neighbour(DMesh3 mesh,
                             DenseGrid3f phi, DenseGrid3i closest_tri,
                             Vector3f gx, int i0, int j0, int k0, int i1, int j1, int k1)
 {
     if (closest_tri[i1, j1, k1] >= 0)
     {
         Index3i  tri = mesh.GetTriangle(closest_tri[i1, j1, k1]);
         int      p = tri.a, q = tri.b, r = tri.c;
         Vector3f xp = (Vector3f)mesh.GetVertex(p);
         Vector3f xq = (Vector3f)mesh.GetVertex(q);
         Vector3f xr = (Vector3f)mesh.GetVertex(r);
         float    d  = point_triangle_distance(gx, xp, xq, xr);
         if (d < phi[i0, j0, k0])
         {
             phi[i0, j0, k0]         = d;
             closest_tri[i0, j0, k0] = closest_tri[i1, j1, k1];
         }
     }
 }
Пример #9
0
        // fill the intersection grid w/ number of intersections in each cell
        void compute_intersections(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3i intersection_count)
        {
            double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2];
            double invdx = 1.0 / dx;

            bool cancelled = false;

            // this is what we will do for each triangle. There are no grid-reads, only grid-writes,
            // since we use atomic_increment, it is always thread-safe
            Action <int> ProcessTriangleF = (tid) => {
                if (tid % 100 == 0 && CancelF() == true)
                {
                    cancelled = true;
                }
                if (cancelled)
                {
                    return;
                }

                Vector3d xp = Vector3d.Zero, xq = Vector3d.Zero, xr = Vector3d.Zero;
                Mesh.GetTriVertices(tid, ref xp, ref xq, ref xr);


                bool neg_x = false;
                if (InsideMode == InsideModes.ParityCount)
                {
                    Vector3d n = MathUtil.FastNormalDirection(ref xp, ref xq, ref xr);
                    neg_x = n.x > 0;
                }

                // 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;

                // recompute j/k integer bounds of triangle w/o exact band
                int j0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fjp, fjq, fjr)), 0, nj - 1);
                int j1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fjp, fjq, fjr)), 0, nj - 1);
                int k0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fkp, fkq, fkr)), 0, nk - 1);
                int k1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fkp, fkq, fkr)), 0, nk - 1);

                // and do intersection counts
                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.atomic_incdec(0, j, k, neg_x);
                            }
                            else if (i_interval < ni)
                            {
                                intersection_count.atomic_incdec(i_interval, j, k, neg_x);
                            }
                            else
                            {
                                // we ignore intersections that are beyond the +x side of the grid
                            }
                        }
                    }
                }
            };

            if (UseParallel)
            {
                gParallel.ForEach(Mesh.TriangleIndices(), ProcessTriangleF);
            }
            else
            {
                foreach (int tid in Mesh.TriangleIndices())
                {
                    ProcessTriangleF(tid);
                }
            }
        }
Пример #10
0
        }   // 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
Пример #11
0
        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
Пример #12
0
        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 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);
            //        }
            //    }
            //}
        }