Ejemplo n.º 1
0
        void smooth_and_remesh(MeshFaceSelection tris)
        {
            if (EnableLaplacianSmooth)
            {
                LaplacianMeshSmoother.RegionSmooth(Mesh, tris, 2, 2, false);
            }

            if (RemeshAfterSmooth)
            {
                tris.ExpandToOneRingNeighbours(2);
                tris.LocalOptimize(true, true);
                MeshProjectionTarget target = MeshProjectionTarget.Auto(Mesh, tris, 5);

                RegionRemesher remesh2 = new RegionRemesher(Mesh, tris);
                remesh2.SetTargetEdgeLength(TargetEdgeLength);
                remesh2.SmoothSpeedT = 1.0;
                remesh2.SetProjectionTarget(target);
                if (ConfigureRemesherF != null)
                {
                    ConfigureRemesherF(remesh2, false);
                }
                for (int k = 0; k < 10; ++k)
                {
                    remesh2.BasicRemeshPass();
                }
                remesh2.BackPropropagate();

                FillTriangles = remesh2.CurrentBaseTriangles;
            }
            else
            {
                FillTriangles = tris.ToArray();
            }
        }
Ejemplo n.º 2
0
        void smooth_and_remesh_preserve(MeshFaceSelection tris, bool bFinal)
        {
            if (EnableLaplacianSmooth)
            {
                LaplacianMeshSmoother.RegionSmooth(Mesh, tris, 2, 2, true);
            }

            if (RemeshAfterSmooth)
            {
                MeshProjectionTarget target = (bFinal) ? MeshProjectionTarget.Auto(Mesh, tris, 5) : null;

                RegionRemesher remesh2 = new RegionRemesher(Mesh, tris);
                remesh2.SetTargetEdgeLength(TargetEdgeLength);
                remesh2.SmoothSpeedT = 1.0;
                remesh2.SetProjectionTarget(target);
                if (ConfigureRemesherF != null)
                {
                    ConfigureRemesherF(remesh2, false);
                }
                for (int k = 0; k < 10; ++k)
                {
                    remesh2.BasicRemeshPass();
                }
                remesh2.BackPropropagate();

                FillTriangles = remesh2.CurrentBaseTriangles;
            }
            else
            {
                FillTriangles = tris.ToArray();
            }
        }
Ejemplo n.º 3
0
        // This function does local remeshing around a boundary loop within a fixed # of
        // rings, to try to 'massage' it into a cleaner shape/topology
        // [TODO] use geodesic distance instead of fixed # of rings?
        public static void cleanup_boundary(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, EdgeLoop loop, double target_edge_len, int nRings = 3)
        {
            Debug.Assert(loop.IsBoundaryLoop());

            MeshFaceSelection roi = new MeshFaceSelection(mesh);

            roi.SelectVertexOneRings(loop.Vertices);

            for (int i = 0; i < nRings; ++i)
            {
                roi.ExpandToOneRingNeighbours();
            }
            roi.LocalOptimize(true, true);

            RegionRemesher r = new RegionRemesher(mesh, roi.ToArray());

            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    = 0.1f;
            for (int k = 0; k < nRings * 3; ++k)
            {
                r.BasicRemeshPass();
            }
            Debug.Assert(mesh.CheckValidity());

            r.BackPropropagate();
        }
Ejemplo n.º 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);
        }
Ejemplo n.º 5
0
        // smooths embedded loop in mesh, by first smoothing edge loop and then
        // smoothing vertex neighbourhood
        // [TODO] geodesic nbrhoold instead of # of rings
        // [TODO] reprojection?
        public static void smooth_loop(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, EdgeLoop loop, int nRings)
        {
            MeshFaceSelection roi_t = new MeshFaceSelection(mesh);

            roi_t.SelectVertexOneRings(loop.Vertices);
            for (int i = 0; i < nRings; ++i)
            {
                roi_t.ExpandToOneRingNeighbours();
            }
            roi_t.LocalOptimize(true, true);

            MeshVertexSelection roi_v = new MeshVertexSelection(mesh);

            roi_v.SelectTriangleVertices(roi_t.ToArray());
            roi_v.Deselect(loop.Vertices);

            MeshLoopSmooth loop_smooth = new MeshLoopSmooth(mesh, loop);

            loop_smooth.Rounds = 1;

            MeshIterativeSmooth mesh_smooth = new MeshIterativeSmooth(mesh, roi_v.ToArray(), true);

            mesh_smooth.Rounds = 1;

            for (int i = 0; i < 10; ++i)
            {
                loop_smooth.Smooth();
                mesh_smooth.Smooth();
            }
        }
Ejemplo n.º 6
0
        void validate_output()
        {
            if (selection_valid)
            {
                return;
            }

            selection = new MeshFaceSelection(TargetSO.Mesh);
            selectionCache.ComputeSelection(selection);

            selection_valid = true;
        }
Ejemplo n.º 7
0
        public void RemoveBorderRing(int vid, bool bInteractive)
        {
            if (PreviewMesh.IsVertex(vid) == false)
            {
                DebugUtil.Log("MeshEditorTool.RemoveBorderRing: invalid vid!");
                return;
            }
            MeshVertexSelection verts = new MeshVertexSelection(PreviewMesh); verts.SelectConnectedBoundaryV(vid);
            MeshFaceSelection   tris  = new MeshFaceSelection(PreviewMesh, verts, 1);

            do_remove_triangles(tris, bInteractive);
        }
Ejemplo n.º 8
0
 public void ComputeSelection(MeshFaceSelection selection)
 {
     for (int ci = 0; ci <= current_partial_chunk; ++ci)
     {
         OrderedChunk chunk = ordered_chunks[ci];
         if (chunk.mesh == null)
         {
             continue;
         }
         for (int k = 0; k < chunk.mesh.current_count; ++k)
         {
             int idx = chunk.order_range.a + k;
             selection.Select(tri_ordering[idx].tid);
         }
     }
 }
Ejemplo n.º 9
0
        public void EndStroke()
        {
            if (CurrentStroke.Count >= 2)
            {
                DMesh3 mesh = Target.Mesh;

                TransformSequence toScene = SceneTransforms.ObjectToSceneXForm(Target);

                List <int> tris1 = new List <int>(), tris2 = new List <int>();
                Ray3f      first = CurrentStroke[0], last = CurrentStroke[CurrentStroke.Count - 1];

                Vector3f v0 = PlaneFrameS.RayPlaneIntersection(first.Origin, first.Direction, 2);
                Vector3f v1 = PlaneFrameS.RayPlaneIntersection(last.Origin, last.Direction, 2);

                Vector3f planeN = Vector3f.Cross(first.Direction, last.Direction);
                Frame3f  planeF = new Frame3f((v0 + v1) / 2, planeN);

                foreach (int tid in mesh.TriangleIndices())
                {
                    Vector3f c = (Vector3f)mesh.GetTriCentroid(tid);
                    c = toScene.TransformP(c);
                    if (planeF.DistanceToPlaneSigned(c, 2) < 0)
                    {
                        tris1.Add(tid);
                    }
                    else
                    {
                        tris2.Add(tid);
                    }
                }

                double area1 = MeshMeasurements.AreaT(mesh, tris1);
                double area2 = MeshMeasurements.AreaT(mesh, tris2);

                lastSelection = new MeshFaceSelection(mesh);
                lastSelection.Select((area1 > area2) ? tris2 : tris1);
                lastSelection.LocalOptimize();

                if (OnStrokeCompletedF != null)
                {
                    OnStrokeCompletedF(Target, lastSelection);
                }
            }

            CurrentStroke.Clear();
        }
Ejemplo n.º 10
0
        protected override void on_curve_validated()
        {
            if (previewGO != null)
            {
                RemoveGO((fGameObject)previewGO);
                previewGO.Destroy();
            }


            if (EnableRegionOverlay)
            {
                if (TargetModelSO == null)
                {
                    throw new InvalidOperationException("EnclosedPatchSO.on_curve_validated: curve is not connected to a Target");
                }
                if (TransformMode != OutputCurveTransform.ToTargetSO)
                {
                    throw new InvalidOperationException("EnclosedPatchSO.on_curve_validated: curve is not transformed to TargetSO");
                }

                DCurve3           target_curve = RequestCurveCopyFromMainThread();
                MeshFacesFromLoop loop         = new MeshFacesFromLoop(TargetModel.SourceMesh,
                                                                       target_curve, TargetModel.SourceSpatial);
                MeshFaceSelection face_selection = loop.ToSelection();

                DSubmesh3 submesh = new DSubmesh3(TargetModel.SourceMesh, face_selection, face_selection.Count);

                MeshNormals normals = new MeshNormals(submesh.SubMesh);
                normals.Compute();
                foreach (int vid in submesh.SubMesh.VertexIndices())
                {
                    Vector3d n = normals.Normals[vid];
                    Vector3d v = submesh.SubMesh.GetVertex(vid);
                    v += 0.1 * n;
                    v  = SceneTransforms.TransformTo(v, TargetModelSO, this);
                    submesh.SubMesh.SetVertex(vid, v);
                }

                previewGO = GameObjectFactory.CreateMeshGO("patch",
                                                           new fMesh(submesh.SubMesh), false, true);
                previewGO.SetMaterial(previewMaterial, true);
                previewGO.SetLayer(FPlatform.WidgetOverlayLayer);
                previewGO.SetIgnoreMaterialChanges();
                AppendNewGO(previewGO, root, false);
            }
        }
Ejemplo n.º 11
0
    private static Vector3d GetAveragedNormal(DMesh3 mesh, int vertex)
    {
        MeshFaceSelection selection = new MeshFaceSelection(mesh);
        var surroundingTris         = new List <int>();

        mesh.GetVtxTriangles(vertex, surroundingTris, false);
        selection.FloodFill(surroundingTris.ToArray(), i => IsInRange(mesh, vertex, i, 3));
        Vector3d cumulative = Vector3d.Zero;

        foreach (var i in selection)
        {
            cumulative += mesh.GetTriNormal(i);
        }

        var normal = cumulative / selection.Count;

        return(normal);
    }
Ejemplo n.º 12
0
        public static void test_remove_change_construct()
        {
            DMesh3 testMesh = TestUtil.LoadTestInputMesh("bunny_open_base.obj");

            Random r = new Random(31337);
            //int N = 100;
            int N = 10;

            int[] indices = TestUtil.RandomIndices(N, r, testMesh.MaxVertexID);
            for (int ii = 0; ii < N; ++ii)
            {
                MeshFaceSelection selection = new MeshFaceSelection(testMesh);
                selection.SelectVertexOneRing(indices[ii]);
                selection.ExpandToOneRingNeighbours(8);

                RemoveTrianglesMeshChange change = new RemoveTrianglesMeshChange();
                change.InitializeFromExisting(testMesh, selection);

                DMesh3 removed = new DMesh3(testMesh);
                MeshEditor.RemoveTriangles(removed, selection);

                DMesh3 changeCopy = new DMesh3(testMesh);
                change.Apply(changeCopy);
                changeCopy.CheckValidity(true);

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

                change.Revert(changeCopy);
                changeCopy.CheckValidity(false);

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

            System.Console.WriteLine("test_remove_change_construct ok");
        }
Ejemplo n.º 13
0
        public static void test_remove_change_apply()
        {
            DMesh3   testMesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");
            DMesh3   copy     = new DMesh3(testMesh);
            Vector3d c        = testMesh.CachedBounds.Center;

            MeshFaceSelection selection = new MeshFaceSelection(testMesh);

            foreach (int tid in testMesh.TriangleIndices())
            {
                if (testMesh.GetTriCentroid(tid).x > c.x)
                {
                    selection.Select(tid);
                }
            }

            RemoveTrianglesMeshChange change = new RemoveTrianglesMeshChange();

            change.InitializeFromApply(testMesh, selection);

            testMesh.CheckValidity(true);
            change.Apply(copy);
            copy.CheckValidity(true);

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

            change.Revert(testMesh);
            testMesh.CheckValidity(false);
            change.Revert(copy);
            copy.CheckValidity(false);

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

            System.Console.WriteLine("test_remove_change_apply ok");
        }
Ejemplo n.º 14
0
        // local mesh smooth applied to all vertices in N-rings around input list
        public static void smooth_region(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, IEnumerable <int> vertices, int nRings)
        {
            MeshFaceSelection roi_t = new MeshFaceSelection(mesh);

            roi_t.SelectVertexOneRings(vertices);
            for (int i = 0; i < nRings; ++i)
            {
                roi_t.ExpandToOneRingNeighbours();
            }
            roi_t.LocalOptimize(true, true);

            MeshVertexSelection roi_v = new MeshVertexSelection(mesh);

            roi_v.SelectTriangleVertices(roi_t.ToArray());

            MeshIterativeSmooth mesh_smooth = new MeshIterativeSmooth(mesh, roi_v.ToArray(), true);

            mesh_smooth.Alpha  = 0.2f;
            mesh_smooth.Rounds = 10;
            mesh_smooth.Smooth();
        }
        protected void merge_loops(DMesh3 mesh, EdgeLoop cutLoop, EdgeLoop connectorLoop, bool is_outer)
        {
            /*
             * To join the loops, we are going to first make a circle, then snap both
             * open loops to that circle. Then we sample a set of vertices on the circle
             * and remesh the loops while also snapping them to the circle vertices.
             * The result is two perfectly-matching edge loops.
             */

            //AxisAlignedBox3d cutLoopBounds =
            //    BoundsUtil.Bounds(cutLoop.Vertices, (vid) => { return mesh.GetVertex(vid); });

            AxisAlignedBox3d cutLoopBounds       = cutLoop.GetBounds();
            AxisAlignedBox3d connectorLoopBounds = connectorLoop.GetBounds();
            Vector3d         midPt = (cutLoopBounds.Center + connectorLoopBounds.Center) * 0.5;
            double           midY  = midPt.y;

            // this mess construcst the circle and the sampled version
            Frame3f           circFrame       = new Frame3f(midPt);
            Circle3d          circ            = new Circle3d(circFrame, connectorLoopBounds.Width * 0.5, 1);
            DistPoint3Circle3 dist            = new DistPoint3Circle3(Vector3d.Zero, circ);
            DCurve3           sampled         = new DCurve3();
            double            target_edge_len = TargetMeshEdgeLength;
            int N = (int)(circ.ArcLength / target_edge_len);

            for (int k = 0; k < N; ++k)
            {
                sampled.AppendVertex(circ.SampleT((double)k / (double)N));
            }

            MergeProjectionTarget circleTarget = new MergeProjectionTarget()
            {
                Mesh = mesh, CircleDist = dist, CircleLoop = sampled
            };

            EdgeLoop[] loops = new EdgeLoop[2] {
                cutLoop, connectorLoop
            };
            EdgeLoop[] outputLoops = new EdgeLoop[2];   // loops after this remeshing/etc (but might be missing some verts/edges!)
            for (int li = 0; li < 2; ++li)
            {
                EdgeLoop loop = loops[li];

                // snap the loop verts onto the analytic circle
                foreach (int vid in loop.Vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    dist.Point = new Vector3d(v.x, midY, v.z);
                    mesh.SetVertex(vid, dist.Compute().CircleClosest);
                }

                if (DebugStep <= 5)
                {
                    continue;
                }

                // remesh around the edge loop while we snap it to the sampled circle verts
                EdgeLoopRemesher loopRemesh = new EdgeLoopRemesher(mesh, loop)
                {
                    LocalSmoothingRings = 3
                };
                loopRemesh.EnableParallelProjection = false;
                loopRemesh.SetProjectionTarget(circleTarget);
                loopRemesh.SetTargetEdgeLength(TargetMeshEdgeLength);
                loopRemesh.SmoothSpeedT = 0.5f;
                for (int k = 0; k < 5; ++k)
                {
                    loopRemesh.BasicRemeshPass();
                }
                loopRemesh.SmoothSpeedT = 0;
                for (int k = 0; k < 2; ++k)
                {
                    loopRemesh.BasicRemeshPass();
                }
                EdgeLoop newLoop = loopRemesh.OutputLoop;
                outputLoops[li] = newLoop;

                if (DebugStep <= 6)
                {
                    continue;
                }

                // hard snap the loop vertices to the sampled circle verts
                foreach (int vid in newLoop.Vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    v = circleTarget.Project(v, vid);
                    mesh.SetVertex(vid, v);
                }

                // [TODO] we could re-order newLoop verts/edges to match the sampled verts order,
                // then the pair of loops would be consistently ordered (currently no guarantee)

                if (DebugStep <= 7)
                {
                    continue;
                }

                // collapse any degenerate edges on loop (just in case)
                // DANGER: if this actually happens, then outputLoops[li] has some invalid verts/edges!
                foreach (int eid in newLoop.Edges)
                {
                    if (mesh.IsEdge(eid))
                    {
                        Index2i  ev = mesh.GetEdgeV(eid);
                        Vector3d a = mesh.GetVertex(ev.a), b = mesh.GetVertex(ev.b);
                        if (a.Distance(b) < TargetMeshEdgeLength * 0.001)
                        {
                            DMesh3.EdgeCollapseInfo collapse;
                            mesh.CollapseEdge(ev.a, ev.b, out collapse);
                        }
                    }
                }
            }

            if (DebugStep <= 7)
            {
                return;
            }


            /*
             * Ok now we want to merge the loops and make them nice
             */

            // would be more efficient to find loops and stitch them...
            MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh);

            merge.Apply();

            // fill any fail-holes??

            // remesh merge region
            MeshVertexSelection remesh_roi_v = new MeshVertexSelection(mesh);

            remesh_roi_v.Select(outputLoops[0].Vertices);
            remesh_roi_v.Select(outputLoops[1].Vertices);
            remesh_roi_v.ExpandToOneRingNeighbours(5);
            MeshFaceSelection remesh_roi = new MeshFaceSelection(mesh, remesh_roi_v, 1);

            remesh_roi.LocalOptimize(true, true);

            IProjectionTarget projTarget = null;

            if (is_outer)
            {
                projTarget = new NoPenetrationProjectionTarget()
                {
                    Spatial = this.OuterOffsetMeshSpatial
                };
            }
            else
            {
                projTarget = new NoPenetrationProjectionTarget()
                {
                    Spatial = this.InnerOffsetMeshSpatial
                };
            }

            RegionRemesher join_remesh =
                RegionRemesher.QuickRemesh(mesh, remesh_roi.ToArray(), TargetMeshEdgeLength, 0.5, 5, projTarget);

            if (DebugStep <= 8)
            {
                return;
            }


            if (false && is_outer)
            {
                Func <int, bool> filterF = (tid) => {
                    return(mesh.GetTriCentroid(tid).y > connectorLoopBounds.Max.y);
                };

                MeshFaceSelection tris = new MeshFaceSelection(mesh);
                foreach (int tid in join_remesh.CurrentBaseTriangles)
                {
                    if (filterF(tid))
                    {
                        tris.Select(tid);
                    }
                }
                tris.ExpandToOneRingNeighbours(5, filterF);

                MeshVertexSelection verts  = new MeshVertexSelection(mesh, tris);
                MeshIterativeSmooth smooth = new MeshIterativeSmooth(mesh, verts.ToArray(), true);
                smooth.Alpha    = 1.0f;
                smooth.Rounds   = 25;
                smooth.ProjectF = (v, n, vid) => { return(projTarget.Project(v)); };
                smooth.Smooth();
            }


            // [RMS] this smooths too far. we basically only want to smooth 'up' from top of socket...
            //LaplacianMeshSmoother.RegionSmooth(mesh, join_remesh.CurrentBaseTriangles, 1, 10);

            // need to post-enforce thickness, which we aren't doing above - we could though
        }
Ejemplo n.º 16
0
        public bool Apply()
        {
            bool do_checks = false;

            if (do_checks)
            {
                Mesh.CheckValidity();
            }


            /*
             * Remove parts of the mesh we don't want before we bother with anything else
             * TODO: maybe we need to repair orientation first? if we want to use MWN...
             */
            do_remove_inside();
            if (Cancelled())
            {
                return(false);
            }

            int repeat_count = 0;

repeat_all:

            /*
             * make sure orientation of connected components is consistent
             * TODO: what about mobius strip problems?
             */
            repair_orientation(false);
            if (Cancelled())
            {
                return(false);
            }

            /*
             *  Do safe close-cracks to handle easy cases
             */

            repair_cracks(true, RepairTolerance);
            if (Mesh.IsClosed())
            {
                goto all_done;
            }
            if (Cancelled())
            {
                return(false);
            }

            /*
             * Collapse tiny edges and then try easy cases again, and
             * then allow for handling of ambiguous cases
             */

            collapse_all_degenerate_edges(RepairTolerance * 0.5, true);
            if (Cancelled())
            {
                return(false);
            }
            repair_cracks(true, 2 * RepairTolerance);
            if (Cancelled())
            {
                return(false);
            }
            repair_cracks(false, 2 * RepairTolerance);
            if (Cancelled())
            {
                return(false);
            }
            if (Mesh.IsClosed())
            {
                goto all_done;
            }

            /*
             * Possibly we have joined regions with different orientation (is it?), fix that
             * TODO: mobius strips again
             */
            repair_orientation(false);
            if (Cancelled())
            {
                return(false);
            }

            if (do_checks)
            {
                Mesh.CheckValidity();
            }

            // get rid of any remaining single-triangles before we start filling holes
            remove_loners();

            /*
             * Ok, fill simple holes.
             */
            int nRemainingBowties = 0;
            int nHoles; bool bSawSpans;

            fill_trivial_holes(out nHoles, out bSawSpans);
            if (Cancelled())
            {
                return(false);
            }
            if (Mesh.IsClosed())
            {
                goto all_done;
            }

            /*
             * Now fill harder holes. If we saw spans, that means boundary loops could
             * not be resolved in some cases, do we disconnect bowties and try again.
             */
            fill_any_holes(out nHoles, out bSawSpans);
            if (Cancelled())
            {
                return(false);
            }
            if (bSawSpans)
            {
                disconnect_bowties(out nRemainingBowties);
                fill_any_holes(out nHoles, out bSawSpans);
            }
            if (Cancelled())
            {
                return(false);
            }
            if (Mesh.IsClosed())
            {
                goto all_done;
            }

            /*
             * We may have a closed mesh now but it might still have bowties (eg
             * tetrahedra sharing vtx case). So disconnect those.
             */
            disconnect_bowties(out nRemainingBowties);
            if (Cancelled())
            {
                return(false);
            }

            /*
             * If the mesh is not closed, we will do one more round to try again.
             */
            if (repeat_count == 0 && Mesh.IsClosed() == false)
            {
                repeat_count++;
                goto repeat_all;
            }

            /*
             * Ok, we didn't get anywhere on our first repeat. If we are still not
             * closed, we will try deleting boundary triangles and repeating.
             * Repeat this N times.
             */
            if (repeat_count <= ErosionIterations && Mesh.IsClosed() == false)
            {
                repeat_count++;
                MeshFaceSelection bdry_faces = new MeshFaceSelection(Mesh);
                foreach (int eid in MeshIterators.BoundaryEdges(Mesh))
                {
                    bdry_faces.SelectEdgeTris(eid);
                }
                MeshEditor.RemoveTriangles(Mesh, bdry_faces, true);
                goto repeat_all;
            }

all_done:

            /*
             * Remove tiny edges
             */
            if (MinEdgeLengthTol > 0)
            {
                collapse_all_degenerate_edges(MinEdgeLengthTol, false);
            }
            if (Cancelled())
            {
                return(false);
            }

            /*
             * finally do global orientation
             */
            repair_orientation(true);
            if (Cancelled())
            {
                return(false);
            }

            if (do_checks)
            {
                Mesh.CheckValidity();
            }

            /*
             * Might as well compact output mesh...
             */
            Mesh = new DMesh3(Mesh, true);
            MeshNormals.QuickCompute(Mesh);

            return(true);
        }
Ejemplo n.º 17
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);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// This is the action we give to the trim-scan tool, to run on accept
        /// </summary>
        public static void CropScanFromSelection(DMeshSO so, MeshFaceSelection selection, object tool)
        {
            DMesh3 beforeMesh = new DMesh3(so.Mesh);
            DMesh3 mesh       = so.Mesh;

            // [RMS] if we are using the two-point tool, then we can use the user input points to
            // try to figure out an up axis, by assuming the first point is on the base of the scan. Steps are:
            //   1) guess a midpoint. Currently centroid of upper-half of geodesic selection.
            //   2) construct up axis as (midpoint-basepoint). this axis to Y-up.
            Vector3f upAxisS = Vector3f.AxisY;
            TwoPointFaceSelectionTool ptool = tool as TwoPointFaceSelectionTool;

            if (ptool != null)
            {
                var        cache     = ptool.SelectionCache;
                Interval1d range     = new Interval1d(cache.CurrentScalarThreshold / 2, cache.CurrentScalarThreshold);
                List <int> triangles = new List <int>(selection.Count);
                cache.FindTrianglesInScalarInterval(range, triangles);
                Vector3d c        = MeshMeasurements.Centroid(triangles, mesh.GetTriCentroid);
                Vector3d cS       = SceneTransforms.ObjectToSceneP(so, c);
                Vector3d basePosS = ptool.SourcePositionS.Origin;
                upAxisS = (Vector3f)(cS - basePosS).Normalized;
            }

            // crop scan and fill top hole
            List <int> borderTris = selection.FindBorderTris();
            MeshEditor editor     = new MeshEditor(mesh);

            editor.RemoveTriangles((tid) => { return(selection.IsSelected(tid) == false); }, true);
            if (OGActions.FillHoleInScan)
            {
                SmoothedHoleFill fill = new SmoothedHoleFill(mesh)
                {
                    TargetEdgeLength = 2.5f,
                    SmoothAlpha      = 0.5f,
                    BorderHintTris   = borderTris,
                    OffsetDirection  = SceneTransforms.SceneToObjectN(so, upAxisS),
                    OffsetDistance   = (ptool != null) ? 25.0 : 0.0
                };
                fill.Apply();
            }

            so.NotifyMeshEdited();
            DMesh3 afterMesh = new DMesh3(so.Mesh);

            so.GetScene().History.PushChange(new ReplaceEntireMeshChange(so, beforeMesh, afterMesh), true);
            mesh = so.Mesh;

            // Now we auto-align the scan so it points upwards, and then
            // recenter pivot and shift to above ground plane
            if (ptool != null)
            {
                Vector3d    basePosS = ptool.SourcePositionS.Origin;
                Quaternionf alignUp  = Quaternionf.FromTo(upAxisS, Vector3f.AxisY);

                // rotate part so that axis points up
                Frame3f           curF          = so.GetLocalFrame(CoordSpace.SceneCoords);
                Frame3f           newF          = curF.Rotated(alignUp);
                TransformSOChange alignUpChange = new TransformSOChange(so, curF, newF, CoordSpace.SceneCoords);
                basePosS = newF.FromFrameP(curF.ToFrameP(basePosS));   // map to new frame
                so.GetScene().History.PushChange(alignUpChange, false);

                // recenter pivot at bbox center
                // [RMS] previously was using vertex centroid, but this is then affected by mesh density
                //   (maybe tri centroid? but bbox makes more sense...and below we assume box center)
                Vector3d centerL   = mesh.CachedBounds.Center;
                Vector3d centerO   = newF.FromFrameP(centerL);
                Frame3f  newPivotO = new Frame3f(centerO);
                so.GetScene().History.PushChange(new RepositionPivotChangeOp(newPivotO, so), false);

                // position above ground plane
                AxisAlignedBox3d bounds     = so.Mesh.CachedBounds;
                float            h          = (float)bounds.Height;
                Vector3f         o          = newPivotO.Origin;
                Vector3f         translateO = new Vector3f(-o.x, h * 0.5f - o.y + BaseHeightAboveGroundPlaneMM, -o.z);
                //Vector3f translateO = new Vector3f(0, h * 0.5f - o.y + BaseHeightAboveGroundPlaneMM, 0);
                newPivotO.Translate(translateO);
                so.GetScene().History.PushChange(new TransformSOChange(so, newPivotO, CoordSpace.ObjectCoords), false);

                // save base point in frame of scan
                basePosS += translateO;
                Vector3d basePosL = SceneTransforms.SceneToObjectP(so, basePosS);
                OG.Scan.UserBasePoint = basePosL;
            }

            so.GetScene().History.PushInteractionCheckpoint();
        }
Ejemplo n.º 19
0
        protected virtual DMesh3 compute_bounded_distance()
        {
            DMesh3   sourceMesh   = MeshSource.GetDMeshUnsafe();
            ISpatial inputSpatial = MeshSource.GetSpatial();

            DMesh3   targetMesh    = TargetSource.GetDMeshUnsafe();
            ISpatial targetSpatial = TargetSource.GetSpatial();

            double max_dist = (TargetMaxDistance == double.MaxValue) ? double.MaxValue : TargetMaxDistance;

            DMesh3 meshIn = new DMesh3(sourceMesh);

            bool target_closed           = targetMesh.IsClosed();
            MeshVertexSelection roiV     = new MeshVertexSelection(meshIn);
            SpinLock            roi_lock = new SpinLock();

            gParallel.ForEach(meshIn.VertexIndices(), (vid) => {
                Vector3d pos       = meshIn.GetVertex(vid);
                Vector3d posTarget = TransformToTarget.TransformP(pos);
                double dist        = MeshQueries.NearestPointDistance(targetMesh, targetSpatial, posTarget, max_dist);
                bool inside        = (target_closed && targetSpatial.IsInside(posTarget));
                if (dist < max_dist || inside)
                {
                    bool taken = false;
                    roi_lock.Enter(ref taken);
                    roiV.Select(vid);
                    roi_lock.Exit();
                }
            });
            if (is_invalidated())
            {
                return(null);
            }

            MeshFaceSelection roi_faces = new MeshFaceSelection(meshIn, roiV, 1);

            roi_faces.ExpandToOneRingNeighbours(3);
            roi_faces.LocalOptimize();
            if (is_invalidated())
            {
                return(null);
            }

            RegionOperator op      = new RegionOperator(meshIn, roi_faces);
            DMesh3         meshROI = op.Region.SubMesh;

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

            RemesherPro remesher = new RemesherPro(meshROI);

            remesher.SetTargetEdgeLength(TargetEdgeLength);
            remesher.PreventNormalFlips = this.PreventNormalFlips;
            remesher.EnableFlips        = this.EnableFlips;
            remesher.EnableSplits       = this.EnableSplits;
            remesher.EnableCollapses    = this.EnableCollapses;
            remesher.EnableSmoothing    = this.EnableSmoothing;
            remesher.SmoothSpeedT       = this.SmoothingSpeed;

            BoundedProjectionTarget target = new BoundedProjectionTarget()
            {
                Source = sourceMesh, SourceSpatial = inputSpatial,
                Target = targetMesh, TargetSpatial = targetSpatial,
                SourceToTargetXForm = source_to_target,
                TargetToSourceXForm = target_to_source,
                MaxDistance         = max_dist,
                Smoothness          = transition_smoothness
            };

            remesher.SetProjectionTarget(target);

            if (remesher.Constraints == null)
            {
                remesher.SetExternalConstraints(new MeshConstraints());
            }
            MeshConstraintUtil.FixAllBoundaryEdges(remesher.Constraints, meshROI);
            if (is_invalidated())
            {
                return(null);
            }

            remesher.Progress = new ProgressCancel(is_invalidated);
            remesher.FastestRemesh(RemeshRounds);
            if (is_invalidated())
            {
                return(null);
            }

            op.BackPropropagate();

            return(meshIn);
        }
Ejemplo n.º 20
0
        protected void do_flatten(DMesh3 mesh)
        {
            double   BAND_HEIGHT = flatten_band_height;
            Vector3d down_axis   = -Vector3d.AxisY;
            double   dot_thresh  = 0.2;

            AxisAlignedBox3d bounds  = mesh.CachedBounds;
            DMeshAABBTree3   spatial = new DMeshAABBTree3(mesh, true);

            Ray3d   ray     = new Ray3d(bounds.Center - 2 * bounds.Height * Vector3d.AxisY, Vector3d.AxisY);
            int     hit_tid = spatial.FindNearestHitTriangle(ray);
            Frame3f hitF;

            MeshQueries.RayHitPointFrame(mesh, spatial, ray, out hitF);
            Vector3d basePt = hitF.Origin;

            Frame3f basePlane = new Frame3f(basePt, Vector3f.AxisY);

            MeshConnectedComponents components = new MeshConnectedComponents(mesh)
            {
                FilterF = (tid) => {
                    if (mesh.GetTriangleGroup(tid) != LastExtrudeOuterGroupID)
                    {
                        return(false);
                    }
                    Vector3d n, c; double a;
                    mesh.GetTriInfo(tid, out n, out a, out c);
                    double h = Math.Abs(c.y - basePt.y);
                    if (h > BAND_HEIGHT)
                    {
                        return(false);
                    }
                    if (n.Dot(down_axis) < dot_thresh)
                    {
                        return(false);
                    }
                    return(true);
                },
                SeedFilterF = (tid) => {
                    return(tid == hit_tid);
                }
            };

            components.FindConnectedT();

            MeshFaceSelection all_faces = new MeshFaceSelection(mesh);

            foreach (var comp in components)
            {
                MeshVertexSelection vertices = new MeshVertexSelection(mesh);
                vertices.SelectTriangleVertices(comp.Indices);
                foreach (int vid in vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    v = basePlane.ProjectToPlane((Vector3f)v, 2);
                    mesh.SetVertex(vid, v);
                }
                all_faces.SelectVertexOneRings(vertices);
            }

            all_faces.ExpandToOneRingNeighbours(3, (tid) => {
                return(mesh.GetTriangleGroup(tid) == LastExtrudeOuterGroupID);
            });

            RegionRemesher r = new RegionRemesher(mesh, all_faces);

            r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh));
            r.SetTargetEdgeLength(2.0f);
            r.SmoothSpeedT = 1.0f;
            for (int k = 0; k < 10; ++k)
            {
                r.BasicRemeshPass();
            }
            r.SetProjectionTarget(null);
            r.SmoothSpeedT = 1.0f;
            for (int k = 0; k < 10; ++k)
            {
                r.BasicRemeshPass();
            }
            r.BackPropropagate();
        }
Ejemplo n.º 21
0
        public bool Insert()
        {
            Func <int, bool> is_contained_v = (vid) => {
                Vector3d v   = Mesh.GetVertex(vid);
                Vector2f vf2 = ProjectFrame.ToPlaneUV((Vector3f)v, 2);
                return(Polygon.Contains(vf2));
            };

            MeshVertexSelection vertexROI = new MeshVertexSelection(Mesh);
            Index3i             seedT     = Mesh.GetTriangle(SeedTriangle);

            // if a seed vert of seed triangle is containd in polygon, we will
            // flood-fill out from there, this gives a better ROI.
            // If not, we will try flood-fill from the seed triangles.
            List <int> seed_verts = new List <int>();

            for (int j = 0; j < 3; ++j)
            {
                if (is_contained_v(seedT[j]))
                {
                    seed_verts.Add(seedT[j]);
                }
            }
            if (seed_verts.Count == 0)
            {
                seed_verts.Add(seedT.a);
                seed_verts.Add(seedT.b);
                seed_verts.Add(seedT.c);
            }

            // flood-fill out from seed vertices until we have found all vertices
            // contained in polygon
            vertexROI.FloodFill(seed_verts.ToArray(), is_contained_v);

            // convert vertex ROI to face ROI
            MeshFaceSelection faceROI = new MeshFaceSelection(Mesh, vertexROI, 1);

            faceROI.ExpandToOneRingNeighbours();
            faceROI.FillEars(true);    // this might be a good idea...

            // construct submesh
            RegionOperator regionOp   = new RegionOperator(Mesh, faceROI);
            DSubmesh3      roiSubmesh = regionOp.Region;
            DMesh3         roiMesh    = roiSubmesh.SubMesh;

            // save 3D positions of unmodified mesh
            Vector3d[] initialPositions = new Vector3d[roiMesh.MaxVertexID];

            // map roi mesh to plane
            MeshTransforms.PerVertexTransform(roiMesh, roiMesh.VertexIndices(), (v, vid) => {
                Vector2f uv           = ProjectFrame.ToPlaneUV((Vector3f)v, 2);
                initialPositions[vid] = v;
                return(new Vector3d(uv.x, uv.y, 0));
            });

            // save a copy of 2D mesh and construct bvtree. we will use
            // this later to project back to 3d
            // [TODO] can we use a better spatial DS here, that takes advantage of 2D?
            DMesh3         projectMesh = new DMesh3(roiMesh);
            DMeshAABBTree3 projecter   = new DMeshAABBTree3(projectMesh, true);

            MeshInsertUVPolyCurve insertUV = new MeshInsertUVPolyCurve(roiMesh, Polygon);
            //insertUV.Validate()
            bool bOK = insertUV.Apply();

            if (!bOK)
            {
                throw new Exception("insertUV.Apply() failed");
            }

            if (SimplifyInsertion)
            {
                insertUV.Simplify();
            }

            int[] insertedPolyVerts = insertUV.CurveVertices;

            // grab inserted loop, assuming it worked
            EdgeLoop insertedLoop = null;

            if (insertUV.Loops.Count == 1)
            {
                insertedLoop = insertUV.Loops[0];
            }

            // find interior triangles
            List <int> interiorT = new List <int>();

            foreach (int tid in roiMesh.TriangleIndices())
            {
                Vector3d centroid = roiMesh.GetTriCentroid(tid);
                if (Polygon.Contains(centroid.xy))
                {
                    interiorT.Add(tid);
                }
            }
            if (RemovePolygonInterior)
            {
                MeshEditor editor = new MeshEditor(roiMesh);
                editor.RemoveTriangles(interiorT, true);
                InteriorTriangles = null;
            }
            else
            {
                InteriorTriangles = interiorT.ToArray();
            }


            // map back to 3d
            Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero;

            foreach (int vid in roiMesh.VertexIndices())
            {
                // [TODO] somehow re-use exact positions from regionOp maps?

                // construct new 3D pos w/ barycentric interpolation
                Vector3d v   = roiMesh.GetVertex(vid);
                int      tid = projecter.FindNearestTriangle(v);
                Index3i  tri = projectMesh.GetTriangle(tid);
                projectMesh.GetTriVertices(tid, ref a, ref b, ref c);
                Vector3d bary = MathUtil.BarycentricCoords(ref v, ref a, ref b, ref c);
                Vector3d pos  = bary.x * initialPositions[tri.a] + bary.y * initialPositions[tri.b] + bary.z * initialPositions[tri.c];

                roiMesh.SetVertex(vid, pos);
            }

            bOK = BackPropagate(regionOp, insertedPolyVerts, insertedLoop);

            return(bOK);
        }
Ejemplo n.º 22
0
        public static void test_uv_insert_string()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_xy_25x25.obj");

            mesh.EnableVertexUVs(Vector2f.Zero);

            DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh);

            spatial.Build();
            int tid = spatial.FindNearestTriangle(Vector3d.Zero);

            PolygonFont2d font = PolygonFont2d.ReadFont("c:\\scratch\\font.bin");

            //List<GeneralPolygon2d> letter = new List<GeneralPolygon2d>(font.Characters.First().Value.Polygons);
            //double targetWidth = 20.0f;
            List <GeneralPolygon2d> letter = font.GetCharacter('a');
            double targetWidth             = 10.0f;

            AxisAlignedBox2d bounds  = font.MaxBounds;
            Vector2d         center  = bounds.Center;
            Vector2d         scale2d = (targetWidth / font.MaxBounds.Width) * new Vector2d(1, 1);


            for (int li = 0; li < letter.Count; ++li)
            {
                GeneralPolygon2d gp = new GeneralPolygon2d(letter[li]);
                gp.Scale(scale2d, center);
                gp.Translate(-center);
                letter[li] = gp;
            }


            List <MeshFaceSelection> letter_interiors = new List <MeshFaceSelection>();

            bool bSimplify = true;

            for (int li = 0; li < letter.Count; ++li)
            {
                GeneralPolygon2d gp = letter[li];

                MeshInsertUVPolyCurve outer = new MeshInsertUVPolyCurve(mesh, gp.Outer);
                Util.gDevAssert(outer.Validate() == ValidationStatus.Ok);
                outer.Apply();
                if (bSimplify)
                {
                    outer.Simplify();
                }

                List <MeshInsertUVPolyCurve> holes = new List <MeshInsertUVPolyCurve>(gp.Holes.Count);
                for (int hi = 0; hi < gp.Holes.Count; ++hi)
                {
                    MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, gp.Holes[hi]);
                    Util.gDevAssert(insert.Validate() == ValidationStatus.Ok);
                    insert.Apply();
                    if (bSimplify)
                    {
                        insert.Simplify();
                    }
                    holes.Add(insert);
                }


                // find a triangle connected to loop that is inside the polygon
                //   [TODO] maybe we could be a bit more robust about this? at least
                //   check if triangle is too degenerate...
                int      seed_tri   = -1;
                EdgeLoop outer_loop = outer.Loops[0];
                for (int i = 0; i < outer_loop.EdgeCount; ++i)
                {
                    if (!mesh.IsEdge(outer_loop.Edges[i]))
                    {
                        continue;
                    }

                    Index2i  et   = mesh.GetEdgeT(outer_loop.Edges[i]);
                    Vector3d ca   = mesh.GetTriCentroid(et.a);
                    bool     in_a = gp.Outer.Contains(ca.xy);
                    Vector3d cb   = mesh.GetTriCentroid(et.b);
                    bool     in_b = gp.Outer.Contains(cb.xy);
                    if (in_a && in_b == false)
                    {
                        seed_tri = et.a;
                        break;
                    }
                    else if (in_b && in_a == false)
                    {
                        seed_tri = et.b;
                        break;
                    }
                }
                Util.gDevAssert(seed_tri != -1);

                // make list of all outer & hole edges
                HashSet <int> loopEdges = new HashSet <int>(outer_loop.Edges);
                foreach (var insertion in holes)
                {
                    foreach (int eid in insertion.Loops[0].Edges)
                    {
                        loopEdges.Add(eid);
                    }
                }

                // flood-fill inside loop from seed triangle
                MeshFaceSelection sel = new MeshFaceSelection(mesh);
                sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); });
                letter_interiors.Add(sel);
            }

            // extrude regions
            Func <Vector3d, Vector3f, int, Vector3d> OffsetF = (v, n, i) => {
                return(v + Vector3d.AxisZ);
            };

            foreach (var interior in letter_interiors)
            {
                MeshExtrudeFaces extrude = new MeshExtrudeFaces(mesh, interior);
                extrude.ExtrudedPositionF = OffsetF;
                extrude.Extrude();
            }

            TestUtil.WriteTestOutputMesh(mesh, "insert_uv_string.obj");
        }
Ejemplo n.º 23
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);
        }
Ejemplo n.º 24
0
        public Mesh Repair(Mesh sourceMesh, CancellationToken cancellationToken)
        {
            var inMesh = sourceMesh;

            try
            {
                if (WeldVertices)
                {
                    inMesh = sourceMesh.Copy(cancellationToken);
                    if (WeldTolerance > 0)
                    {
                        inMesh.MergeVertices(.01);
                    }
                    else
                    {
                        inMesh.CleanAndMerge();
                    }

                    if (!FaceOrientation &&
                        RemoveMode == RemoveModes.None &&
                        !WeldEdges &&
                        !FillHoles)
                    {
                        return(inMesh);
                    }
                }

                var    mesh              = inMesh.ToDMesh3();
                int    repeatCount       = 0;
                int    erosionIterations = 5;
                double repairTolerance   = MathUtil.ZeroTolerancef;
                double minEdgeLengthTol  = 0.0001;

repeat_all:

                if (FaceOrientation)
                {
                    // make sure orientation of connected components is consistent
                    // TODO: what about mobius strip problems?
                    RepairOrientation(mesh, cancellationToken, true);
                }

                if (RemoveMode != RemoveModes.None)
                {
                    // Remove parts of the mesh we don't want before we bother with anything else
                    // TODO: maybe we need to repair orientation first? if we want to use MWN (MeshWindingNumber)...
                    RemoveInside(mesh);
                    cancellationToken.ThrowIfCancellationRequested();
                }

                if (WeldEdges || FillHoles)
                {
                    // Do safe close-cracks to handle easy cases
                    RepairCracks(mesh, true, repairTolerance);

                    if (mesh.IsClosed())
                    {
                        goto all_done;
                    }

                    cancellationToken.ThrowIfCancellationRequested();

                    // Collapse tiny edges and then try easy cases again, and
                    // then allow for handling of ambiguous cases
                    CollapseAllDegenerateEdges(mesh, cancellationToken, repairTolerance * 0.5, true);
                    cancellationToken.ThrowIfCancellationRequested();

                    RepairCracks(mesh, true, 2 * repairTolerance);
                    cancellationToken.ThrowIfCancellationRequested();

                    RepairCracks(mesh, false, 2 * repairTolerance);
                    cancellationToken.ThrowIfCancellationRequested();

                    if (mesh.IsClosed())
                    {
                        goto all_done;
                    }

                    // Possibly we have joined regions with different orientation (is it?), fix that
                    // TODO: mobius strips again
                    RepairOrientation(mesh, cancellationToken, true);
                    cancellationToken.ThrowIfCancellationRequested();

                    // get rid of any remaining single-triangles before we start filling holes
                    MeshEditor.RemoveIsolatedTriangles(mesh);
                }

                if (FillHoles)
                {
                    // Ok, fill simple holes.
                    int nRemainingBowties = 0;
                    FillTrivialHoles(mesh, cancellationToken, out int nHoles, out bool bSawSpans);
                    cancellationToken.ThrowIfCancellationRequested();

                    if (mesh.IsClosed())
                    {
                        goto all_done;
                    }

                    // Now fill harder holes. If we saw spans, that means boundary loops could
                    // not be resolved in some cases, do we disconnect bowties and try again.
                    FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans);
                    cancellationToken.ThrowIfCancellationRequested();

                    if (bSawSpans)
                    {
                        DisconnectBowties(mesh, out nRemainingBowties);
                        FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans);
                    }

                    cancellationToken.ThrowIfCancellationRequested();

                    if (mesh.IsClosed())
                    {
                        goto all_done;
                    }

                    // We may have a closed mesh now but it might still have bowties (eg
                    // tetrahedra sharing vtx case). So disconnect those.
                    DisconnectBowties(mesh, out nRemainingBowties);
                    cancellationToken.ThrowIfCancellationRequested();

                    // If the mesh is not closed, we will do one more round to try again.
                    if (repeatCount == 0 && mesh.IsClosed() == false)
                    {
                        repeatCount++;
                        goto repeat_all;
                    }

                    // Ok, we didn't get anywhere on our first repeat. If we are still not
                    // closed, we will try deleting boundary triangles and repeating.
                    // Repeat this N times.
                    if (repeatCount <= erosionIterations && mesh.IsClosed() == false)
                    {
                        repeatCount++;
                        var bdry_faces = new MeshFaceSelection(mesh);
                        foreach (int eid in MeshIterators.BoundaryEdges(mesh))
                        {
                            bdry_faces.SelectEdgeTris(eid);
                        }

                        MeshEditor.RemoveTriangles(mesh, bdry_faces, true);
                        goto repeat_all;
                    }
                }

all_done:

                // and do a final clean up of the model
                if (FillHoles)
                {
                    // Remove tiny edges
                    if (minEdgeLengthTol > 0)
                    {
                        CollapseAllDegenerateEdges(mesh, cancellationToken, minEdgeLengthTol, false);
                    }

                    cancellationToken.ThrowIfCancellationRequested();

                    // finally do global orientation
                    RepairOrientation(mesh, cancellationToken, true);
                    cancellationToken.ThrowIfCancellationRequested();
                }

                return(mesh.ToMesh());
            }
            catch (OperationCanceledException)
            {
                return(inMesh);
            }
        }
Ejemplo n.º 25
0
        public static void test_uv_insert_segment()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_250v.obj");

            mesh.EnableVertexUVs(Vector2f.Zero);

            MeshTransforms.ConvertYUpToZUp(mesh);

            DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh);

            spatial.Build();
            int tid = spatial.FindNearestTriangle(Vector3d.Zero);

            //Polygon2d poly = Polygon2d.MakeRectangle(Vector2d.Zero, 5, 5);
            Polygon2d poly = Polygon2d.MakeCircle(5, 13);
            //PolyLine2d poly = new PolyLine2d( new Vector2d[] { -5 * Vector2d.One, 5 * Vector2d.One });


            //int tri_edge0 = mesh.GetTriEdge(tid, 0);
            //Index2i edge0_tris = mesh.GetEdgeT(tri_edge0);
            //Index2i edge0_verts = mesh.GetEdgeV(tri_edge0);
            //Vector3d v0 = mesh.GetVertex(edge0_verts.a), v1 = mesh.GetVertex(edge0_verts.b);
            //Vector3d c = mesh.GetTriCentroid(tid);
            //Polygon2d poly = new Polygon2d(new Vector2d[] {
            //    Vector2d.Lerp(v0.xy, v1.xy, -0.25),
            //    Vector2d.Lerp(v0.xy, v1.xy, 1.5),
            //    c.xy
            //});

            MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, poly);

            insert.Apply();



            Polygon2d     test_poly = new Polygon2d();
            List <double> distances = new List <double>();
            List <int>    nearests  = new List <int>();

            for (int i = 0; i < insert.Loops[0].VertexCount; ++i)
            {
                Vector2d v = mesh.GetVertex(insert.Loops[0].Vertices[i]).xy;
                test_poly.AppendVertex(v);
                int iNear; double fNear;
                distances.Add(poly.DistanceSquared(v, out iNear, out fNear));
                nearests.Add(iNear);
            }

            System.Console.WriteLine("inserted loop poly has {0} edges", insert.Loops[0].EdgeCount);

            // find a triangle connected to loop that is inside the polygon
            //   [TODO] maybe we could be a bit more robust about this? at least
            //   check if triangle is too degenerate...
            int seed_tri = -1;

            for (int i = 0; i < insert.Loops[0].EdgeCount; ++i)
            {
                Index2i  et   = mesh.GetEdgeT(insert.Loops[0].Edges[i]);
                Vector3d ca   = mesh.GetTriCentroid(et.a);
                bool     in_a = poly.Contains(ca.xy);
                Vector3d cb   = mesh.GetTriCentroid(et.b);
                bool     in_b = poly.Contains(cb.xy);
                if (in_a && in_b == false)
                {
                    seed_tri = et.a;
                    break;
                }
                else if (in_b && in_a == false)
                {
                    seed_tri = et.b;
                    break;
                }
            }
            Util.gDevAssert(seed_tri != -1);

            // flood-fill inside loop
            HashSet <int>     loopEdges = new HashSet <int>(insert.Loops[0].Edges);
            MeshFaceSelection sel       = new MeshFaceSelection(mesh);

            sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); });

            // delete inside loop
            MeshEditor editor = new MeshEditor(mesh);

            editor.RemoveTriangles(sel, true);


            MeshTransforms.ConvertZUpToYUp(mesh);

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



            //OBJWriter writer = new OBJWriter();
            //var s = new System.IO.StreamWriter(Program.TEST_OUTPUT_PATH + "mesh_local_param.obj", false);
            //List<WriteMesh> wm = new List<WriteMesh>() { new WriteMesh(mesh) };
            //WriteOptions opt = new WriteOptions() {
            //    bCombineMeshes = false, bWriteGroups = false, bPerVertexColors = true, bPerVertexUVs = true,
            //    AsciiHeaderFunc = () => { return "mttllib checkerboard.mtl\r\nusemtl checkerboard\r\n"; }
            //};
            //writer.Write(s, wm, opt);
            //s.Close();
        }