Exemple #1
0
        public static void test_planar_fill()
        {
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_hollow.obj");
            DMesh3 mesh = TestUtil.LoadTestInputMesh("local\\__LAST_FAILED_CUT_MESH.obj");
            double mine, maxe, avge;

            MeshQueries.EdgeLengthStats(mesh, out mine, out maxe, out avge);

            AxisAlignedBox3d bounds    = mesh.CachedBounds;
            Vector3d         origin    = bounds.Center;
            Vector3d         axis      = Vector3d.AxisY;
            double           targetLen = avge;

            origin    = new Vector3d(-7.53203300, -39.50826000, -4.22260500);
            axis      = new Vector3d(0.00000007, 0.00000007, -0.99999960);
            targetLen = 3.0;

            MeshPlaneCut cut = new MeshPlaneCut(mesh, origin, axis);

            cut.Cut();

            PlanarHoleFiller fill = new PlanarHoleFiller(cut)
            {
                FillTargetEdgeLen = targetLen
            };

            if (fill.Fill() == false)
            {
                System.Console.WriteLine("test_meshEdits.test_planar_fill: Fill() failed!");
            }

            TestUtil.WriteTestOutputMesh(mesh, "planar_filled.obj");
        }
Exemple #2
0
    InteractiveRemesher make_remesher(DMesh3 mesh)
    {
        var m = new InteractiveRemesher(mesh);

        m.PreventNormalFlips = true;

        double mine, maxe, avge;

        MeshQueries.EdgeLengthStats(mesh, out mine, out avge, out maxe);
        m.SetTargetEdgeLength(avge * EdgeLengthMultiplier);

        m.SmoothSpeedT = SmoothSpeed;

        if (Reproject)
        {
            m.SetProjectionTarget(MeshProjectionTarget.Auto(mesh));
        }

        if (RemeshBoundary)
        {
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
            int k = 1;
            foreach (var loop in loops)
            {
                MeshConstraintUtil.ConstrainVtxLoopTo(m, loop.Vertices, new DCurveProjectionTarget(loop.ToCurve()), k++);
            }
        }
        else if (PreserveBoundary)
        {
            MeshConstraintUtil.FixAllBoundaryEdges(m);
        }

        return(m);
    }
        public static void test_basic_closed_reduce()
        {
            //DMesh3 mesh = TestUtil.MakeCappedCylinder(false);
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("sphere_bowtie_groups.obj");
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");

            //MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1,2,1));
            //DMesh3 mesh = TestUtil.MakeOpenCylinder(false);
            mesh.CheckValidity();

            if (WriteDebugMeshes)
            {
                TestUtil.WriteTestOutputMesh(mesh, "basic_closed_reduce_before.obj");
            }

            Reducer r = new Reducer(mesh);

            DMeshAABBTree3 tree = new DMeshAABBTree3(new DMesh3(mesh));

            tree.Build();
            //r.SetProjectionTarget(new MeshProjectionTarget() { Mesh = tree.Mesh, Spatial = tree });

            r.ReduceToTriangleCount(3000);
            //r.ReduceToEdgeLength(2.0);

            double mine, maxe, avge;

            MeshQueries.EdgeLengthStats(mesh, out mine, out maxe, out avge);
            System.Console.WriteLine("Edge length stats: {0} {1} {2}", mine, maxe, avge);

            if (WriteDebugMeshes)
            {
                TestUtil.WriteTestOutputMesh(mesh, "basic_closed_reduce_after.obj");
            }
        }
Exemple #4
0
        virtual public void CutMesh()
        {
            Frame3f frameL = SceneTransforms.SceneToObject(target, cut_plane);

            Action <DMesh3> editF = (mesh) => {
                MeshPlaneCut cut = new MeshPlaneCut(mesh, frameL.Origin, frameL.Y);
                cut.Cut();

                PlaneProjectionTarget planeTarget = new PlaneProjectionTarget()
                {
                    Origin = frameL.Origin, Normal = frameL.Y
                };

                if (GenerateFillSurface)
                {
                    double min, max, avg;
                    MeshQueries.EdgeLengthStats(mesh, out min, out max, out avg);

                    cut.FillHoles();

                    MeshFaceSelection selection = new MeshFaceSelection(mesh);
                    foreach (var tris in cut.LoopFillTriangles)
                    {
                        selection.Select(tris);
                    }
                    RegionRemesher.QuickRemesh(mesh, selection.ToArray(), 2 * avg, 1.0f, 25, planeTarget);

                    MeshNormals normals = new MeshNormals(mesh);
                    normals.Compute();
                    normals.CopyTo(mesh);
                }
            };

            target.EditAndUpdateMesh(editF, GeometryEditTypes.ArbitraryEdit);
        }
Exemple #5
0
        DMesh3 GenerateRemesh(DMesh3 mesh)
        {
            DMesh3 remeshed = new DMesh3(mesh);

            DMeshAABBTree3 project = new DMeshAABBTree3(mesh);

            project.Build();
            MeshProjectionTarget Target = new MeshProjectionTarget(project.Mesh, project);

            double minlen, maxlen, avglen;

            MeshQueries.EdgeLengthStats(mesh, out minlen, out maxlen, out avglen);
            double edge_len = (TargetEdgeLength == 0) ? Loop.AverageEdgeLength : avglen;

            Remesher r = new Remesher(remeshed);

            r.SetTargetEdgeLength(edge_len);
            r.SetProjectionTarget(Target);
            MeshConstraintUtil.FixAllBoundaryEdges(r);

            for (int k = 0; k < 20; ++k)
            {
                r.BasicRemeshPass();
            }

            return(remeshed);
        }
Exemple #6
0
        public static void test_auto_fill()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_cases.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_tempcase1.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_planarcase.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_box_edge_strip.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_box_edge_strip_2.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("crazyhole.obj");

            double mine, maxe, avge;

            MeshQueries.EdgeLengthStats(mesh, out mine, out maxe, out avge);

            int ROUNDS = 1;

            for (int k = 0; k < ROUNDS; ++k)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
                foreach (EdgeLoop loop in loops)
                {
                    AutoHoleFill fill = new AutoHoleFill(mesh, loop);
                    fill.TargetEdgeLength = avge;
                    fill.Apply();
                }
                if (k == ROUNDS - 1)
                {
                    continue;
                }

                RemesherPro r = new RemesherPro(mesh);
                r.SetTargetEdgeLength(avge);
                for (int j = 0; j < 10; ++j)
                {
                    r.FastSplitIteration();
                }

                MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh);
                merge.Apply();
            }

            TestUtil.WriteTestOutputMesh(mesh, "autofill_result.obj");

            //DMesh3 mesh = new DMesh3();
            //SphericalFibonacciPointSet ps = new SphericalFibonacciPointSet();
            //for (int k = 0; k < ps.N; ++k) {
            //    MeshEditor.AppendBox(mesh, ps[k], ps[k], 0.05f);
            //}
            //Random r = new Random(31337);
            //Vector3d[] pts = TestUtil.RandomPoints3(10000, r, Vector3d.Zero);
            //foreach ( Vector3d pt in pts ) {
            //    pt.Normalize();
            //    int nearest = ps.NearestPoint(pt, true);
            //    Vector3d p = ps[nearest];
            //    MeshEditor.AppendLine(mesh, new Segment3d(p, p + 0.25f * pt), 0.01f);
            //}
            //TestUtil.WriteTestOutputMesh(mesh, "fibonacci.obj");
        }
Exemple #7
0
        public virtual void Update()
        {
            base.begin_update();
            int start_timestamp = this.CurrentInputTimestamp;

            if (MeshSource == null)
            {
                throw new Exception("GenerateClosedMeshOp: must set valid MeshSource to compute!");
            }

            try {
                ResultMesh = null;

                DMesh3 meshIn = MeshSource.GetDMeshUnsafe();
                input_spatial = MeshSource.GetSpatial() as DMeshAABBTree3;
                if (meshIn.ShapeTimestamp != input_mesh_cache_timestamp)
                {
                    cached_is_closed = meshIn.IsClosed();
                    MeshQueries.EdgeLengthStats(meshIn, out input_mesh_edge_stats.x, out input_mesh_edge_stats.y, out input_mesh_edge_stats.z);
                    if (input_spatial == null)
                    {
                        input_spatial = new DMeshAABBTree3(meshIn, false);
                    }
                    input_mesh_cache_timestamp = meshIn.ShapeTimestamp;
                }

                if (closing_type == ClosingTypes.LevelSet)
                {
                    update_level_set();
                }
                else if (closing_type == ClosingTypes.WindingNumberGrid)
                {
                    if (cached_is_closed)
                    {
                        update_winding();
                    }
                    else
                    {
                        update_winding_fast();
                    }
                }
                else
                {
                    update_winding_exact();
                }

                base.complete_update();
            } catch (Exception e) {
                PostOnOperatorException(e);
                ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe());
                base.complete_update();
            }
        }
Exemple #8
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            DMesh3_goo goo = null;

            DA.GetData(0, ref goo);

            DMesh3 mesh = new DMesh3(goo.Value);

            MeshQueries.EdgeLengthStats(mesh, out double min, out double max, out double avg);

            DA.SetData(0, max);
            DA.SetData(1, min);
            DA.SetData(2, avg);
        }
Exemple #9
0
    void remove_old_vertices(int[] MapV, DMesh3 mesh)
    {
        HashSet <int> keepV = new HashSet <int>();

        for (int k = 0; k < MapV.Length; ++k)
        {
            if (MapV[k] != DMesh3.InvalidID)
            {
                keepV.Add(MapV[k]);
            }
        }

        Remesher r = new Remesher(mesh);

        //r.EnableCollapses = false;
        //r.EnableSplits = false;
        //r.EnableFlips = false;
        r.SmoothSpeedT = 1.0;
        //r.EnableSmoothing = false;
        r.PreventNormalFlips = true;
        r.SetTargetEdgeLength(1.0);
        //r.EnableSmoothing = false;
        MeshConstraints c = new MeshConstraints();

        foreach (int vid in keepV)
        {
            c.SetOrUpdateVertexConstraint(vid, VertexConstraint.Pinned);
        }
        r.SetExternalConstraints(c);

        double minE, maxE, avgE;

        MeshQueries.EdgeLengthStats(mesh, out minE, out maxE, out avgE);
        r.SetTargetEdgeLength(avgE * .3);

        for (int k = 0; k < 10; ++k)
        {
            r.BasicRemeshPass();
        }

        //int iter = 0;
        //while (iter++ < 10) {
        //    r.SetTargetEdgeLength(iter * 1.0);
        //    for (int k = 0; k < 10; ++k)
        //        r.BasicRemeshPass();
        //}
    }
Exemple #10
0
        public virtual void Update()
        {
            base.begin_update();
            int start_timestamp = this.CurrentInputTimestamp;

            if (MeshSource == null)
            {
                throw new Exception("MeshShellOp: must set valid MeshSource to compute!");
            }

            try {
                ResultMesh = null;

                DMesh3 meshIn = MeshSource.GetDMeshUnsafe();
                input_spatial = MeshSource.GetSpatial() as DMeshAABBTree3;
                if (meshIn.ShapeTimestamp != input_mesh_cache_timestamp)
                {
                    cached_is_closed = meshIn.IsClosed();
                    MeshQueries.EdgeLengthStats(meshIn, out input_mesh_edge_stats.x, out input_mesh_edge_stats.y, out input_mesh_edge_stats.z);
                    if (input_spatial == null)
                    {
                        input_spatial = new DMeshAABBTree3(meshIn, false);
                    }
                    input_mesh_cache_timestamp = meshIn.ShapeTimestamp;
                }

                if (shell_type == ShellTypes.DistanceField)
                {
                    compute_shell_distancefield();
                }
                else
                {
                    compute_shell_extrude();
                }

                if (ResultMesh.TriangleCount == 0)
                {
                    ResultMesh = base.make_failure_output(null);
                }

                base.complete_update();
            } catch (Exception e) {
                PostOnOperatorException(e);
                ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe());
                base.complete_update();
            }
        }
Exemple #11
0
        public virtual void Update()
        {
            base.begin_update();
            int start_timestamp = this.CurrentInputTimestamp;

            if (mesh_sources == null)
            {
                throw new Exception("MeshVoxelBooleanOp: must set valid MeshSource to compute!");
            }

            try {
                ResultMesh = null;

                if (source_edge_stats == null)
                {
                    source_edge_stats = new List <Vector3d>();
                    source_bounds     = AxisAlignedBox3d.Empty;
                    foreach (DMeshSourceOp op in mesh_sources)
                    {
                        Vector3d einfo = new Vector3d();
                        MeshQueries.EdgeLengthStats(op.GetDMeshUnsafe(), out einfo.x, out einfo.y, out einfo.z);
                        source_edge_stats.Add(einfo);
                        source_bounds.Contain(op.GetDMeshUnsafe().CachedBounds);
                    }
                }

                //ResultMesh = compute_blend();
                //ResultMesh = compute_blend_bounded();
                ResultMesh = compute_blend_analytic();

                if (ResultMesh.TriangleCount == 0)
                {
                    ResultMesh = base.make_failure_output(null);
                }

                base.complete_update();
            } catch (Exception e) {
                PostOnOperatorException(e);
                ResultMesh = base.make_failure_output(mesh_sources[0].GetDMeshUnsafe());
                base.complete_update();
            }
        }
Exemple #12
0
        public static void test_smooth_fill()
        {
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("n_holed_bunny.obj");
            DMesh3 mesh = TestUtil.LoadTestInputMesh("crazyhole.obj");
            double mine, maxe, avge;

            MeshQueries.EdgeLengthStats(mesh, out mine, out maxe, out avge);

            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);

            foreach (EdgeLoop loop in loops)
            {
                SmoothedHoleFill fill = new SmoothedHoleFill(mesh, loop);
                fill.TargetEdgeLength        = avge;
                fill.SmoothAlpha             = 1.0f;
                fill.InitialRemeshPasses     = 50;
                fill.EnableLaplacianSmooth   = true;
                fill.ConstrainToHoleInterior = true;
                fill.Apply();
            }

            TestUtil.WriteTestOutputMesh(mesh, "smooth_fill_result.obj");
        }
Exemple #13
0
        public bool Apply()
        {
            // do a simple fill
            SimpleHoleFiller simplefill = new SimpleHoleFiller(Mesh, FillLoop);
            int  fill_gid = Mesh.AllocateTriangleGroup();
            bool bOK      = simplefill.Fill(fill_gid);

            if (bOK == false)
            {
                return(false);
            }

            if (FillLoop.Vertices.Length <= 3)
            {
                FillTriangles = simplefill.NewTriangles;
                FillVertices  = new int[0];
                return(true);
            }

            // extract the simple fill mesh as a submesh, via RegionOperator, so we can backsub later
            HashSet <int> intial_fill_tris = new HashSet <int>(simplefill.NewTriangles);

            regionop = new RegionOperator(Mesh, simplefill.NewTriangles,
                                          (submesh) => { submesh.ComputeTriMaps = true; });
            fillmesh = regionop.Region.SubMesh;

            // for each boundary vertex, compute the exterior angle sum
            // we will use this to compute gaussian curvature later
            boundaryv           = new HashSet <int>(MeshIterators.BoundaryEdgeVertices(fillmesh));
            exterior_angle_sums = new Dictionary <int, double>();
            if (IgnoreBoundaryTriangles == false)
            {
                foreach (int sub_vid in boundaryv)
                {
                    double angle_sum = 0;
                    int    base_vid  = regionop.Region.MapVertexToBaseMesh(sub_vid);
                    foreach (int tid in regionop.BaseMesh.VtxTrianglesItr(base_vid))
                    {
                        if (intial_fill_tris.Contains(tid) == false)
                        {
                            Index3i et  = regionop.BaseMesh.GetTriangle(tid);
                            int     idx = IndexUtil.find_tri_index(base_vid, ref et);
                            angle_sum += regionop.BaseMesh.GetTriInternalAngleR(tid, idx);
                        }
                    }
                    exterior_angle_sums[sub_vid] = angle_sum;
                }
            }


            // try to guess a reasonable edge length that will give us enough geometry to work with in simplify pass
            double loop_mine, loop_maxe, loop_avge, fill_mine, fill_maxe, fill_avge;

            MeshQueries.EdgeLengthStatsFromEdges(Mesh, FillLoop.Edges, out loop_mine, out loop_maxe, out loop_avge);
            MeshQueries.EdgeLengthStats(fillmesh, out fill_mine, out fill_maxe, out fill_avge);
            double remesh_target_len = loop_avge;

            if (fill_maxe / remesh_target_len > 10)
            {
                remesh_target_len = fill_maxe / 10;
            }
            //double remesh_target_len = Math.Min(loop_avge, fill_avge / 4);

            // remesh up to target edge length, ideally gives us some triangles to work with
            RemesherPro remesh1 = new RemesherPro(fillmesh);

            remesh1.SmoothSpeedT = 1.0;
            MeshConstraintUtil.FixAllBoundaryEdges(remesh1);
            //remesh1.SetTargetEdgeLength(remesh_target_len / 2);       // would this speed things up? on large regions?
            //remesh1.FastestRemesh();
            remesh1.SetTargetEdgeLength(remesh_target_len);
            remesh1.FastestRemesh();

            /*
             * first round: collapse to minimal mesh, while flipping to try to
             * get to ballpark minimal mesh. We stop these passes as soon as
             * we have done two rounds where we couldn't do another collapse
             *
             * This is the most unstable part of the algorithm because there
             * are strong ordering effects. maybe we could sort the edges somehow??
             */

            int zero_collapse_passes = 0;
            int collapse_passes      = 0;

            while (collapse_passes++ < 20 && zero_collapse_passes < 2)
            {
                // collapse pass
                int NE        = fillmesh.MaxEdgeID;
                int collapses = 0;
                for (int ei = 0; ei < NE; ++ei)
                {
                    if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }
                    Index2i ev = fillmesh.GetEdgeV(ei);
                    bool    a_bdry = boundaryv.Contains(ev.a), b_bdry = boundaryv.Contains(ev.b);
                    if (a_bdry && b_bdry)
                    {
                        continue;
                    }
                    int      keepv  = (a_bdry) ? ev.a : ev.b;
                    int      otherv = (keepv == ev.a) ? ev.b : ev.a;
                    Vector3d newv   = fillmesh.GetVertex(keepv);
                    if (MeshUtil.CheckIfCollapseCreatesFlip(fillmesh, ei, newv))
                    {
                        continue;
                    }
                    DMesh3.EdgeCollapseInfo info;
                    MeshResult result = fillmesh.CollapseEdge(keepv, otherv, out info);
                    if (result == MeshResult.Ok)
                    {
                        collapses++;
                    }
                }
                if (collapses == 0)
                {
                    zero_collapse_passes++;
                }
                else
                {
                    zero_collapse_passes = 0;
                }

                // flip pass. we flip in these cases:
                //  1) if angle between current triangles is too small (slightly more than 90 degrees, currently)
                //  2) if angle between flipped triangles is smaller than between current triangles
                //  3) if flipped edge length is shorter *and* such a flip won't flip the normal
                NE = fillmesh.MaxEdgeID;
                Vector3d n1, n2, on1, on2;
                for (int ei = 0; ei < NE; ++ei)
                {
                    if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }
                    bool do_flip = false;

                    Index2i ev = fillmesh.GetEdgeV(ei);
                    MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2);
                    double dot_cur  = n1.Dot(n2);
                    double dot_flip = on1.Dot(on2);
                    if (n1.Dot(n2) < 0.1 || dot_flip > dot_cur + MathUtil.Epsilonf)
                    {
                        do_flip = true;
                    }

                    if (do_flip == false)
                    {
                        Index2i otherv   = fillmesh.GetEdgeOpposingV(ei);
                        double  len_e    = fillmesh.GetVertex(ev.a).Distance(fillmesh.GetVertex(ev.b));
                        double  len_flip = fillmesh.GetVertex(otherv.a).Distance(fillmesh.GetVertex(otherv.b));
                        if (len_flip < len_e)
                        {
                            if (MeshUtil.CheckIfEdgeFlipCreatesFlip(fillmesh, ei) == false)
                            {
                                do_flip = true;
                            }
                        }
                    }

                    if (do_flip)
                    {
                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                    }
                }
            }

            // Sometimes, for some reason, we have a remaining interior vertex (have only ever seen one?)
            // Try to force removal of such vertices, even if it makes ugly mesh
            remove_remaining_interior_verts();


            // enable/disable passes.
            bool DO_FLATTER_PASS   = true;
            bool DO_CURVATURE_PASS = OptimizeDevelopability && true;
            bool DO_AREA_PASS      = OptimizeDevelopability && OptimizeTriangles && true;


            /*
             * In this pass we repeat the flipping iterations from the previous pass.
             *
             * Note that because of the always-flip-if-dot-is-small case (commented),
             * this pass will frequently not converge, as some number of edges will
             * be able to flip back and forth (because neither has large enough dot).
             * This is not ideal, but also, if we remove this behavior, then we
             * generally get worse fills. This case basically introduces a sort of
             * randomization factor that lets us escape local minima...
             *
             */

            HashSet <int> remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
            HashSet <int> updated_edges   = new HashSet <int>();

            int flatter_passes    = 0;
            int zero_flips_passes = 0;

            while (flatter_passes++ < 40 && zero_flips_passes < 2 && remaining_edges.Count() > 0 && DO_FLATTER_PASS)
            {
                zero_flips_passes++;
                foreach (int ei in remaining_edges)
                {
                    if (fillmesh.IsBoundaryEdge(ei))
                    {
                        continue;
                    }

                    bool do_flip = false;

                    Index2i  ev = fillmesh.GetEdgeV(ei);
                    Vector3d n1, n2, on1, on2;
                    MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2);
                    double dot_cur  = n1.Dot(n2);
                    double dot_flip = on1.Dot(on2);
                    if (flatter_passes < 20 && dot_cur < 0.1)   // this check causes oscillatory behavior
                    {
                        do_flip = true;
                    }
                    if (dot_flip > dot_cur + MathUtil.Epsilonf)
                    {
                        do_flip = true;
                    }

                    if (do_flip)
                    {
                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result == MeshResult.Ok)
                        {
                            zero_flips_passes = 0;
                            add_all_edges(ei, updated_edges);
                        }
                    }
                }

                var tmp = remaining_edges;
                remaining_edges = updated_edges;
                updated_edges   = tmp; updated_edges.Clear();
            }


            int curvature_passes = 0;

            if (DO_CURVATURE_PASS)
            {
                curvatures = new double[fillmesh.MaxVertexID];
                foreach (int vid in fillmesh.VertexIndices())
                {
                    update_curvature(vid);
                }

                remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
                updated_edges   = new HashSet <int>();

                /*
                 *  In this pass we try to minimize gaussian curvature at all the vertices.
                 *  This will recover sharp edges, etc, and do lots of good stuff.
                 *  However, this pass will not make much progress if we are not already
                 *  relatively close to a minimal mesh, so it really relies on the previous
                 *  passes getting us in the ballpark.
                 */
                while (curvature_passes++ < 40 && remaining_edges.Count() > 0 && DO_CURVATURE_PASS)
                {
                    foreach (int ei in remaining_edges)
                    {
                        if (fillmesh.IsBoundaryEdge(ei))
                        {
                            continue;
                        }

                        Index2i ev = fillmesh.GetEdgeV(ei);
                        Index2i ov = fillmesh.GetEdgeOpposingV(ei);

                        int find_other = fillmesh.FindEdge(ov.a, ov.b);
                        if (find_other != DMesh3.InvalidID)
                        {
                            continue;
                        }

                        double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b);
                        if (total_curv_cur < MathUtil.ZeroTolerancef)
                        {
                            continue;
                        }

                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result != MeshResult.Ok)
                        {
                            continue;
                        }

                        double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b);

                        bool keep_flip = total_curv_flip < total_curv_cur - MathUtil.ZeroTolerancef;
                        if (keep_flip == false)
                        {
                            result = fillmesh.FlipEdge(ei, out info);
                        }
                        else
                        {
                            update_curvature(ev.a); update_curvature(ev.b);
                            update_curvature(ov.a); update_curvature(ov.b);
                            add_all_edges(ei, updated_edges);
                        }
                    }
                    var tmp = remaining_edges;
                    remaining_edges = updated_edges;
                    updated_edges   = tmp; updated_edges.Clear();
                }
            }
            //System.Console.WriteLine("collapse {0}   flatter {1}   curvature {2}", collapse_passes, flatter_passes, curvature_passes);

            /*
             * In this final pass, we try to improve triangle quality. We flip if
             * the flipped triangles have better total aspect ratio, and the
             * curvature doesn't change **too** much. The .DevelopabilityTolerance
             * parameter determines what is "too much" curvature change.
             */
            if (DO_AREA_PASS)
            {
                remaining_edges = new HashSet <int>(fillmesh.EdgeIndices());
                updated_edges   = new HashSet <int>();
                int area_passes = 0;
                while (remaining_edges.Count() > 0 && area_passes < 20)
                {
                    area_passes++;
                    foreach (int ei in remaining_edges)
                    {
                        if (fillmesh.IsBoundaryEdge(ei))
                        {
                            continue;
                        }

                        Index2i ev = fillmesh.GetEdgeV(ei);
                        Index2i ov = fillmesh.GetEdgeOpposingV(ei);

                        int find_other = fillmesh.FindEdge(ov.a, ov.b);
                        if (find_other != DMesh3.InvalidID)
                        {
                            continue;
                        }

                        double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b);

                        double a = aspect_metric(ei);
                        if (a > 1)
                        {
                            continue;
                        }

                        DMesh3.EdgeFlipInfo info;
                        MeshResult          result = fillmesh.FlipEdge(ei, out info);
                        if (result != MeshResult.Ok)
                        {
                            continue;
                        }

                        double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b);

                        bool keep_flip = Math.Abs(total_curv_cur - total_curv_flip) < DevelopabilityTolerance;
                        if (keep_flip == false)
                        {
                            result = fillmesh.FlipEdge(ei, out info);
                        }
                        else
                        {
                            update_curvature(ev.a); update_curvature(ev.b);
                            update_curvature(ov.a); update_curvature(ov.b);
                            add_all_edges(ei, updated_edges);
                        }
                    }
                    var tmp = remaining_edges;
                    remaining_edges = updated_edges;
                    updated_edges   = tmp; updated_edges.Clear();
                }
            }


            regionop.BackPropropagate();
            FillTriangles = regionop.CurrentBaseTriangles;
            FillVertices  = regionop.CurrentBaseInteriorVertices().ToArray();

            return(true);
        }
Exemple #14
0
        public void Close_Flat()
        {
            double minlen, maxlen, avglen;

            MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000);
            double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen;

            // massage around boundary loop
            cleanup_boundary(Mesh, InitialBorderLoop, avglen, 3);

            // find new border loop
            // [TODO] this just assumes there is only one!!
            MeshBoundaryLoops loops     = new MeshBoundaryLoops(Mesh);
            EdgeLoop          fill_loop = loops.Loops[0];

            int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup;
            int fill_group    = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup;

            // decide on projection plane
            //AxisAlignedBox3d loopbox = fill_loop.GetBounds();
            //Vector3d topPt = loopbox.Center;
            //if ( bIsUpper ) {
            //    topPt.y = loopbox.Max.y + 0.25 * dims.y;
            //} else {
            //    topPt.y = loopbox.Min.y - 0.25 * dims.y;
            //}
            //Frame3f plane = new Frame3f((Vector3f)topPt);

            // extrude loop to this plane
            MeshExtrusion extrude = new MeshExtrusion(Mesh, fill_loop);

            extrude.PositionF = (v, n, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1));
            };
            extrude.Extrude(extrude_group);
            MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop);

            Debug.Assert(Mesh.CheckValidity());

            // smooth the extrude loop
            MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop);

            loop_smooth.ProjectF = (v, i) => {
                return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1));
            };
            loop_smooth.Alpha  = 0.5f;
            loop_smooth.Rounds = 100;
            loop_smooth.Smooth();

            Debug.Assert(Mesh.CheckValidity());

            // fill result
            SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop);

            filler.Fill(fill_group);

            Debug.Assert(Mesh.CheckValidity());

            // make selection for remesh region
            MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh);

            remesh_roi.Select(extrude.NewTriangles);
            remesh_roi.Select(filler.NewTriangles);
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.ExpandToOneRingNeighbours();
            remesh_roi.LocalOptimize(true, true);
            int[] new_roi = remesh_roi.ToArray();

            // get rid of extrude group
            FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0);

            /*  clean up via remesh
             *     - constrain loop we filled to itself
             */

            RegionRemesher r = new RegionRemesher(Mesh, new_roi);

            DCurve3 top_curve = mesh.MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices);
            DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve);

            int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone();
            r.Region.MapVerticesToSubmesh(top_loop);
            MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target);

            DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh);

            spatial.Build();
            MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial);

            r.SetProjectionTarget(target);

            bool bRemesh = true;

            if (bRemesh)
            {
                r.Precompute();
                r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
                r.MinEdgeLength   = target_edge_len;
                r.MaxEdgeLength   = 2 * target_edge_len;
                r.EnableSmoothing = true;
                r.SmoothSpeedT    = 1.0f;
                for (int k = 0; k < 40; ++k)
                {
                    r.BasicRemeshPass();
                }
                r.SetProjectionTarget(null);
                r.SmoothSpeedT = 0.25f;
                for (int k = 0; k < 10; ++k)
                {
                    r.BasicRemeshPass();
                }
                Debug.Assert(Mesh.CheckValidity());

                r.BackPropropagate();
            }

            // smooth around the join region to clean up ugliness
            smooth_region(Mesh, r.Region.BaseBorderV, 3);
        }
Exemple #15
0
        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            const ObjectType geometryFilter = ObjectType.Mesh;
            
            OptionDouble minEdgeLengthOption = new OptionDouble(minEdgeLength, 0.001, 200);
            OptionInteger maxTriangleCountOption = new OptionInteger(maxTriangleCount, 0, 100000000);
            
            GetObject go = new GetObject();
            go.SetCommandPrompt("Select meshes to reduce");
            go.GeometryFilter = geometryFilter;
            go.AddOptionDouble("MinEdge", ref minEdgeLengthOption);
            go.AddOptionInteger("MaxTriangle", ref maxTriangleCountOption);

            go.GroupSelect = true;
            go.SubObjectSelect = false;
            go.EnableClearObjectsOnEntry(false);
            go.EnableUnselectObjectsOnExit(false);
            go.DeselectAllBeforePostSelect = false;

            bool bHavePreselectedObjects = false;

            for (;;)
            {
                GetResult res = go.GetMultiple(1, 0);

                if (res == GetResult.Option)
                {
                    go.EnablePreSelect(false, true);
                    continue;
                }

                else if (go.CommandResult() != Result.Success)
                    return go.CommandResult();

                if (go.ObjectsWerePreselected)
                {
                    bHavePreselectedObjects = true;
                    go.EnablePreSelect(false, true);
                    continue;
                }

                break;
            }

            maxTriangleCount = maxTriangleCountOption.CurrentValue;
            minEdgeLength = minEdgeLengthOption.CurrentValue;

            if (bHavePreselectedObjects)
            {
                // Normally, pre-selected objects will remain selected, when a
                // command finishes, and post-selected objects will be unselected.
                // This this way of picking, it is possible to have a combination
                // of pre-selected and post-selected. So, to make sure everything
                // "looks the same", lets unselect everything before finishing
                // the command.
                for (int i = 0; i < go.ObjectCount; i++)
                {
                    RhinoObject rhinoObject = go.Object(i).Object();
                    if (null != rhinoObject)
                        rhinoObject.Select(false);
                }
                doc.Views.Redraw();
            }

            bool result = false;

            foreach (var obj in go.Objects())
            {
                var rhinoMesh = obj.Mesh();

                if (rhinoMesh == null || !rhinoMesh.IsValid)
                    continue;

                var mesh = GopherUtil.ConvertToD3Mesh(obj.Mesh());
                
                GopherUtil.ReduceMesh(mesh, (float)minEdgeLength, maxTriangleCount);

                double mine, maxe, avge;
                MeshQueries.EdgeLengthStats(mesh, out mine, out maxe, out avge);
                   RhinoApp.WriteLine("Edge length stats: {0} {1} {2}", mine, maxe, avge);

                var newRhinoMesh = GopherUtil.ConvertToRhinoMesh(mesh);

                if (newRhinoMesh != null && newRhinoMesh.IsValid)
                {
                    doc.Objects.Replace(obj, newRhinoMesh);
                }

                if (newRhinoMesh != null && newRhinoMesh.IsValid)
                {
                    result |= doc.Objects.Replace(obj, newRhinoMesh);
                }
            }

            doc.Views.Redraw();

            return Result.Success;
        }