static void smooth(DenseGrid3f grid, DenseGrid3f tmp, float alpha, int iters, int min_j = 1)
        {
            if ( tmp == null )
                tmp = new DenseGrid3f(grid);

            int ni = grid.ni, nj = grid.nj, nk = grid.nk;

            for (int iter = 0; iter < iters; ++iter) {

                for (int j = min_j; j < nj-1; ++j) {
                    for (int k = 1; k < nk - 1; ++k) {
                        for (int i = 1; i < ni - 1; ++i) {

                            float avg = 0;
                            foreach (Vector3i o in gIndices.GridOffsets26) {
                                int xi = i + o.x, yi = j + o.y, zi = k + o.z;
                                float f = grid[xi, yi, zi];
                                avg += f;
                            }
                            avg /= 26.0f;
                            tmp[i, j, k] = (1 - alpha) * grid[i, j, k] + (alpha) * avg;
                        }
                    }
                }

                grid.swap(tmp);
            }
        }
        public void Generate()
        {
            // figure out origin & dimensions
            AxisAlignedBox3d bounds = Mesh.CachedBounds;
            if (ForceMinY != float.MaxValue )
                bounds.Min.y = ForceMinY;

            // expand grid so we have some border space in x and z
            float fBufferWidth = 2 * (float)CellSize;
            Vector3f b = new Vector3f(fBufferWidth, 0, fBufferWidth);
            grid_origin = (Vector3f)bounds.Min - b;

            // need zero isovalue to be at y=0. right now we set yi=0 voxels to be -1,
            // so if we nudge up half a cell, then interpolation with boundary outside
            // value should be 0 right at cell border (seems to be working?)
            grid_origin.y += (float)CellSize * 0.5f;

            Vector3f max = (Vector3f)bounds.Max + b;
            int ni = (int)((max.x - grid_origin.x) / (float)CellSize) + 1;
            int nj = (int)((max.y - grid_origin.y) / (float)CellSize) + 1;
            int nk = (int)((max.z - grid_origin.z) / (float)CellSize) + 1;

            volume_grid = new DenseGrid3f();
            generate_support(grid_origin, (float)CellSize, ni, nj, nk, volume_grid);
        }
예제 #3
0
        public void swap(DenseGrid3f g2)
        {
            Util.gDevAssert(ni == g2.ni && nj == g2.nj && nk == g2.nk);
            var tmp = g2.Buffer;

            g2.Buffer   = this.Buffer;
            this.Buffer = tmp;
        }
 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;
 }
예제 #5
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
                            }
                        }
                    }
                }
            }
        }
예제 #6
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);
        }
 IEnumerable<int> down_neighbours(Vector3i idx, DenseGrid3f grid)
 {
     yield return grid.to_linear(idx.x, idx.y - 1, idx.z);
     yield return grid.to_linear(idx.x-1, idx.y - 1, idx.z);
     yield return grid.to_linear(idx.x+1, idx.y - 1, idx.z);
     yield return grid.to_linear(idx.x, idx.y - 1, idx.z-1);
     yield return grid.to_linear(idx.x, idx.y - 1, idx.z+1);
     yield return grid.to_linear(idx.x-1, idx.y - 1, idx.z-1);
     yield return grid.to_linear(idx.x+1, idx.y - 1, idx.z-1);
     yield return grid.to_linear(idx.x-1, idx.y - 1, idx.z+1);
     yield return grid.to_linear(idx.x+1, idx.y - 1, idx.z+1);
 }
예제 #8
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);
                    }
                }
            }
        }
예제 #9
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;
                }
            }
        }
예제 #10
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];
         }
     }
 }
        public ImplicitFieldSampler3d(AxisAlignedBox3d fieldBounds, double cellSize)
        {
            CellSize   = cellSize;
            GridOrigin = fieldBounds.Min;
            Indexer    = new ShiftGridIndexer3(GridOrigin, CellSize);

            Vector3d max = fieldBounds.Max; max += cellSize;
            int      ni  = (int)((max.x - GridOrigin.x) / CellSize) + 1;
            int      nj  = (int)((max.y - GridOrigin.y) / CellSize) + 1;
            int      nk  = (int)((max.z - GridOrigin.z) / CellSize) + 1;

            GridBounds = new AxisAlignedBox3i(0, 0, 0, ni, nj, nk);

            BackgroundValue = (float)((ni + nj + nk) * CellSize);
            Grid            = new DenseGrid3f(ni, nj, nk, BackgroundValue);
        }
예제 #12
0
        public void Compute()
        {
            // figure out origin & dimensions
            AxisAlignedBox3d bounds = Mesh.CachedBounds;

            float fBufferWidth = 2 * ExactBandWidth * CellSize;

            grid_origin = (Vector3f)bounds.Min - fBufferWidth * Vector3f.One;
            Vector3f max = (Vector3f)bounds.Max + fBufferWidth * Vector3f.One;
            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;

            grid = new DenseGrid3f();
            make_level_set3(Mesh, grid_origin, CellSize, ni, nj, nk, grid, ExactBandWidth);
        }
        void generate_mesh(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField)
        {
            DenseGridTrilinearImplicit volume = new DenseGridTrilinearImplicit(
                supportGrid, GridOrigin, CellSize);
            BoundedImplicitFunction3d inputF = volume;

            if (SubtractMesh)
            {
                BoundedImplicitFunction3d sub = distanceField;
                if (SubtractMeshOffset > 0)
                {
                    sub = new ImplicitOffset3d()
                    {
                        A = distanceField, Offset = SubtractMeshOffset
                    }
                }
                ;
                ImplicitDifference3d subtract = new ImplicitDifference3d()
                {
                    A = volume, B = sub
                };
                inputF = subtract;
            }

            ImplicitHalfSpace3d cutPlane = new ImplicitHalfSpace3d()
            {
                Origin = Vector3d.Zero, Normal = Vector3d.AxisY
            };
            ImplicitDifference3d cut = new ImplicitDifference3d()
            {
                A = inputF, B = cutPlane
            };

            MarchingCubes mc = new MarchingCubes()
            {
                Implicit = cut, Bounds = grid_bounds, CubeSize = CellSize
            };

            mc.Bounds.Min.y  = -2 * mc.CubeSize;
            mc.Bounds.Min.x -= 2 * mc.CubeSize; mc.Bounds.Min.z -= 2 * mc.CubeSize;
            mc.Bounds.Max.x += 2 * mc.CubeSize; mc.Bounds.Max.z += 2 * mc.CubeSize;
            mc.Generate();

            SupportMesh = mc.Mesh;
        }
    }
예제 #14
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];
         }
     }
 }
        void fill_vertical_spans(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 = get_cell_center(i, j, k);
                            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;
                        }
                    }
                }
            }
        }
        void generate_graph(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField)
        {
            int ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk;
            float dx = (float)CellSize;
            Vector3f origin = this.GridOrigin;

            // parameters for initializing cost grid
            float MODEL_SPACE = 0.01f;      // needs small positive so that points on triangles count as inside (eg on ground plane)
            //float MODEL_SPACE = 2.0f*(float)CellSize;
            float CRAZY_DISTANCE = 99999.0f;
            bool UNIFORM_DISTANCE = true;
            float MAX_DIST = 10 * (float)CellSize;

            // parameters for sorting seeds
            Vector3i center_idx = new Vector3i(ni / 2, 0, nk / 2);      // middle
            //Vector3i center_idx = new Vector3i(0, 0, 0);              // corner
            bool reverse_per_layer = true;

            DenseGrid3f costGrid = new DenseGrid3f(supportGrid);
            foreach ( Vector3i ijk in costGrid.Indices() ) {
                Vector3d cell_center = new Vector3f(ijk.x * dx, ijk.y * dx, ijk.z * dx) + origin;
                float f = (float)distanceField.Value(ref cell_center);
                if (f <= MODEL_SPACE)
                    f = CRAZY_DISTANCE;
                else if (UNIFORM_DISTANCE)
                    f = 1.0f;
                else if (f > MAX_DIST)
                    f = MAX_DIST;
                costGrid[ijk] = f;
            }

            // Find seeds on each layer, sort, and add to accumulated bottom-up seeds list.
            // This sorting has an *enormous* effect on the support generation.

            List<Vector3i> seeds = new List<Vector3i>();
            List<Vector3i> layer_seeds = new List<Vector3i>();
            for (int j = 0; j < nj; ++j) {
                layer_seeds.Clear();
                for (int k = 0; k < nk; ++k) {
                    for (int i = 0; i < ni; ++i) {
                        if (supportGrid[i, j, k] == SUPPORT_TIP_BASE)
                            layer_seeds.Add(new Vector3i(i, j, k));
                    }
                }

                layer_seeds.Sort((a, b) => {
                    Vector3i pa = a; pa.y = 0;
                    Vector3i pb = b; pb.y = 0;
                    int sa = (pa-center_idx).LengthSquared, sb = (pb-center_idx).LengthSquared;
                    return sa.CompareTo(sb);
                });

                // reversing sort order is intresting?
                if(reverse_per_layer)
                    layer_seeds.Reverse();

                seeds.AddRange(layer_seeds);
            }
            HashSet<Vector3i> seed_indices = new HashSet<Vector3i>(seeds);

            // gives very different results...
            if (ProcessBottomUp == false)
                seeds.Reverse();

            // for linear index a, is this a node we allow in graph? (ie graph bounds)
            Func<int, bool> node_filter_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                // why not y check??
                return ai.x > 0 &&  ai.z > 0 && ai.x != ni - 1 && ai.y != nj - 1 && ai.z != nk - 1;
            };

            // distance from linear index a to linear index b
            // this defines the cost field we want to find shortest path through
            Func<int, int, float> node_dist_f = (a, b) => {
                Vector3i ai = costGrid.to_index(a), bi = costGrid.to_index(b);
                if (bi.y >= ai.y)               // b.y should always be a.y-1
                    return float.MaxValue;
                float sg = supportGrid[bi];

                // don't connect to tips
                //if (sg == SUPPORT_TIP_BASE || sg == SUPPORT_TIP_TOP)
                //    return float.MaxValue;
                if (sg == SUPPORT_TIP_TOP)
                    return float.MaxValue;

                if (sg < 0)
                    return -999999;    // if b is already used, we will terminate there, so this is a good choice

                // otherwise cost is sqr-grid-distance + costGrid value  (which is basically distance to surface)
                float c = costGrid[b];
                float f = (float)(Math.Sqrt((bi - ai).LengthSquared) * CellSize);
                //float f = 0;
                return c + f;
            };

            // which linear-index nbrs to consider for linear index a
            Func<int, IEnumerable<int>> neighbour_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                return down_neighbours(ai, costGrid);
            };

            // when do we terminate
            Func<int, bool> terminate_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                // terminate if we hit existing support path
                if (seed_indices.Contains(ai) == false && supportGrid[ai] < 0)
                    return true;
                // terminate if we hit ground plane
                if (ai.y == 0)
                    return true;
                return false;
            };

            DijkstraGraphDistance dijkstra = new DijkstraGraphDistance(ni * nj * nk, false,
                node_filter_f, node_dist_f, neighbour_f);
            dijkstra.TrackOrder = true;

            List<int> path = new List<int>();

            Graph = new DGraph3();
            Dictionary<Vector3i, int> CellToGraph = new Dictionary<Vector3i, int>();
            TipVertices = new HashSet<int>();
            TipBaseVertices = new HashSet<int>();
            GroundVertices = new HashSet<int>();

            // seeds are tip-base points
            for (int k = 0; k < seeds.Count; ++k) {
                // add seed point (which is a tip-base vertex) as seed for dijkstra prop
                int seed = costGrid.to_linear(seeds[k]);
                dijkstra.Reset();
                dijkstra.AddSeed(seed, 0);

                // compute to termination (ground, existing node, etc)
                int base_node = dijkstra.ComputeToNode(terminate_f);
                if (base_node < 0)
                    base_node = dijkstra.GetOrder().Last();

                // extract the path
                path.Clear();
                dijkstra.GetPathToSeed(base_node, path);
                int N = path.Count;

                // first point on path is termination point.
                // create vertex for it if we have not yet
                Vector3i basept_idx = supportGrid.to_index(path[0]);
                int basept_vid;
                if ( CellToGraph.TryGetValue(basept_idx, out basept_vid) == false ) {
                    Vector3d curv = get_cell_center(basept_idx);
                    if (basept_idx.y == 0) {
                        curv.y = 0;
                    }
                    basept_vid = Graph.AppendVertex(curv);
                    if (basept_idx.y == 0) {
                        GroundVertices.Add(basept_vid);
                    }
                    CellToGraph[basept_idx] = basept_vid;
                }

                int cur_vid = basept_vid;

                // now walk up path and create vertices as necessary
                for (int i = 0; i < N; ++i) {
                    int idx = path[i];
                    if ( supportGrid[idx] >= 0 )
                        supportGrid[idx] = SUPPORT_GRID_USED;
                    if ( i > 0 ) {
                        Vector3i next_idx = supportGrid.to_index(path[i]);
                        int next_vid;
                        if (CellToGraph.TryGetValue(next_idx, out next_vid) == false) {
                            Vector3d nextv = get_cell_center(next_idx);
                            next_vid = Graph.AppendVertex(nextv);
                            CellToGraph[next_idx] = next_vid;
                        }
                        Graph.AppendEdge(cur_vid, next_vid);
                        cur_vid = next_vid;
                    }
                }

                // seed was tip-base so we should always get back there. Then we
                // explicitly add tip-top and edge to it.
                if ( supportGrid[path[N-1]] == SUPPORT_TIP_BASE ) {
                    Vector3i vec_idx = supportGrid.to_index(path[N-1]);
                    TipBaseVertices.Add(CellToGraph[vec_idx]);

                    Vector3i tip_idx = vec_idx + Vector3i.AxisY;
                    int tip_vid;
                    if (CellToGraph.TryGetValue(tip_idx, out tip_vid) == false) {
                        Vector3d tipv = get_cell_center(tip_idx);
                        tip_vid = Graph.AppendVertex(tipv);
                        CellToGraph[tip_idx] = tip_vid;
                        Graph.AppendEdge(cur_vid, tip_vid);
                        TipVertices.Add(tip_vid);
                    }
                }

            }

            /*
             * Snap tips to surface
             */

            gParallel.ForEach(TipVertices, (tip_vid) => {
                bool snapped = false;
                Vector3d v = Graph.GetVertex(tip_vid);
                Frame3f hitF;
                // try shooting ray straight up. if that hits, and point is close, we use it
                if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, Vector3d.AxisY), out hitF)) {
                    if (v.Distance(hitF.Origin) < 2 * CellSize) {
                        v = hitF.Origin;
                        snapped = true;
                    }
                }

                // if that failed, try straight down
                if (!snapped) {
                    if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, -Vector3d.AxisY), out hitF)) {
                        if (v.Distance(hitF.Origin) < CellSize) {
                            v = hitF.Origin;
                            snapped = true;
                        }
                    }
                }

                // if it missed, or hit pt was too far, find nearest point and try that
                if (!snapped) {
                    hitF = MeshQueries.NearestPointFrame(Mesh, MeshSpatial, v);
                    if (v.Distance(hitF.Origin) < 2 * CellSize) {
                        v = hitF.Origin;
                        snapped = true;
                    }
                    // can this ever fail? tips should always be within 2 cells...
                }
                if (snapped)
                    Graph.SetVertex(tip_vid, v);
            });
        }
예제 #17
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
예제 #18
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
 public CachingDenseGridTrilinearImplicit(Vector3d gridOrigin, double cellSize, Vector3i gridDimensions)
 {
     Grid       = new DenseGrid3f(gridDimensions.x, gridDimensions.y, gridDimensions.z, Invalid);
     GridOrigin = gridOrigin;
     CellSize   = cellSize;
 }
        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");
        }
예제 #21
0
 public DenseGrid3f(DenseGrid3f copy)
 {
     Buffer = new float[copy.Buffer.Length];
     Array.Copy(copy.Buffer, Buffer, Buffer.Length);
     ni = copy.ni; nj = copy.nj; nk = copy.nk;
 }
예제 #22
0
 public DenseGridTrilinearImplicit(DenseGrid3f grid, Vector3d gridOrigin, double cellSize)
 {
     Grid       = grid;
     GridOrigin = gridOrigin;
     CellSize   = cellSize;
 }
예제 #23
0
 public DenseGridTrilinearImplicit(MeshSignedDistanceGrid sdf_grid)
 {
     Grid       = sdf_grid.Grid;
     GridOrigin = sdf_grid.GridOrigin;
     CellSize   = sdf_grid.CellSize;
 }
        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);
            //        }
            //    }
            //}
        }
        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");
        }
예제 #26
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 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);
        }