Beispiel #1
0
        void fill_any_holes(out int nRemaining, out bool saw_spans)
        {
            MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh);

            nRemaining = 0;
            saw_spans  = loops.SawOpenSpans;

            foreach (var loop in loops)
            {
                if (Cancelled())
                {
                    break;
                }
                MinimalHoleFill filler = new MinimalHoleFill(Mesh, loop);
                bool            filled = filler.Apply();
                if (filled == false)
                {
                    if (Cancelled())
                    {
                        break;
                    }
                    SimpleHoleFiller fallback = new SimpleHoleFiller(Mesh, loop);
                    filled = fallback.Fill();
                }
            }
        }
Beispiel #2
0
        DMesh3 compute_partial_hole(Vector3d start, Vector3d end, double tol)
        {
            DMesh3         origMesh    = MeshSource.GetDMeshUnsafe();
            DMeshAABBTree3 origSpatial = MeshSource.GetSpatial() as DMeshAABBTree3;

            DMesh3 cutMesh = new DMesh3(origMesh);

            Polygon2d polygon = Polygon2d.MakeCircle(hole_size / 2, hole_subdivisions);

            Vector3f axis = (Vector3f)(start - end).Normalized;

            int     start_tid   = origSpatial.FindNearestTriangle(start);
            Frame3f start_frame = origMesh.GetTriFrame(start_tid);

            start_frame.Origin = (Vector3f)start;
            start_frame.AlignAxis(2, axis);

            int end_tid = origSpatial.FindNearestTriangle(end);
            //Frame3f end_frame = origMesh.GetTriFrame(end_tid); end_frame.Origin = (Vector3f)end;
            Frame3f end_frame = start_frame; end_frame.Origin = (Vector3f)end;

            // [TODO] we don't need to Simplify here...is more robust?

            MeshInsertProjectedPolygon start_insert = new MeshInsertProjectedPolygon(cutMesh, polygon, start_frame, start_tid);
            bool start_ok = start_insert.Insert();

            if (start_ok == false)
            {
                throw new Exception("CutPolygonHoleOp.compute_partial_hole: start or end insertion failed!");
            }

            EdgeLoop outLoop = start_insert.InsertedLoop;

            MeshExtrudeLoop extrude = new MeshExtrudeLoop(cutMesh, outLoop);

            extrude.PositionF = (v, n, vid) => {
                cutMesh.GetVertex(vid);
                return(end_frame.ProjectToPlane((Vector3f)v, 2));
            };
            extrude.Extrude();

            SimpleHoleFiller filler = new SimpleHoleFiller(cutMesh, extrude.NewLoop);

            filler.Fill();

            return(cutMesh);
        }
Beispiel #3
0
        public AddTrianglesMeshChange fill_trivial(DMesh3 mesh, EdgeLoop loop)
        {
            AddTrianglesMeshChange addChange = null;
            SimpleHoleFiller       filler    = new SimpleHoleFiller(mesh, loop);
            bool fill_ok = filler.Fill();

            if (fill_ok)
            {
                addChange = new AddTrianglesMeshChange();
                addChange.InitializeFromExisting(mesh,
                                                 new List <int>()
                {
                    filler.NewVertex
                },
                                                 filler.NewTriangles);
            }
            return(addChange);
        }
Beispiel #4
0
        public static void test_basic_fills()
        {
            // test trivial hole-fill

            //List<int> tests = new List<int>() { 0, 1 };
            List <int> tests = new List <int>()
            {
                2
            };

            foreach (int num_test in tests)
            {
                string name;
                DMesh3 mesh = MakeEditTestMesh(num_test, out name);
                mesh.EnableTriangleGroups();

                MeshBoundaryLoops loops;
                try {
                    loops = new MeshBoundaryLoops(mesh);
                    Debug.Assert(loops.Loops.Count > 0);
                } catch (Exception) {
                    System.Console.WriteLine("failed to extract boundary loops for " + name);
                    continue;
                }

                System.Console.WriteLine("Closing " + name + " - {0} holes", loops.Loops.Count);

                bool bOK = true;
                foreach (EdgeLoop loop in loops.Loops)
                {
                    SimpleHoleFiller filler = new SimpleHoleFiller(mesh, loop);
                    Debug.Assert(filler.Validate() == ValidationStatus.Ok);
                    bOK = bOK && filler.Fill(1);
                    Debug.Assert(bOK);
                }
                System.Console.WriteLine("{0}", (bOK) ? "Ok" : "Failed");
                if (bOK)
                {
                    Debug.Assert(mesh.CachedIsClosed);
                }
                TestUtil.WriteTestOutputMesh(mesh, name + "_filled" + ".obj");
            }
        }         // test_basic_fills
        public static void test_add_change()
        {
            DMesh3 testMesh = TestUtil.LoadTestInputMesh("bunny_open_base.obj");
            DMesh3 copy     = new DMesh3(testMesh);

            MeshBoundaryLoops loops = new MeshBoundaryLoops(copy);

            foreach (var loop in loops)
            {
                SimpleHoleFiller filler = new SimpleHoleFiller(copy, loop);
                bool             ok     = filler.Fill();
                Util.gDevAssert(ok);

                AddTrianglesMeshChange change = new AddTrianglesMeshChange();
                change.InitializeFromExisting(copy,
                                              new List <int>()
                {
                    filler.NewVertex
                }, filler.NewTriangles);

                DMesh3 tmp = new DMesh3(copy);
                change.Revert(copy);
                copy.CheckValidity(true);

                if (!copy.IsSameMesh(testMesh, true))
                {
                    System.Console.WriteLine("FAILED copy.IsSameMesh() 1");
                }

                change.Apply(copy);
                copy.CheckValidity(true);
                if (!copy.IsSameMesh(tmp, true))
                {
                    System.Console.WriteLine("FAILED copy.IsSameMesh() 1");
                }
            }

            System.Console.WriteLine("test_add_change ok");
        }
        private void FillAnyHoles(DMesh3 mesh,
                                  CancellationToken cancellationToken,
                                  out int nRemaining,
                                  out bool sawSpans)
        {
            var loops = new MeshBoundaryLoops(mesh);

            nRemaining = 0;
            sawSpans   = loops.SawOpenSpans;

            foreach (var loop in loops)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var  filler = new MinimalHoleFill(mesh, loop);
                bool filled = filler.Apply();
                if (filled == false)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var fallback = new SimpleHoleFiller(mesh, loop);
                    fallback.Fill();
                }
            }
        }
        void fill_trivial_holes(out int nRemaining, out bool saw_spans)
        {
            var loops = new MeshBoundaryLoops(Mesh);

            nRemaining = 0;
            saw_spans  = loops.SawOpenSpans;

            foreach (var loop in loops)
            {
                if (Cancelled())
                {
                    break;
                }

                bool filled = false;
                if (loop.VertexCount == 3)
                {
                    var filler = new SimpleHoleFiller(Mesh, loop);
                    filled = filler.Fill();
                }
                else if (loop.VertexCount == 4)
                {
                    var filler = new MinimalHoleFill(Mesh, loop);
                    filled = filler.Apply();
                    if (filled == false)
                    {
                        var fallback = new SimpleHoleFiller(Mesh, loop);
                        filled = fallback.Fill();
                    }
                }

                if (filled == false)
                {
                    ++nRemaining;
                }
            }
        }
Beispiel #8
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);
        }
        public virtual bool Apply()
        {
            DMesh3 testAgainstMesh = Mesh;

            if (InsideMode == CalculationMode.RayParity)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh);
                if (loops.Count > 0)
                {
                    testAgainstMesh = new DMesh3(Mesh);
                    foreach (var loop in loops)
                    {
                        if (Cancelled())
                        {
                            return(false);
                        }
                        SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop);
                        filler.Fill();
                    }
                }
            }

            DMeshAABBTree3 spatial = (Spatial != null && testAgainstMesh == Mesh) ?
                                     Spatial : new DMeshAABBTree3(testAgainstMesh, true);

            if (InsideMode == CalculationMode.AnalyticWindingNumber)
            {
                spatial.WindingNumber(Vector3d.Zero);
            }
            else if (InsideMode == CalculationMode.FastWindingNumber)
            {
                spatial.FastWindingNumber(Vector3d.Zero);
            }

            if (Cancelled())
            {
                return(false);
            }

            // ray directions
            List <Vector3d> ray_dirs = null; int NR = 0;

            if (InsideMode == CalculationMode.SimpleOcclusionTest)
            {
                ray_dirs = new List <Vector3d>();
                ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX);
                ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY);
                ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ);
                NR = ray_dirs.Count;
            }

            Func <Vector3d, bool> isOccludedF = (pt) => {
                if (InsideMode == CalculationMode.RayParity)
                {
                    return(spatial.IsInside(pt));
                }
                else if (InsideMode == CalculationMode.AnalyticWindingNumber)
                {
                    return(spatial.WindingNumber(pt) > WindingIsoValue);
                }
                else if (InsideMode == CalculationMode.FastWindingNumber)
                {
                    return(spatial.FastWindingNumber(pt) > WindingIsoValue);
                }
                else
                {
                    for (int k = 0; k < NR; ++k)
                    {
                        int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k]));
                        if (hit_tid == DMesh3.InvalidID)
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
            };

            bool cancel = false;

            BitArray vertices = null;

            if (PerVertex)
            {
                vertices = new BitArray(Mesh.MaxVertexID);

                MeshNormals normals = null;
                if (Mesh.HasVertexNormals == false)
                {
                    normals = new MeshNormals(Mesh);
                    normals.Compute();
                }

                gParallel.ForEach(Mesh.VertexIndices(), (vid) => {
                    if (cancel)
                    {
                        return;
                    }
                    if (vid % 10 == 0)
                    {
                        cancel = Cancelled();
                    }

                    Vector3d c    = Mesh.GetVertex(vid);
                    Vector3d n    = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid];
                    c            += n * NormalOffset;
                    vertices[vid] = isOccludedF(c);
                });
            }
            if (Cancelled())
            {
                return(false);
            }

            RemovedT = new List <int>();
            SpinLock removeLock = new SpinLock();

            gParallel.ForEach(Mesh.TriangleIndices(), (tid) => {
                if (cancel)
                {
                    return;
                }
                if (tid % 10 == 0)
                {
                    cancel = Cancelled();
                }

                bool inside = false;
                if (PerVertex)
                {
                    Index3i tri = Mesh.GetTriangle(tid);
                    inside      = vertices[tri.a] || vertices[tri.b] || vertices[tri.c];
                }
                else
                {
                    Vector3d c = Mesh.GetTriCentroid(tid);
                    Vector3d n = Mesh.GetTriNormal(tid);
                    c         += n * NormalOffset;
                    inside     = isOccludedF(c);
                }

                if (inside)
                {
                    bool taken = false;
                    removeLock.Enter(ref taken);
                    RemovedT.Add(tid);
                    removeLock.Exit();
                }
            });

            if (Cancelled())
            {
                return(false);
            }

            if (RemovedT.Count > 0)
            {
                MeshEditor editor = new MeshEditor(Mesh);
                bool       bOK    = editor.RemoveTriangles(RemovedT, true);
                RemoveFailed = (bOK == false);
            }

            return(true);
        }
Beispiel #10
0
        public override DMesh3 Cut(CuttingInfo info)
        {
            var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum);

            if (painted.Count <= 0)
            {
                return(info.mesh);
            }

            var components = FindConnectedComponents(info, painted);
            var subMeshes  = new List <DMesh3>();

            foreach (var component in components.Components)
            {
                DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices);
                var       newMesh = subMesh.SubMesh;
                newMesh.EnableTriangleGroups();
                newMesh.EnableVertexColors(ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f());

                foreach (var componentIndex in component.Indices)
                {
                    info.mesh.RemoveTriangle(componentIndex);
                }

                var loops             = new MeshBoundaryLoops(newMesh, true);
                var meshEditorNewMesh = new MeshEditor(newMesh);
                var meshEditorOldMesh = new MeshEditor(info.mesh);
                foreach (var meshBoundaryLoop in loops)
                {
                    var offsettedVerticesNewMesh = new List <int>();
                    var offsettedVerticesOldMesh = new List <int>();
                    foreach (var vertex in meshBoundaryLoop.Vertices)
                    {
                        var normal          = newMesh.GetVertexNormal(vertex);
                        var vertextPosition = newMesh.GetVertex(vertex);
                        var newVertex       = newMesh.AppendVertex(vertextPosition - normal.toVector3d() * info.data.depth);
                        offsettedVerticesNewMesh.Add(newVertex);
                        var newVertexOldMesh =
                            info.mesh.AppendVertex(vertextPosition - normal.toVector3d() * info.data.depth);
                        offsettedVerticesOldMesh.Add(newVertexOldMesh);
                    }

                    var boundaryLoopOfOldMesh = meshBoundaryLoop.Vertices
                                                .Select(subv => subMesh.MapVertexToBaseMesh(subv)).ToArray();
                    var loopNewMesh = meshEditorNewMesh.StitchLoop(meshBoundaryLoop.Vertices,
                                                                   offsettedVerticesNewMesh.ToArray(), info.data.ColorNum);
                    var loopOldMesh = meshEditorOldMesh.StitchLoop(boundaryLoopOfOldMesh,
                                                                   offsettedVerticesOldMesh.ToArray(), info.data.mainColorId);

                    for (var index = 0; index < offsettedVerticesNewMesh.Count; index++)
                    {
                        info.PointToPoint.Add(offsettedVerticesNewMesh[index], offsettedVerticesOldMesh[index]);
                    }

                    var offsettedLoop = EdgeLoop.FromVertices(newMesh, offsettedVerticesNewMesh);
                    var holeFiller    = new SimpleHoleFiller(newMesh, offsettedLoop);
                    var valid         = holeFiller.Validate();
                    if (valid == ValidationStatus.Ok)
                    {
                        var res = holeFiller.Fill(info.data.ColorNum);
                        if (res)
                        {
                            var newVertex    = holeFiller.NewVertex;
                            var newTriangles = holeFiller.NewTriangles;

                            //Add the same triangles to old mesh.
                            if (newVertex == -1) //case where it added only one tri
                            {
                                var vertices     = newMesh.GetTriangle(newTriangles.First());
                                var edgeAOldMesh = info.PointToPoint[vertices.a];
                                var edgeBOldMesh = info.PointToPoint[vertices.b];
                                var edgeCOldMesh = info.PointToPoint[vertices.c];
                                info.mesh.AppendTriangle(edgeAOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId);
                            }
                            else //case where multiple tris and a middle vertex were added
                            {
                                var newVertexOldMesh = info.mesh.AppendVertex(newMesh.GetVertex(newVertex));
                                foreach (var newTriangle in newTriangles)
                                {
                                    //the center is always the first vertex in newTriangle
                                    var edgeVertices = newMesh.GetTriangle(newTriangle);
                                    var edgeBOldMesh = info.PointToPoint[edgeVertices.b];
                                    var edgeCOldMesh = info.PointToPoint[edgeVertices.c];
                                    info.mesh.AppendTriangle(newVertexOldMesh, edgeCOldMesh, edgeBOldMesh,
                                                             info.data.mainColorId);
                                }

                                if (info.PointToPoint.ContainsKey(newVertex))
                                {
                                    Debug.Log($"Double insertion from HF: {newVertex}, {newVertexOldMesh}");
                                }
                                else
                                {
                                    info.PointToPoint.Add(newVertex, newVertexOldMesh);
                                }
                            }
                        }
                    }
                }
                subMeshes.Add(newMesh);
            }

            InstantiateNewObjects(info, subMeshes);


            return(info.mesh);
        }
Beispiel #11
0
        public bool Apply()
        {
            EdgeLoop useLoop = null;

            if (FillLoop == null)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, true);
                if (loops.Count == 0)
                {
                    return(false);
                }

                if (BorderHintTris != null)
                {
                    useLoop = select_loop_tris_hint(loops);
                }
                if (useLoop == null && loops.MaxVerticesLoopIndex >= 0)
                {
                    useLoop = loops[loops.MaxVerticesLoopIndex];
                }
            }
            else
            {
                useLoop = FillLoop;
            }
            if (useLoop == null)
            {
                return(false);
            }

            // step 1: do stupid hole fill
            SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, useLoop);

            if (filler.Fill() == false)
            {
                return(false);
            }

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

            MeshFaceSelection tris = new MeshFaceSelection(Mesh);

            tris.Select(filler.NewTriangles);

            // extrude initial fill surface (this is used in socketgen for example)
            if (OffsetDistance > 0)
            {
                MeshExtrudeFaces extrude = new MeshExtrudeFaces(Mesh, tris);
                extrude.ExtrudedPositionF = (v, n, vid) => {
                    return(v + OffsetDistance * OffsetDirection);
                };
                if (!extrude.Extrude())
                {
                    return(false);
                }
                tris.Select(extrude.JoinTriangles);
            }

            // if we aren't trying to stay inside hole, expand out a bit,
            // which allows us to clean up ugly edges
            if (ConstrainToHoleInterior == false)
            {
                tris.ExpandToOneRingNeighbours(2);
                tris.LocalOptimize(true, true);
            }

            // remesh the initial coarse fill region
            if (RemeshBeforeSmooth)
            {
                RegionRemesher remesh = new RegionRemesher(Mesh, tris);
                remesh.SetTargetEdgeLength(TargetEdgeLength);
                remesh.EnableSmoothing = (SmoothAlpha > 0);
                remesh.SmoothSpeedT    = SmoothAlpha;
                if (ConfigureRemesherF != null)
                {
                    ConfigureRemesherF(remesh, true);
                }
                for (int k = 0; k < InitialRemeshPasses; ++k)
                {
                    remesh.BasicRemeshPass();
                }
                remesh.BackPropropagate();

                tris = new MeshFaceSelection(Mesh);
                tris.Select(remesh.CurrentBaseTriangles);
                if (ConstrainToHoleInterior == false)
                {
                    tris.LocalOptimize(true, true);
                }
            }

            if (ConstrainToHoleInterior)
            {
                for (int k = 0; k < SmoothSolveIterations; ++k)
                {
                    smooth_and_remesh_preserve(tris, k == SmoothSolveIterations - 1);
                    tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles);
                }
            }
            else
            {
                smooth_and_remesh(tris);
                tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles);
            }

            MeshVertexSelection fill_verts = new MeshVertexSelection(Mesh);

            fill_verts.SelectInteriorVertices(tris);
            FillVertices = fill_verts.ToArray();

            return(true);
        }
Beispiel #12
0
        public override DMesh3 Cut(CuttingInfo info)
        {
            var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum);

            if (painted.Count <= 0)
            {
                return(info.mesh);
            }

            var components = FindConnectedComponents(info, painted);
            var subMeshes  = new List <DMesh3>();

            foreach (var component in components.Components)
            {
                DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices);
                var       newMesh = subMesh.SubMesh;
                newMesh.EnableTriangleGroups();
                newMesh.EnableVertexColors(ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f());
                foreach (var componentIndex in component.Indices)
                {
                    info.mesh.RemoveTriangle(componentIndex);
                }


                var loops = new MeshBoundaryLoops(newMesh, true);
                foreach (var meshBoundaryLoop in loops)
                {
                    var holeFiller = new SimpleHoleFiller(newMesh, meshBoundaryLoop);
                    var valid      = holeFiller.Validate();
                    if (valid == ValidationStatus.Ok)
                    {
                        var res = holeFiller.Fill(info.data.ColorNum);
                        if (res)
                        {
                            var newVertex    = holeFiller.NewVertex;
                            var newTriangles = holeFiller.NewTriangles;

                            //Add the same triangles to old mesh.
                            if (newVertex == -1) //case where it added only one tri
                            {
                                var vertices     = newMesh.GetTriangle(newTriangles.First());
                                var edgeAOldMesh = subMesh.MapVertexToBaseMesh(vertices.a);
                                var edgeBOldMesh = subMesh.MapVertexToBaseMesh(vertices.b);
                                var edgeCOldMesh = subMesh.MapVertexToBaseMesh(vertices.c);
                                info.mesh.AppendTriangle(edgeAOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId);
                            }
                            else //case where multiple tris and a middle vertex were added
                            {
                                var newVertexOldMesh = info.mesh.AppendVertex(newMesh.GetVertex(newVertex));
                                foreach (var newTriangle in newTriangles)
                                {
                                    //the center is always the first vertex in newTriangle
                                    var edgeVertices = newMesh.GetTriangle(newTriangle);
                                    var edgeBOldMesh = subMesh.MapVertexToBaseMesh(edgeVertices.b);
                                    var edgeCOldMesh = subMesh.MapVertexToBaseMesh(edgeVertices.c);
                                    info.mesh.AppendTriangle(newVertexOldMesh, edgeCOldMesh, edgeBOldMesh,
                                                             info.data.mainColorId);
                                }

                                if (info.PointToPoint.ContainsKey(newVertex))
                                {
                                    Debug.Log($"Double insertion from HF: {newVertex}, {newVertexOldMesh}");
                                }
                                else
                                {
                                    info.PointToPoint.Add(newVertex, newVertexOldMesh);
                                }
                            }
                        }
                    }
                }
                subMeshes.Add(newMesh);
            }

            InstantiateNewObjects(info, subMeshes);

            return(info.mesh);
        }