Example #1
0
        public static Rhino.Geometry.Point3d[] ToRhinoPts(this DenseGridTrilinearImplicit Value)
        {
            int dimX = Value.Grid.ni;
            int dimY = Value.Grid.nj;
            int dimZ = Value.Grid.nk;

            int counter = 0;

            Rhino.Geometry.Point3d origin = Value.GridOrigin.ToRhinoPt();
            double size = Value.CellSize;

            Rhino.Geometry.Point3d[] Pts = new Rhino.Geometry.Point3d[dimX * dimY * dimZ];

            for (int k = 0; k < dimZ; k++)
            {
                for (int j = 0; j < dimY; j++)
                {
                    for (int i = 0; i < dimX; i++)
                    {
                        Pts[counter] = origin + new Rhino.Geometry.Point3d(i * size, j * size, k * size);
                        counter++;
                    }
                }
            }

            return(Pts);
        }
        public static void test_marching_cubes_levelset()
        {
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_overlap_solids.obj");
            //Sphere3Generator_NormalizedCube gen = new Sphere3Generator_NormalizedCube() { EdgeVertices = 100, Radius = 5 };
            //DMesh3 mesh = gen.Generate().MakeDMesh();

            AxisAlignedBox3d bounds = mesh.CachedBounds;
            int    numcells         = 128;
            double cellsize         = bounds.MaxDim / numcells;

            MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, cellsize);

            levelSet.ExactBandWidth = 3;
            //levelSet.InsideMode = MeshSignedDistanceGrid.InsideModes.CrossingCount;
            levelSet.UseParallel = true;
            levelSet.ComputeMode = MeshSignedDistanceGrid.ComputeModes.NarrowBandOnly;
            levelSet.Compute();

            var iso = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize);

            MarchingCubes c = new MarchingCubes();

            c.Implicit = iso;
            c.Bounds   = mesh.CachedBounds;
            c.Bounds.Expand(c.Bounds.MaxDim * 0.1);
            c.CubeSize = c.Bounds.MaxDim / 128;
            //c.CubeSize = levelSet.CellSize;

            c.Generate();

            TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_levelset.obj");
        }
Example #3
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            Grid3f_goo   goo  = null;
            List <Curve> crvs = new List <Curve>();

            DA.GetData(0, ref goo);
            DA.GetDataList(1, crvs);

            DenseGridTrilinearImplicit grid = new DenseGridTrilinearImplicit(new DenseGrid3f(goo.Value.Grid), new g3.Vector3f(goo.Value.GridOrigin), goo.Value.CellSize);

            var pts       = grid.ToRhinoPts();
            int numCurves = crvs.Count;

            Parallel.For(0, pts.Length, i =>
            {
                var pt             = pts[i];
                double[] distances = new double[numCurves];
                for (int j = 0; j < crvs.Count; j++)
                {
                    double param = 0;
                    var crv      = crvs[j];
                    bool res     = crv.ClosestPoint(pt, out param);

                    double tempDouble = crv.PointAt(param).DistanceTo(pt);
                    distances[j]      = tempDouble;
                }
                grid.Grid.Buffer[i] = (float)distances.Min();
            });

            DA.SetData(0, grid);
        }
Example #4
0
        void cache_input_sdfs()
        {
            gParallel.ForEach(Interval1i.Range(mesh_sources.Count), (k) => {
                if (cached_sdfs[k] != null)
                {
                    return;
                }
                if (is_invalidated())
                {
                    return;
                }

                DMesh3 source_mesh = mesh_sources[k].GetDMeshUnsafe();
                Vector3d expand    = source_mesh.CachedBounds.Extents;

                int exact_cells            = 2;
                MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(source_mesh, grid_cell_size)
                {
                    ExactBandWidth = exact_cells,
                    ComputeMode    = MeshSignedDistanceGrid.ComputeModes.FullGrid,
                    ExpandBounds   = expand
                };
                sdf.CancelF = is_invalidated;
                sdf.Compute();
                if (is_invalidated())
                {
                    return;
                }


                cached_sdfs[k] = sdf;
                cached_isos[k] = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);
            });
        }
Example #5
0
        public static DMesh3 BooleanUnion(DMesh3 mesh, int num_cells)
        {
            // credits to geometry3sharp team
            double cell_size = mesh.CachedBounds.MaxDim / num_cells;

            MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size);

            sdf.Compute();

            var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);

            DMesh3 outputMesh = GenerateMeshF(iso, num_cells);

            return(outputMesh);
        }
        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;
        }
    }
        public static Mesh HollowOut(Mesh inMesh, double distance)
        {
            // Convert to DMesh3
            var mesh = inMesh.ToDMesh3();

            // Create instance of BoundedImplicitFunction3d interface
            int    numcells     = 64;
            double meshCellsize = mesh.CachedBounds.MaxDim / numcells;

            var levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize)
            {
                ExactBandWidth = (int)(distance / meshCellsize) + 1
            };

            levelSet.Compute();

            // Outer shell
            var implicitMesh = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize);

            // Offset shell
            var insetMesh = GenerateMeshF(
                new ImplicitOffset3d()
            {
                A      = implicitMesh,
                Offset = -distance
            },
                128);

            // make sure it is a reasonable number of polygons
            var reducer = new Reducer(insetMesh);

            reducer.ReduceToTriangleCount(Math.Max(inMesh.Faces.Count / 2, insetMesh.TriangleCount / 10));

            // Convert to PolygonMesh and reverse faces
            var interior = insetMesh.ToMesh();

            interior.ReverseFaces();

            // Combine the original mesh with the reversed offset resulting in hollow
            var combinedMesh = inMesh.Copy(CancellationToken.None);

            combinedMesh.CopyFaces(interior);

            return(combinedMesh);
        }
Example #8
0
        /// <summary>
        /// uses marching cubes to help smooth the mesh after using the remesher
        /// experimental
        /// </summary>
        /// <param name="edgeLength"></param>
        /// <param name="smoothSpeed"></param>
        /// <param name="iterations"></param>
        /// <param name="cells"></param>
        public void Smooth(double edgeLength, double smoothSpeed, double iterations, double cells)
        {
            //Use the Remesher class to do a basic remeshing
            DMesh3   mesh = new DMesh3(_mesh);
            Remesher r    = new Remesher(mesh);

            r.PreventNormalFlips = true;
            r.SetTargetEdgeLength(edgeLength);
            r.SmoothSpeedT = smoothSpeed;
            r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh));
            for (int k = 0; k < iterations; k++)
            {
                r.BasicRemeshPass();
            }

            //marching cubes
            int num_cells = (int)cells;

            if (cells > 0)
            {
                double cell_size = mesh.CachedBounds.MaxDim / num_cells;

                MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size);
                sdf.Compute();

                var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);

                MarchingCubes c = new MarchingCubes();
                c.Implicit = iso;
                c.Bounds   = mesh.CachedBounds;
                c.CubeSize = c.Bounds.MaxDim / cells;
                c.Bounds.Expand(3 * c.CubeSize);

                c.Generate();

                _smoothMesh = c.Mesh;
            }
            else
            {
                _smoothMesh = mesh;
            }

            _displayMesh = DMeshToMeshGeometry(_smoothMesh);
            _moldMesh    = null;
        }
        private static DMesh3 VoxelizeMesh(int numCells, DMesh3 mesh)
        {
            var cellSize = mesh.CachedBounds.MaxDim / numCells;
            var sdf      = new MeshSignedDistanceGrid(mesh, cellSize);

            sdf.Compute();

            var iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);
            var marchingCubesOperation = new MarchingCubes {
                Implicit = iso, Bounds = mesh.CachedBounds
            };

            marchingCubesOperation.CubeSize = marchingCubesOperation.Bounds.MaxDim / numCells;
            marchingCubesOperation.Bounds.Expand(2.5 * marchingCubesOperation.CubeSize);
            marchingCubesOperation.Generate();
            mesh = marchingCubesOperation.Mesh;

            return(mesh);
        }
Example #10
0
        public static void test_1()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");

            int    num_cells           = 64;
            double cell_size           = mesh.CachedBounds.MaxDim / num_cells;
            MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(mesh, cell_size)
            {
                ExactBandWidth = 5
            };

            sdf.Compute();

            var iso        = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);
            var skel_field = new DistanceFieldToSkeletalField()
            {
                DistanceField = iso, FalloffDistance = 5 * cell_size
            };
            var offset_field = new ImplicitOffset3d()
            {
                A = skel_field, Offset = DistanceFieldToSkeletalField.ZeroIsocontour
            };


            MarchingCubesPro c = new MarchingCubesPro();

            //c.Implicit = iso;
            //c.Implicit = skel_field;
            //c.IsoValue = DistanceFieldToSkeletalField.ZeroIsocontour;
            c.Implicit = offset_field;

            c.Bounds   = mesh.CachedBounds;
            c.CubeSize = c.Bounds.MaxDim / 128;
            c.Bounds.Expand(3 * c.CubeSize);
            //c.RootMode = MarchingCubesPro.RootfindingModes.Bisection;
            c.ParallelCompute = false;

            c.Generate();
            //c.GenerateContinuation(mesh.Vertices());

            TestUtil.WriteTestOutputMesh(c.Mesh, "mcpro_output.obj");
        }
Example #11
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            Point3d pt = Point3d.Origin;

            int    ni       = 0;
            int    nj       = 0;
            int    nk       = 0;
            double cellSize = 0;

            DA.GetData(0, ref pt);
            DA.GetData(1, ref ni);
            DA.GetData(2, ref nj);
            DA.GetData(3, ref nk);
            DA.GetData(4, ref cellSize);

            DenseGrid3f grid = new DenseGrid3f(ni, nj, nk, 0);
            DenseGridTrilinearImplicit grd = new DenseGridTrilinearImplicit(grid, pt.ToVec3d(), cellSize);

            DA.SetData(0, grd);
        }
Example #12
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            int        num_cells = 128;
            DMesh3_goo dMsh_goo  = null;

            DA.GetData(0, ref dMsh_goo);
            DA.GetData(1, ref num_cells);

            DMesh3 dMsh_copy = new DMesh3(dMsh_goo.Value);
            double cell_size = dMsh_copy.CachedBounds.MaxDim / num_cells;

            MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(dMsh_copy, cell_size);

            sdf.Compute();

            var        iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);
            Grid3f_goo goo = iso;

            DA.SetData(0, goo);
        }
        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;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// compute SDF for the scan object, and then compute offset iso-contours
        /// </summary>
        void compute_offset_meshes()
        {
            int sdf_cells  = 128;
            int mesh_cells = 128;

            double max_offset = inner_offset + thickness;

            if (max_offset > cached_sdf_max_offset)
            {
                DMesh3 meshIn = new DMesh3(MeshSource.GetIMesh(), MeshHints.IsCompact, MeshComponents.None);
                MeshTransforms.FromFrame(meshIn, cachedInputsTransform);

                // [RMS] reduce this mesh? speeds up SDF quite a bit...
                Reducer r = new Reducer(meshIn);
                r.ReduceToTriangleCount(2500);

                double cell_size           = meshIn.CachedBounds.MaxDim / sdf_cells;
                int    exact_cells         = (int)((max_offset) / cell_size) + 1;
                MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, cell_size)
                {
                    ExactBandWidth = exact_cells
                };
                sdf.Compute();
                cached_sdf            = sdf;
                cached_sdf_max_offset = max_offset;
                cached_sdf_bounds     = meshIn.CachedBounds;

                cached_inner_sdf_offset = 0;
                cached_outer_sdf_offset = 0;
            }

            if (cached_inner_sdf_offset != inner_offset || cached_outer_sdf_offset != max_offset)
            {
                var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize);

                MarchingCubes c = new MarchingCubes()
                {
                    Implicit = iso
                };
                c.Bounds   = cached_sdf_bounds;
                c.CubeSize = c.Bounds.MaxDim / mesh_cells;
                c.Bounds.Expand(max_offset + 3 * c.CubeSize);

                if (cached_inner_sdf_offset != inner_offset)
                {
                    c.IsoValue = inner_offset;
                    c.Generate();
                    InnerOffsetMesh = c.Mesh;
                    Reducer reducer = new Reducer(InnerOffsetMesh);
                    reducer.ReduceToEdgeLength(c.CubeSize / 2);
                    InnerOffsetMeshSpatial  = new DMeshAABBTree3(InnerOffsetMesh, true);
                    cached_inner_sdf_offset = inner_offset;
                }

                if (cached_outer_sdf_offset != max_offset)
                {
                    c.IsoValue = inner_offset + thickness;
                    c.Generate();
                    OuterOffsetMesh = c.Mesh;
                    Reducer reducer = new Reducer(OuterOffsetMesh);
                    reducer.ReduceToEdgeLength(c.CubeSize / 2);
                    OuterOffsetMeshSpatial  = new DMeshAABBTree3(OuterOffsetMesh, true);
                    cached_outer_sdf_offset = max_offset;
                }
            }

            //Util.WriteDebugMesh(MeshSource.GetIMesh(), "c:\\scratch\\__OFFESTS_orig.obj");
            //Util.WriteDebugMesh(InnerOffsetMesh, "c:\\scratch\\__OFFESTS_inner.obj");
            //Util.WriteDebugMesh(OuterOffsetMesh, "c:\\scratch\\__OFFESTS_outer.obj");
        }
 public void Initialize(MeshWindingNumberGrid mwnGrid)
 {
     WindingGrid     = new DenseGridTrilinearImplicit(mwnGrid.Grid, mwnGrid.GridOrigin, mwnGrid.CellSize);
     WindingIsoValue = mwnGrid.WindingIsoValue;
 }
        protected virtual DMesh3 update_step_2(DMesh3 meshIn)
        {
            double unsigned_offset = Math.Abs(distance);
            int    exact_cells     = (int)(unsigned_offset / grid_cell_size) + 1;

            // only use spatial DS if we are computing enough cells
            bool compute_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial(
                input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z) != null;
            DMeshAABBTree3         use_spatial = (compute_spatial) ? new DMeshAABBTree3(meshIn, true) : null;
            MeshSignedDistanceGrid sdf         = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial)
            {
                ExactBandWidth = exact_cells
            };

            if (use_spatial != null)
            {
                sdf.NarrowBandMaxDistance = unsigned_offset + grid_cell_size;
                sdf.ComputeMode           = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill;
            }

            sdf.CancelF = is_invalidated;
            sdf.Compute();
            if (is_invalidated())
            {
                return(null);
            }

            var           iso = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize);
            MarchingCubes c   = new MarchingCubes();

            c.Implicit = iso;

            if (op_type == OperationTypes.Close)
            {
                c.IsoValue = -distance;
            }
            else
            {
                c.IsoValue = distance;
            }

            c.Bounds   = cached_sdf_bounds;
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(distance + 3 * c.CubeSize);
            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;

            c.CancelF = is_invalidated;
            c.Generate();
            if (is_invalidated())
            {
                return(null);
            }

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize * 0.5, 3, true);
            if (is_invalidated())
            {
                return(null);
            }

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return(null);
            }

            return(c.Mesh);
        }
        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");
        }
        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 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");
        }
        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);
                }
            });
        }
        public static void test_marching_cubes_topology()
        {
            AxisAlignedBox3d bounds = new AxisAlignedBox3d(1.0);
            int    numcells         = 64;
            double cellsize         = bounds.MaxDim / numcells;

            Random r = new Random(31337);

            for (int ii = 0; ii < 100; ++ii)
            {
                DenseGrid3f grid = new DenseGrid3f();
                grid.resize(numcells, numcells, numcells);
                grid.assign(1);
                for (int k = 2; k < numcells - 3; k++)
                {
                    for (int j = 2; j < numcells - 3; j++)
                    {
                        for (int i = 2; i < numcells - 3; i++)
                        {
                            double d = r.NextDouble();
                            if (d > 0.9)
                            {
                                grid[i, j, k] = 0.0f;
                            }
                            else if (d > 0.5)
                            {
                                grid[i, j, k] = 1.0f;
                            }
                            else
                            {
                                grid[i, j, k] = -1.0f;
                            }
                        }
                    }
                }

                var iso = new DenseGridTrilinearImplicit(grid, Vector3f.Zero, cellsize);

                MarchingCubes c = new MarchingCubes();
                c.Implicit = iso;
                c.Bounds   = bounds;
                //c.Bounds.Max += 3 * cellsize * Vector3d.One;
                //c.Bounds.Expand(2*cellsize);

                // this produces holes
                c.CubeSize = cellsize * 4.1;
                //c.CubeSize = cellsize * 2;

                //c.Bounds = new AxisAlignedBox3d(2.0);

                c.Generate();

                for (float f = 2.0f; f < 8.0f; f += 0.13107f)
                {
                    c.CubeSize = cellsize * 4.1;
                    c.Generate();
                    c.Mesh.CheckValidity(false);

                    MeshBoundaryLoops loops = new MeshBoundaryLoops(c.Mesh);
                    if (loops.Count > 0)
                    {
                        throw new Exception("found loops!");
                    }
                }
            }

            //c.Mesh.CheckValidity(false);


            //TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_topotest.obj");
        }
        public static void test_marching_cubes_implicits()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");

            MeshTransforms.Translate(mesh, -mesh.CachedBounds.Center);
            double meshCellsize             = mesh.CachedBounds.MaxDim / 32;
            MeshSignedDistanceGrid levelSet = new MeshSignedDistanceGrid(mesh, meshCellsize);

            levelSet.ExactBandWidth = 3;
            levelSet.UseParallel    = true;
            levelSet.ComputeMode    = MeshSignedDistanceGrid.ComputeModes.NarrowBandOnly;
            levelSet.Compute();
            var meshIso = new DenseGridTrilinearImplicit(levelSet.Grid, levelSet.GridOrigin, levelSet.CellSize);


            ImplicitOffset3d offsetMeshIso = new ImplicitOffset3d()
            {
                A = meshIso, Offset = 2.0
            };

            double           r       = 15.0;
            ImplicitSphere3d sphere1 = new ImplicitSphere3d()
            {
                Origin = Vector3d.Zero,
                Radius = r
            };
            ImplicitSphere3d sphere2 = new ImplicitSphere3d()
            {
                Origin = r * Vector3d.AxisX,
                Radius = r
            };
            ImplicitAxisAlignedBox3d aabox1 = new ImplicitAxisAlignedBox3d()
            {
                AABox = new AxisAlignedBox3d(r * 0.5 * Vector3d.One, r, r * 0.75, r * 0.5)
            };
            ImplicitBox3d box1 = new ImplicitBox3d()
            {
                Box = new Box3d(new Frame3f(r * 0.5 * Vector3d.One, Vector3d.One.Normalized),
                                new Vector3d(r, r * 0.75, r * 0.5))
            };
            ImplicitLine3d line1 = new ImplicitLine3d()
            {
                Segment = new Segment3d(Vector3d.Zero, r * Vector3d.One),
                Radius  = 3.0
            };
            ImplicitHalfSpace3d half1 = new ImplicitHalfSpace3d()
            {
                Origin = Vector3d.Zero, Normal = Vector3d.One.Normalized
            };

            ImplicitUnion3d union = new ImplicitUnion3d()
            {
                A = sphere1, B = line1
            };
            ImplicitDifference3d difference = new ImplicitDifference3d()
            {
                A = meshIso, B = aabox1
            };
            ImplicitIntersection3d intersect = new ImplicitIntersection3d()
            {
                A = meshIso, B = half1
            };
            ImplicitNaryUnion3d nunion = new ImplicitNaryUnion3d()
            {
                Children = new List <BoundedImplicitFunction3d>()
                {
                    offsetMeshIso, sphere1, sphere2
                }
            };
            ImplicitNaryDifference3d ndifference = new ImplicitNaryDifference3d()
            {
                A    = offsetMeshIso,
                BSet = new List <BoundedImplicitFunction3d>()
                {
                    sphere1, sphere2
                }
            };
            ImplicitBlend3d blend = new ImplicitBlend3d()
            {
                A = sphere1, B = sphere2
            };

            BoundedImplicitFunction3d root = intersect;

            AxisAlignedBox3d bounds = root.Bounds();
            int           numcells  = 64;
            MarchingCubes c         = new MarchingCubes();

            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;
            c.Implicit      = root;
            c.Bounds        = bounds;
            c.CubeSize      = bounds.MaxDim / numcells;
            c.Bounds.Expand(3 * c.CubeSize);

            c.Generate();

            MeshNormals.QuickCompute(c.Mesh);
            TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_implicit.obj");
        }
Example #23
0
        protected virtual void compute_shell_distancefield()
        {
            if (cached_is_closed == false)
            {
                compute_shell_distancefield_unsigned();
                return;
            }

            double     offset_distance = shell_thickness;
            Interval1d shell_range     = new Interval1d(0, offset_distance);

            if (shell_direction == ShellDirections.Symmetric)
            {
                shell_range = new Interval1d(-offset_distance / 2, offset_distance / 2);
            }
            else if (shell_direction == ShellDirections.Inner)
            {
                shell_range     = new Interval1d(-offset_distance, 0);
                offset_distance = -offset_distance;
            }


            if (cached_sdf == null ||
                shell_thickness > cached_sdf_max_offset ||
                grid_cell_size != cached_sdf.CellSize)
            {
                DMesh3 meshIn      = MeshSource.GetDMeshUnsafe();
                int    exact_cells = (int)((shell_thickness) / grid_cell_size) + 1;

                // only use spatial DS if we are computing enough cells
                DMeshAABBTree3         use_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial(input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z);
                MeshSignedDistanceGrid sdf         = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial)
                {
                    ExactBandWidth = exact_cells
                };
                if (use_spatial != null)
                {
                    sdf.NarrowBandMaxDistance = shell_thickness + grid_cell_size;
                    sdf.ComputeMode           = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill;
                }

                sdf.CancelF = is_invalidated;
                sdf.Compute();
                if (is_invalidated())
                {
                    return;
                }
                cached_sdf            = sdf;
                cached_sdf_max_offset = shell_thickness;
                cached_sdf_bounds     = meshIn.CachedBounds;
            }

            var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize);
            BoundedImplicitFunction3d shell_field = (shell_direction == ShellDirections.Symmetric) ?
                                                    (BoundedImplicitFunction3d) new ImplicitShell3d()
            {
                A = iso, Inside = shell_range
            } :
            (BoundedImplicitFunction3d) new ImplicitOffset3d()
            {
                A = iso, Offset = offset_distance
            };
            //var shell_field = new ImplicitShell3d() { A = iso, Inside = shell_range };
            //BoundedImplicitFunction3d shell_field = (signed_field) ?
            //    (BoundedImplicitFunction3d)new ImplicitShell3d() { A = iso, Inside = shell_range } :
            //    (BoundedImplicitFunction3d)new ImplicitOffset3d() { A = iso, Offset = offset_distance };
            //ImplicitOffset3d offset = new ImplicitOffset3d() { A = iso, Offset = offset_distance };

            MarchingCubes c = new MarchingCubes();

            c.Implicit = shell_field;
            c.IsoValue = 0;
            c.Bounds   = cached_sdf_bounds;
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(offset_distance + 3 * c.CubeSize);
            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;

            c.CancelF = is_invalidated;
            c.Generate();
            if (is_invalidated())
            {
                return;
            }

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize * 0.5, 3, true);
            if (is_invalidated())
            {
                return;
            }

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return;
            }

            if (shell_surface_only)
            {
                if (shell_direction == ShellDirections.Inner || shell_direction == ShellDirections.Outer)
                {
                    c.Mesh.AttachMetadata("is_partial", new object());
                }
            }

            ResultMesh = c.Mesh;
        }
Example #24
0
        protected virtual void compute_shell_distancefield_unsigned()
        {
            double offset_distance = shell_thickness * 0.5;

            if (cached_sdf == null ||
                offset_distance > cached_sdf_max_offset ||
                grid_cell_size != cached_sdf.CellSize)
            {
                DMesh3 meshIn              = MeshSource.GetDMeshUnsafe();
                int    exact_cells         = (int)((offset_distance) / grid_cell_size) + 1;
                MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, grid_cell_size)
                {
                    ExactBandWidth = exact_cells,
                    ComputeSigns   = false
                };
                sdf.CancelF = is_invalidated;
                sdf.Compute();
                if (is_invalidated())
                {
                    return;
                }
                cached_sdf            = sdf;
                cached_sdf_max_offset = offset_distance;
                cached_sdf_bounds     = meshIn.CachedBounds;
            }

            var           iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize);
            MarchingCubes c   = new MarchingCubes();

            c.Implicit = iso;
            c.IsoValue = offset_distance;
            c.Bounds   = cached_sdf_bounds;
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(offset_distance + 3 * c.CubeSize);
            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;

            c.CancelF = is_invalidated;
            c.Generate();
            if (is_invalidated())
            {
                return;
            }

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize * 0.5, 3, true);
            if (is_invalidated())
            {
                return;
            }

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return;
            }

            ResultMesh = c.Mesh;
        }
        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);
        }
Example #26
0
        protected virtual void update_level_set()
        {
            double unsigned_offset = Math.Abs(offset_distance);

            if (cached_sdf == null ||
                unsigned_offset > cached_sdf_max_offset ||
                grid_cell_size != cached_sdf.CellSize)
            {
                DMesh3 meshIn      = MeshSource.GetDMeshUnsafe();
                int    exact_cells = (int)(unsigned_offset / grid_cell_size) + 1;

                // only use spatial DS if we are computing enough cells
                DMeshAABBTree3 use_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial(
                    input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z);
                MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial)
                {
                    ExactBandWidth = exact_cells
                };
                if (use_spatial != null)
                {
                    sdf.NarrowBandMaxDistance = unsigned_offset + grid_cell_size;
                    sdf.ComputeMode           = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill;
                }

                sdf.CancelF = is_invalidated;
                sdf.Compute();
                if (is_invalidated())
                {
                    return;
                }
                cached_sdf            = sdf;
                cached_sdf_max_offset = unsigned_offset;
                cached_sdf_bounds     = meshIn.CachedBounds;
            }

            var           iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize);
            MarchingCubes c   = new MarchingCubes();

            c.Implicit = iso;
            c.IsoValue = offset_distance;
            c.Bounds   = cached_sdf_bounds;
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(offset_distance + 3 * c.CubeSize);
            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;

            c.CancelF = is_invalidated;
            c.Generate();
            if (is_invalidated())
            {
                return;
            }

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize * 0.5, 3, true);
            if (is_invalidated())
            {
                return;
            }

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return;
            }

            ResultMesh = c.Mesh;
        }
Example #27
0
        protected virtual DMesh3 compute_blend_bounded()
        {
            bool          profile = true;
            LocalProfiler p       = null;

            if (profile)
            {
                p = new LocalProfiler();
                p.Start("sdf");
            }

            cache_input_sdfs_bounded();
            if (is_invalidated())
            {
                return(null);
            }

            if (profile)
            {
                p.Stop("sdf");
                p.Start("mc");
            }

            List <BoundedImplicitFunction3d> inputs = new List <BoundedImplicitFunction3d>();

            foreach (var sdf in cached_bounded_sdfs)
            {
                var dist_field = new DenseGridTrilinearImplicit(sdf);
                var skel_field = new DistanceFieldToSkeletalField()
                {
                    DistanceField = dist_field, FalloffDistance = blend_falloff
                };
                inputs.Add(skel_field);
            }

            SkeletalRicciNaryBlend3d blend = new SkeletalRicciNaryBlend3d()
            {
                Children = inputs, BlendPower = this.blend_power
            };

            MarchingCubesPro c = new MarchingCubesPro();

            c.Implicit = blend;
            c.IsoValue = DistanceFieldToSkeletalField.ZeroIsocontour;
            c.Bounds   = blend.Bounds();
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(3 * c.CubeSize);
            c.RootMode      = MarchingCubesPro.RootfindingModes.LerpSteps;
            c.RootModeSteps = 3;

            c.CancelF = is_invalidated;
            //c.Generate();
            c.GenerateContinuation(input_mesh_seeds());
            if (is_invalidated())
            {
                return(null);
            }

            if (profile)
            {
                p.Stop("mc");
                p.Start("reduce");
            }

            c.Mesh.ReverseOrientation();

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize / 2, 3, true);
            if (is_invalidated())
            {
                return(null);
            }

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return(null);
            }

            if (profile)
            {
                p.Stop("reduce");
#if G3_USING_UNITY
                UnityEngine.Debug.Log("BLEND TIME: " + p.AllTimes());
#endif
            }

            return(c.Mesh);
        }
Example #28
0
 public void Initialize(MeshScalarSamplingGrid scalarGrid)
 {
     WindingGrid = new DenseGridTrilinearImplicit(scalarGrid.Grid, scalarGrid.GridOrigin, scalarGrid.CellSize);
     IsoValue    = scalarGrid.IsoValue;
 }
Example #29
0
        protected virtual void compute_hollow()
        {
            double offset_distance = -wall_thickness;

            if (cached_sdf == null ||
                wall_thickness > cached_sdf_max_offset ||
                grid_cell_size != cached_sdf.CellSize)
            {
                DMesh3 meshIn      = MeshSource.GetDMeshUnsafe();
                int    exact_cells = (int)((wall_thickness) / grid_cell_size) + 1;

                // only use spatial DS if we are computing enough cells
                DMeshAABBTree3         use_spatial = GenerateClosedMeshOp.MeshSDFShouldUseSpatial(input_spatial, exact_cells, grid_cell_size, input_mesh_edge_stats.z);
                MeshSignedDistanceGrid sdf         = new MeshSignedDistanceGrid(meshIn, grid_cell_size, use_spatial)
                {
                    ExactBandWidth = exact_cells
                };
                if (use_spatial != null)
                {
                    sdf.NarrowBandMaxDistance = wall_thickness + grid_cell_size;
                    sdf.ComputeMode           = MeshSignedDistanceGrid.ComputeModes.NarrowBand_SpatialFloodFill;
                }

                sdf.CancelF = is_invalidated;
                sdf.Compute();
                if (is_invalidated())
                {
                    return;
                }
                cached_sdf            = sdf;
                cached_sdf_max_offset = wall_thickness;
                cached_sdf_bounds     = meshIn.CachedBounds;
            }

            var iso = new DenseGridTrilinearImplicit(cached_sdf.Grid, cached_sdf.GridOrigin, cached_sdf.CellSize);
            ImplicitOffset3d shell_field = new ImplicitOffset3d()
            {
                A = iso, Offset = offset_distance
            };
            ImplicitFunction3d use_iso = shell_field;


            if (enable_infill)
            {
                GridDistanceField grid_df = new GridDistanceField()
                {
                    CellSize = infill_spacing,
                    Radius   = infill_thickness * 0.5,
                    Origin   = cached_sdf.GridOrigin
                };
                ImplicitDifference3d diff = new ImplicitDifference3d()
                {
                    A = shell_field, B = grid_df
                };
                use_iso = diff;
            }


            MarchingCubes c = new MarchingCubes();

            c.Implicit = use_iso;
            c.IsoValue = 0;
            c.Bounds   = cached_sdf_bounds;
            c.CubeSize = mesh_cell_size;
            c.Bounds.Expand(offset_distance + 3 * c.CubeSize);
            c.RootMode      = MarchingCubes.RootfindingModes.LerpSteps;
            c.RootModeSteps = 5;

            c.CancelF = is_invalidated;
            c.Generate();
            if (is_invalidated())
            {
                return;
            }

            Reducer r = new Reducer(c.Mesh);

            r.FastCollapsePass(c.CubeSize * 0.5, 3, true);
            if (is_invalidated())
            {
                return;
            }

            //r.ReduceToTriangleCount(c.Mesh.TriangleCount / 5);
            //if (is_invalidated())
            //    return;

            if (min_component_volume > 0)
            {
                MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume);
            }
            if (is_invalidated())
            {
                return;
            }

            c.Mesh.AttachMetadata("is_partial", new object());

            ResultMesh = c.Mesh;
        }