示例#1
0
        internal static void test_MeshAutoRepair()
        {
            Console.Write("test_MeshAutoRepair...");
            // b1 will has edges that are overlapping, but disconnected, it's the result of a merge
            // I guess autorepair should weld it back

            DMesh3 b1 = TestUtil.LoadTestInputMesh("NeedHealing.obj");

            // b2 is the same mesh, but we weld it
            var b2 = new DMesh3(b1, true);
            MergeCoincidentEdges merg = new MergeCoincidentEdges(b2);

            merg.Apply();

            MeshAutoRepair autoRepair = new MeshAutoRepair(b1);

            autoRepair.Apply();

            var allOk =
                b1.VertexCount == b2.VertexCount &&
                b1.EdgeCount == b2.EdgeCount &&
                b1.TriangleCount == b2.TriangleCount;

            if (!allOk)
            {
                var outF = TestUtil.WriteTestOutputMesh(b1, "MeshOps_MeshAutoRepair.obj");
                Console.WriteLine($"Error, shape written to: {outF}");
            }
            else
            {
                Console.WriteLine("Ok");
            }
        }
示例#2
0
        public static void basic_tests()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("three_edge_crack.obj");
            MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh);

            merge.Apply();
            Util.gDevAssert(mesh.BoundaryEdgeIndices().Count() == 0);
            mesh.CheckValidity(true, FailMode.DebugAssert);
            TestUtil.WriteTestOutputMesh(mesh, "three_edge_crack_merged.obj");

            DMesh3 mesh2 = TestUtil.LoadTestInputMesh("crack_loop.obj");
            MergeCoincidentEdges merge2 = new MergeCoincidentEdges(mesh2);

            merge2.Apply();
            Util.gDevAssert(mesh2.BoundaryEdgeIndices().Count() == 0);
            mesh2.CheckValidity(true, FailMode.DebugAssert);
            TestUtil.WriteTestOutputMesh(mesh2, "crack_loop_merged.obj");

            DMesh3 mesh3 = TestUtil.LoadTestInputMesh("cracks_many.obj");
            MergeCoincidentEdges merge3 = new MergeCoincidentEdges(mesh3);

            merge3.Apply();
            Util.gDevAssert(mesh3.BoundaryEdgeIndices().Count() == 0);
            mesh3.CheckValidity(true, FailMode.DebugAssert);
            TestUtil.WriteTestOutputMesh(mesh3, "cracks_many_merged.obj");

            DMesh3 mesh4 = TestUtil.LoadTestInputMesh("cracks_duplicate_edge.obj");
            MergeCoincidentEdges merge4 = new MergeCoincidentEdges(mesh4);

            merge4.Apply();
            Util.gDevAssert(mesh4.BoundaryEdgeIndices().Count() == 0);
            mesh4.CheckValidity(true, FailMode.DebugAssert);
            TestUtil.WriteTestOutputMesh(mesh4, "cracks_duplicate_edge_merged.obj");
        }
示例#3
0
        public static void test_auto_fill()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_cases.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_tempcase1.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_planarcase.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_box_edge_strip.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("autofill_box_edge_strip_2.obj");
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("crazyhole.obj");

            double mine, maxe, avge;

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

            int ROUNDS = 1;

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

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

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

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

            //DMesh3 mesh = new DMesh3();
            //SphericalFibonacciPointSet ps = new SphericalFibonacciPointSet();
            //for (int k = 0; k < ps.N; ++k) {
            //    MeshEditor.AppendBox(mesh, ps[k], ps[k], 0.05f);
            //}
            //Random r = new Random(31337);
            //Vector3d[] pts = TestUtil.RandomPoints3(10000, r, Vector3d.Zero);
            //foreach ( Vector3d pt in pts ) {
            //    pt.Normalize();
            //    int nearest = ps.NearestPoint(pt, true);
            //    Vector3d p = ps[nearest];
            //    MeshEditor.AppendLine(mesh, new Segment3d(p, p + 0.25f * pt), 0.01f);
            //}
            //TestUtil.WriteTestOutputMesh(mesh, "fibonacci.obj");
        }
示例#4
0
        public static void hard_test()
        {
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("three_edge_crack.obj");
            DMesh3 mesh = TestUtil.LoadTestMesh("c:\\scratch\\VTX_Scan_removeocc.obj");

            //MeshEditor editor = new MeshEditor(mesh);
            //editor.DisconnectAllBowties(100);

            mesh.CheckValidity(true, FailMode.DebugAssert);
            MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh);

            merge.Apply();
            mesh.CheckValidity(true, FailMode.DebugAssert);
            TestUtil.WriteTestOutputMesh(mesh, "vtx_scan_merged.obj");
        }
示例#5
0
 private bool RepairCracks(DMesh3 mesh, bool bUniqueOnly, double mergeDist)
 {
     try
     {
         var merge = new MergeCoincidentEdges(mesh)
         {
             OnlyUniquePairs = bUniqueOnly,
             MergeDistance   = mergeDist
         };
         return(merge.Apply());
     }
     catch (Exception /*e*/)
     {
         // ??
         return(false);
     }
 }
示例#6
0
        public static void test_write_solids()
        {
            //string FORMAT = ".obj";
            string FORMAT    = ".g3mesh";
            string WRITEPATH = "E:\\Thingi10K\\closed\\";

            string[] files = File.ReadAllLines("E:\\Thingi10K\\current\\thingi10k_closed.txt");
            SafeListBuilder <string> failures = new SafeListBuilder <string>();

            if (!Directory.Exists(WRITEPATH))
            {
                Directory.CreateDirectory(WRITEPATH);
            }

            int k = 0;

            gParallel.ForEach(files, (filename) => {
                int i = k;
                Interlocked.Increment(ref k);
                if (i % 500 == 0)
                {
                    System.Console.WriteLine("{0} : {1}", i, files.Length);
                }

                long start_ticks = DateTime.Now.Ticks;

                DMesh3Builder builder     = new DMesh3Builder();
                StandardMeshReader reader = new StandardMeshReader()
                {
                    MeshBuilder = builder
                };
                IOReadResult result = reader.Read(filename, ReadOptions.Defaults);
                if (result.code != IOCode.Ok)
                {
                    System.Console.WriteLine("{0} FAILED!", filename);
                    failures.SafeAdd(filename);
                    return;
                }

                DMesh3 combineMesh = new DMesh3();
                if (builder.Meshes.Count == 1)
                {
                    combineMesh = builder.Meshes[0];
                }
                else
                {
                    foreach (DMesh3 mesh in builder.Meshes)
                    {
                        MeshEditor.Append(combineMesh, mesh);
                    }
                }


                if (combineMesh.IsClosed() == false)
                {
                    MergeCoincidentEdges closeCracks = new MergeCoincidentEdges(combineMesh);
                    closeCracks.Apply();
                }

                if (combineMesh.IsClosed() == false)
                {
                    System.Console.WriteLine("NOT CLOSED: {0}", filename);
                    return;
                }

                string outPath = Path.Combine(WRITEPATH, Path.GetFileNameWithoutExtension(filename) + FORMAT);
                StandardMeshWriter.WriteMesh(outPath, combineMesh, WriteOptions.Defaults);
            });
        }
示例#7
0
        public static void test_specific_file()
        {
            //string filename = "F:\\Thingi10K\\raw_meshes\\1423009.stl";
            //string filename = "E:\\Thingi10K\\raw_meshes\\99944.stl";
            string filename = "E:\\Thingi10K\\raw_meshes\\57356.stl";

            System.Console.WriteLine("reading {0}", filename);

            DMesh3Builder      builder = new DMesh3Builder();
            StandardMeshReader reader  = new StandardMeshReader()
            {
                MeshBuilder = builder
            };
            IOReadResult result = reader.Read(filename, ReadOptions.Defaults);

            if (result.code != IOCode.Ok)
            {
                System.Console.WriteLine("{0} FAILED!", filename);
                return;
            }

            System.Console.WriteLine("got {0} meshes", builder.Meshes.Count);

            bool is_open      = false;
            bool loops_failed = false;
            bool is_empty     = true;

            foreach (DMesh3 mesh in builder.Meshes)
            {
                if (mesh.TriangleCount > 0)
                {
                    is_empty = false;
                }

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

                if (mesh.IsClosed() == false)
                {
                    MergeCoincidentEdges closeCracks = new MergeCoincidentEdges(mesh);
                    closeCracks.Apply();
                }

                if (mesh.IsClosed() == false)
                {
                    is_open = true;
                    try {
                        MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh, false)
                        {
                            SpanBehavior    = MeshBoundaryLoops.SpanBehaviors.Compute,
                            FailureBehavior = MeshBoundaryLoops.FailureBehaviors.ConvertToOpenSpan
                        };
                        loops.Compute();
                    } catch (Exception e) {
                        System.Console.WriteLine("EXCEPTION: " + e.Message);
                        loops_failed = true;
                    }
                }

                System.Console.WriteLine("open: {0}  loopsFailed: {1}  empty: {2}", is_open, loops_failed, is_empty);
                TestUtil.WriteTestOutputMesh(mesh, "thingi10k_specific_file_out.obj");
            }
        }
示例#8
0
        public static void test_repair_all()
        {
            //const string THINGIROOT = "D:\\meshes\\Thingi10K\\raw_meshes\\";
            const string THINGIROOT = "E:\\Thingi10K\\raw_meshes\\";

            string[] files = Directory.GetFiles(THINGIROOT);
            //files = File.ReadAllLines("C:\\git\\geometry3SharpDemos\\geometry3Test\\test_output\\thingi10k_open.txt");
            SafeListBuilder <string> failures = new SafeListBuilder <string>();

            SafeListBuilder <string> empty                   = new SafeListBuilder <string>();
            SafeListBuilder <string> closed                  = new SafeListBuilder <string>();
            SafeListBuilder <string> open                    = new SafeListBuilder <string>();
            SafeListBuilder <string> boundaries_failed       = new SafeListBuilder <string>();
            SafeListBuilder <string> boundaries_spans_failed = new SafeListBuilder <string>();
            SafeListBuilder <string> slow                    = new SafeListBuilder <string>();
            SafeListBuilder <string> veryslow                = new SafeListBuilder <string>();

            int k             = 0;
            int MAX_NUM_FILES = 10000;

            gParallel.ForEach(files, (filename) => {
                if (k > MAX_NUM_FILES)
                {
                    return;
                }

                int i = k;
                Interlocked.Increment(ref k);
                System.Console.WriteLine("{0} : {1}", i, filename);

                long start_ticks = DateTime.Now.Ticks;

                DMesh3Builder builder     = new DMesh3Builder();
                StandardMeshReader reader = new StandardMeshReader()
                {
                    MeshBuilder = builder
                };
                IOReadResult result = reader.Read(filename, ReadOptions.Defaults);
                if (result.code != IOCode.Ok)
                {
                    System.Console.WriteLine("{0} FAILED!", filename);
                    failures.SafeAdd(filename);
                    return;
                }

                bool is_open            = false;
                bool loops_failed       = false;
                bool loops_spans_failed = false;
                bool is_empty           = true;
                foreach (DMesh3 mesh in builder.Meshes)
                {
                    if (mesh.TriangleCount > 0)
                    {
                        is_empty = false;
                    }

                    if (mesh.IsClosed() == false)
                    {
                        MergeCoincidentEdges closeCracks = new MergeCoincidentEdges(mesh);
                        closeCracks.Apply();
                    }

                    if (mesh.IsClosed() == false)
                    {
                        is_open = true;
                        try {
                            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh, false)
                            {
                                SpanBehavior    = MeshBoundaryLoops.SpanBehaviors.ThrowException,
                                FailureBehavior = MeshBoundaryLoops.FailureBehaviors.ThrowException
                            };
                            loops.Compute();
                        } catch {
                            loops_failed = true;
                        }

                        if (loops_failed)
                        {
                            try {
                                MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh, false)
                                {
                                    SpanBehavior    = MeshBoundaryLoops.SpanBehaviors.Compute,
                                    FailureBehavior = MeshBoundaryLoops.FailureBehaviors.ConvertToOpenSpan
                                };
                                loops.Compute();
                            } catch {
                                loops_spans_failed = true;
                            }
                        }
                    }
                }


                TimeSpan elapsed = new TimeSpan(DateTime.Now.Ticks - start_ticks);
                if (elapsed.TotalSeconds > 60)
                {
                    veryslow.SafeAdd(filename);
                }
                else if (elapsed.TotalSeconds > 10)
                {
                    slow.SafeAdd(filename);
                }

                if (is_empty)
                {
                    empty.SafeAdd(filename);
                }
                else if (is_open)
                {
                    open.SafeAdd(filename);
                    if (loops_failed)
                    {
                        boundaries_failed.SafeAdd(filename);
                    }
                    if (loops_spans_failed)
                    {
                        boundaries_spans_failed.SafeAdd(filename);
                    }
                }
                else
                {
                    closed.SafeAdd(filename);
                }
            });


            foreach (string failure in failures.Result)
            {
                System.Console.WriteLine("FAIL: {0}", failure);
            }

            TestUtil.WriteTestOutputStrings(make_strings(failures), "thingi10k_failures.txt");
            TestUtil.WriteTestOutputStrings(make_strings(empty), "thingi10k_empty.txt");
            TestUtil.WriteTestOutputStrings(make_strings(closed), "thingi10k_closed.txt");
            TestUtil.WriteTestOutputStrings(make_strings(open), "thingi10k_open.txt");
            TestUtil.WriteTestOutputStrings(make_strings(boundaries_failed), "thingi10k_boundaries_failed.txt");
            TestUtil.WriteTestOutputStrings(make_strings(boundaries_spans_failed), "thingi10k_boundaries_spans_failed.txt");

            TestUtil.WriteTestOutputStrings(make_strings(slow), "thingi10k_slow.txt");
            TestUtil.WriteTestOutputStrings(make_strings(veryslow), "thingi10k_veryslow.txt");
        }
        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
        }