Example #1
0
        virtual public void Setup()
        {
            // push history stream, so that we can do undo/redo internal to tool,
            // that will not end up in external history
            push_history_stream();

            if (PreviewMaterial == null)
            {
                PreviewMaterial = SOMaterial.CreateFlatShaded("holefill_preview", Colorf.DimGrey);
            }

            if (HoleBoundaryMaterial == null)
            {
                HoleBoundaryMaterial = SOMaterial.CreateStandard("holefill_boundary", Colorf.PivotYellow);
            }

            previewSO = new DMeshSO();
            previewSO.EnableSpatial = false;
            previewSO.Create(new DMesh3(Target.Mesh), PreviewMaterial);
            previewSO.Name = "HoleFillTool_preview";
            previewSO.SetLocalFrame(Target.GetLocalFrame(CoordSpace.ObjectCoords), CoordSpace.ObjectCoords);
            previewSO.SetLocalScale(Target.GetLocalScale());
            previewSO.OnMeshModified += PreviewSO_OnMeshModified;
            Scene.AddSceneObject(previewSO);

            sceneToObjUnitScale = SceneTransforms.SceneToObject(Target, 1.0f);

            Loops               = new MeshBoundaryLoops(previewSO.Mesh);
            boundaryGeom        = new BoundaryCurveSet(previewSO.RootGameObject, HoleBoundaryMaterial.ToFMaterial());
            boundaryGeom.Radius = BorderRadius.SceneValue * sceneToObjUnitScale;
            boundaryGeom.Initialize(Loops);
        }
Example #2
0
        void fill_any_holes(out int nRemaining, out bool saw_spans)
        {
            MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh);

            nRemaining = 0;
            saw_spans  = loops.SawOpenSpans;

            foreach (var loop in loops)
            {
                if (Cancelled())
                {
                    break;
                }
                MinimalHoleFill filler = new MinimalHoleFill(Mesh, loop);
                bool            filled = filler.Apply();
                if (filled == false)
                {
                    if (Cancelled())
                    {
                        break;
                    }
                    SimpleHoleFiller fallback = new SimpleHoleFiller(Mesh, loop);
                    filled = fallback.Fill();
                }
            }
        }
Example #3
0
        public static void TestOffsetAnimation()
        {
            Window window = new Window("TestFill");

            window.SetDefaultSize(600, 600);
            window.SetPosition(WindowPosition.Center);

            DMesh3            mesh  = StandardMeshReader.ReadMesh("c:\\scratch\\remesh.obj");
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
            DCurve3           curve = loops[0].ToCurve();
            Polygon2d         poly  = new Polygon2d();

            foreach (Vector3d v in curve.Vertices)
            {
                poly.AppendVertex(v.xy);
            }
            Outer = new GeneralPolygon2d(poly);

            DebugViewCanvas view = new DebugViewCanvas();

            view.AddPolygon(Outer, Colorf.Black);


            DGraph2 graph = TopoOffset2d.QuickCompute(Outer, AnimOffset, AnimSpacing);

            view.AddGraph(graph, Colorf.Red);

            window.Add(view);
            window.ShowAll();

            Active = view;
        }
Example #4
0
    InteractiveRemesher make_remesher(DMesh3 mesh)
    {
        var m = new InteractiveRemesher(mesh);

        m.PreventNormalFlips = true;

        double mine, maxe, avge;

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

        m.SmoothSpeedT = SmoothSpeed;

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

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

        return(m);
    }
Example #5
0
        public bool Compute()
        {
            PlanarMesh   = BuildPlanarMesh(false);
            InflatedMesh = ComputeInflation(PlanarMesh);

            DMesh3 remeshed = GenerateRemesh(InflatedMesh);

            Flatten(remeshed);

            ResultMesh = ComputeInflation(remeshed);

            MeshBoundaryLoops loops = new MeshBoundaryLoops(ResultMesh);

            DMesh3 otherSide = new DMesh3(ResultMesh);

            foreach (int vid in otherSide.VertexIndices())
            {
                Vector3d v = otherSide.GetVertex(vid);
                v.z = -v.z;
                otherSide.SetVertex(vid, v);
            }
            otherSide.ReverseOrientation();

            MeshEditor editor = new MeshEditor(ResultMesh);

            int[] mapVArray;
            editor.AppendMesh(otherSide, out mapVArray);
            IndexMap mapV = new IndexMap(mapVArray);

            foreach (EdgeLoop loop in loops)
            {
                int[] otherLoop = (int[])loop.Vertices.Clone();
                IndexUtil.Apply(otherLoop, mapV);
                editor.StitchLoop(loop.Vertices, otherLoop);
            }


            Remesher remesh = new Remesher(ResultMesh);

            remesh.SetTargetEdgeLength(TargetEdgeLength);
            remesh.SmoothSpeedT = 0.25f;
            for (int k = 0; k < 10; ++k)
            {
                remesh.BasicRemeshPass();
            }
            ResultMesh = new DMesh3(ResultMesh, true);


            LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(ResultMesh);

            foreach (int vid in ResultMesh.VertexIndices())
            {
                smoother.SetConstraint(vid, ResultMesh.GetVertex(vid), 0.5f, false);
            }
            smoother.SolveAndUpdateMesh();


            return(true);
        }
Example #6
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            this.Message = type.ToString();

            DMesh3_goo goo  = null;
            double     eLen = 1;

            DA.GetData(0, ref goo);
            DA.GetData(1, ref eLen);

            DMesh3 msh = new DMesh3(goo.Value);

            DMesh3            outMesh = msh;
            MeshBoundaryLoops loops   = new MeshBoundaryLoops(outMesh, true);

            var lps = loops.Loops;

            bool hasLoops = (lps.Count > 0);
            int  iter     = 0;

            while (hasLoops)
            {
                EdgeLoop loop = lps[0];

                switch (type)
                {
                case HoleFillerType.Planar:
                    outMesh = HoleFillMethods.PlanarFill(outMesh, loop, eLen);
                    break;

                case HoleFillerType.Smooth:
                    outMesh = HoleFillMethods.SmoothFill(outMesh, loop, eLen);
                    break;

                case HoleFillerType.Minimal:
                    outMesh = HoleFillMethods.MinimalFill(outMesh, loop, eLen);
                    break;

                default:
                    outMesh = HoleFillMethods.PlanarFill(outMesh, loop, eLen);
                    break;
                }

                loops = new MeshBoundaryLoops(outMesh, true);
                lps   = loops.Loops;

                hasLoops = (lps.Count > 0);

                iter++;
                if (iter > 500)
                {
                    break;
                }
            }

            this.Message += "\n" + iter.ToString() + " holes filled.";

            DA.SetData(0, outMesh);
        }
Example #7
0
        Polygon2d make_poly(DMesh3 mesh, IEnumerable <int> triangles)
        {
            DSubmesh3         submesh = new DSubmesh3(mesh, triangles);
            MeshBoundaryLoops loops   = new MeshBoundaryLoops(submesh.SubMesh);

            Util.gDevAssert(loops.Loops.Count == 1);
            return(make_poly(submesh.SubMesh, loops.Loops[0]));
        }
Example #8
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");
        }
        public static void compute_distance_image()
        {
            DMesh3            mesh  = StandardMeshReader.ReadMesh("c:\\scratch\\remesh.obj");
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
            DCurve3           curve = loops[0].ToCurve();
            Polygon2d         poly  = new Polygon2d();

            foreach (Vector3d v in curve.Vertices)
            {
                poly.AppendVertex(v.xy);
            }
            int      N        = 1024;
            double   cellsize = poly.Bounds.MaxDim / (double)N;
            Vector2d o        = poly.Bounds.Min;

            o -= 4 * cellsize * Vector2d.One;
            N += 8;

            ShiftGridIndexer2 indexer = new ShiftGridIndexer2(poly.Bounds.Min, cellsize);

            double[] df   = new double[N * N];
            double   maxd = 0;

            for (int yi = 0; yi < N; ++yi)
            {
                for (int xi = 0; xi < N; ++xi)
                {
                    Vector2d p = indexer.FromGrid(new Vector2i(xi, yi));
                    double   d = Math.Sqrt(poly.DistanceSquared(p));
                    df[yi * N + xi] = d;
                    maxd            = Math.Max(d, maxd);
                }
            }

            SKBitmap bmp = new SKBitmap(N, N);

            for (int yi = 0; yi < N; ++yi)
            {
                for (int xi = 0; xi < N; ++xi)
                {
                    double d = df[yi * N + xi];
                    float  f = (float)(d / maxd);
                    byte   b = (byte)(int)(f * 255);
                    bmp.SetPixel(xi, yi, new SKColor(b, b, b));
                }
            }

            using (var image = SKImage.FromBitmap(bmp))
                using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) {
                    // save the data to a stream
                    using (var stream = File.OpenWrite("c:\\scratch\\distances.png")) {
                        data.SaveTo(stream);
                    }
                }
        }
Example #10
0
        public static void PreserveBoundaryLoops(MeshConstraints cons, NGonsCore.geometry3Sharp.mesh.DMesh3 mesh)
        {
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);

            foreach (EdgeLoop loop in loops)
            {
                DCurve3 loopC = MeshUtil.ExtractLoopV(mesh, loop.Vertices);
                DCurveProjectionTarget target = new DCurveProjectionTarget(loopC);
                ConstrainVtxLoopTo(cons, mesh, loop.Vertices, target);
            }
        }
        void compute_inner_wall()
        {
            if (DebugStep <= 0)
            {
                SocketMesh = new DMesh3(TrimmedMesh);
                return;
            }

            InnerMesh = new DMesh3(TrimmedMesh);

            // compute flare band
            Func <int, double> flareOffsetF = (vid) => { return(0); };

            if (flare_offset > 0 && flare_band_width > 0)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(InnerMesh);
                if (loops.Count != 1)
                {
                    goto done_inner_wall;
                }

                DijkstraGraphDistance dist = DijkstraGraphDistance.MeshVertices(InnerMesh);
                dist.TrackOrder = true;
                foreach (int vid in loops[0].Vertices)
                {
                    dist.AddSeed(vid, 0);
                }
                dist.ComputeToMaxDistance(1.25f * (float)flare_band_width);

                flareOffsetF = (viD) => {
                    float d = dist.GetDistance(viD);
                    if (d < flare_band_width)
                    {
                        double t = d / flare_band_width;
                        t = 1 - t * t;
                        t = t * t * t;
                        return(t * flare_offset);
                    }
                    return(0);
                };
            }


            gParallel.ForEach(InnerMesh.VertexIndices(), (vid) => {
                Vector3d v    = InnerMesh.GetVertex(vid);
                Vector3d n    = InnerMesh.GetVertexNormal(vid);
                double offset = inner_offset + flareOffsetF(vid);
                InnerMesh.SetVertex(vid, v + offset * n);
            });


done_inner_wall:
            MeshNormals.QuickCompute(InnerMesh);
        }
Example #12
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            DMesh3_goo goo = null;

            DA.GetData(0, ref goo);

            DMesh3 mesh = new DMesh3(goo.Value);

            MeshBoundaryLoops bounds = new MeshBoundaryLoops(mesh, true);

            DA.SetDataList(0, bounds.Loops);
        }
        EdgeLoop select_loop_tris_hint(MeshBoundaryLoops loops)
        {
            var hint_edges = new HashSet <int>();

            foreach (int tid in BorderHintTris)
            {
                if (Mesh.IsTriangle(tid) == false)
                {
                    continue;
                }

                Index3i et = Mesh.GetTriEdges(tid);
                for (int j = 0; j < 3; ++j)
                {
                    if (Mesh.IsBoundaryEdge(et[j]))
                    {
                        hint_edges.Add(et[j]);
                    }
                }
            }


            int N         = loops.Count;
            int best_loop = -1;
            int max_votes = 0;

            for (int li = 0; li < N; ++li)
            {
                int      votes = 0;
                EdgeLoop l     = loops[li];
                foreach (int eid in l.Edges)
                {
                    if (hint_edges.Contains(eid))
                    {
                        votes++;
                    }
                }
                if (votes > max_votes)
                {
                    best_loop = li;
                    max_votes = votes;
                }
            }

            if (best_loop == -1)
            {
                return(null);
            }

            return(loops[best_loop]);
        }
        public static void test_mesh_boundary()
        {
            //List<int> cases = new List<int>() { 0, 1, 2, 3 };
            List <int> cases = new List <int>()
            {
                0, 1, 2
            };

            // TODO: currently this case fails to find a simple boundary loop.
            //List<int> cases = new List<int>() { 3 };

            foreach (int num in cases)
            {
                string name;
                DMesh3 mesh = MakeBoundaryTestMesh(num, out name);

                MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
            }
        }
Example #15
0
        public static void test_basic_fills()
        {
            // test trivial hole-fill

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

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

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

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

                bool bOK = true;
                foreach (EdgeLoop loop in loops.Loops)
                {
                    SimpleHoleFiller filler = new SimpleHoleFiller(mesh, loop);
                    Debug.Assert(filler.Validate() == ValidationStatus.Ok);
                    bOK = bOK && filler.Fill(1);
                    Debug.Assert(bOK);
                }
                System.Console.WriteLine("{0}", (bOK) ? "Ok" : "Failed");
                if (bOK)
                {
                    Debug.Assert(mesh.CachedIsClosed);
                }
                TestUtil.WriteTestOutputMesh(mesh, name + "_filled" + ".obj");
            }
        }         // test_basic_fills
Example #16
0
        public void Initialize(MeshBoundaryLoops loops)
        {
            if (CurveSet != null)
            {
                Clear();
            }
            CurveSet = new Curve[loops.Count];

            for (int i = 0; i < loops.Count; ++i)
            {
                Curve c = new Curve();
                c.id        = i;
                c.curve     = loops[i].ToCurve().ResampleSharpTurns();
                c.bounds    = c.curve.GetBoundingBox();
                c.spatial   = new DCurve3BoxTree(c.curve);
                c.visible   = true;
                CurveSet[i] = c;
            }
            invalidate_geometry();
        }
Example #17
0
        static GeneralPolygon2d GetPolygonFromMesh(string sPath)
        {
            DMesh3            mesh  = StandardMeshReader.ReadMesh(sPath);
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);

            PlanarComplex complex = new PlanarComplex();

            foreach (var loop in loops)
            {
                Polygon2d poly  = new Polygon2d();
                DCurve3   curve = MeshUtil.ExtractLoopV(mesh, loop.Vertices);
                foreach (Vector3d v in curve.Vertices)
                {
                    poly.AppendVertex(v.xy);
                }
                complex.Add(poly);
            }

            PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(0.0, false);
            return(solids.Polygons[0]);
        }
        public static void test_add_change()
        {
            DMesh3 testMesh = TestUtil.LoadTestInputMesh("bunny_open_base.obj");
            DMesh3 copy     = new DMesh3(testMesh);

            MeshBoundaryLoops loops = new MeshBoundaryLoops(copy);

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

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

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

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

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

            System.Console.WriteLine("test_add_change ok");
        }
Example #19
0
        /// <summary>
        /// this runs inside SO.SafeMeshRead
        /// </summary>
        private object LockedComputeMeshBoundaryData(DMesh3 mesh)
        {
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh, true);

            MeshBoundaryData bd = new MeshBoundaryData();

            foreach (EdgeLoop loop in loops.Loops)
            {
                bd.Loops.Add(loop.ToCurve());
            }
            if (loops.SawOpenSpans)
            {
                foreach (EdgeSpan span in loops.Spans)
                {
                    bd.Spans.Add(span.ToCurve());
                }
            }

            bd.timestamp = mesh.ShapeTimestamp;

            return(bd);
        }
Example #20
0
        private void FillAnyHoles(DMesh3 mesh,
                                  CancellationToken cancellationToken,
                                  out int nRemaining,
                                  out bool sawSpans)
        {
            var loops = new MeshBoundaryLoops(mesh);

            nRemaining = 0;
            sawSpans   = loops.SawOpenSpans;

            foreach (var loop in loops)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var  filler = new MinimalHoleFill(mesh, loop);
                bool filled = filler.Apply();
                if (filled == false)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var fallback = new SimpleHoleFiller(mesh, loop);
                    fallback.Fill();
                }
            }
        }
Example #21
0
        public static void test_smooth_fill()
        {
            //DMesh3 mesh = TestUtil.LoadTestInputMesh("n_holed_bunny.obj");
            DMesh3 mesh = TestUtil.LoadTestInputMesh("crazyhole.obj");
            double mine, maxe, avge;

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

            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);

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

            TestUtil.WriteTestOutputMesh(mesh, "smooth_fill_result.obj");
        }
Example #22
0
        void fill_trivial_holes(out int nRemaining, out bool saw_spans)
        {
            var loops = new MeshBoundaryLoops(Mesh);

            nRemaining = 0;
            saw_spans  = loops.SawOpenSpans;

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

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

                if (filled == false)
                {
                    ++nRemaining;
                }
            }
        }
Example #23
0
        protected void set_output_meshes(DMesh3 inner, DMesh3 outer)
        {
            InnerMesh = inner;
            OuterMesh = outer;

            AxisAlignedBox3d bounds = OuterMesh.CachedBounds;

            if (InnerMesh != null)
            {
                bounds.Contain(InnerMesh.CachedBounds);
            }

            // position center-top at origin
            Vector3d top = bounds.Center + bounds.Extents[1] * Vector3d.AxisY;

            if (InnerMesh != null)
            {
                MeshTransforms.Translate(InnerMesh, -top);
            }
            MeshTransforms.Translate(OuterMesh, -top);

            CombinedBounds = OuterMesh.CachedBounds;
            if (InnerMesh != null)
            {
                CombinedBounds.Contain(InnerMesh.CachedBounds);
            }

            if (InnerMesh != null)
            {
                var innerLoops = new MeshBoundaryLoops(InnerMesh);
                InnerLoop = innerLoops[0];
            }
            var outerLoops = new MeshBoundaryLoops(OuterMesh);

            OuterLoop = outerLoops[0];
        }
Example #24
0
        public void FillAllHoles()
        {
            if (IsComputing)
            {
                return;
            }

            is_computing = true;

            List <EdgeLoop> cur_loops = new List <EdgeLoop>(Loops.Loops);

            Task.Run((Action)(() => {
                var use_fill_type = FillType;
                DMesh3 filled_mesh = (DMesh3)previewSO.SafeMeshRead((mesh) => { return(new DMesh3(mesh)); });

                try_fills_again:
                if (cancel_background_task)
                {
                    return;
                }

                foreach (EdgeLoop loop in cur_loops)
                {
                    if (cancel_background_task)
                    {
                        return;
                    }
                    try {
                        // skip loops that have been filled
                        if (loop.IsBoundaryLoop(filled_mesh) == false)
                        {
                            continue;
                        }

                        switch (use_fill_type)
                        {
                        case FillTypes.Trivial: fill_trivial(filled_mesh, loop); break;

                        case FillTypes.Minimal: fill_minimal(filled_mesh, loop); break;

                        case FillTypes.Smooth: fill_smooth(filled_mesh, loop); break;
                        }
                    } catch (Exception e) {
                        DebugUtil.Log("FillHolesTool.FillAllHoles: Exception: " + e.Message);
                        // ignore-and-continue for now
                    }
                }

                // recompute loops in case any failed
                MeshBoundaryLoops loops = new MeshBoundaryLoops(filled_mesh, true);

                // if any did fail, fall back to simpler fills in second pass
                if (loops.Count > 0 && use_fill_type != FillTypes.Trivial)
                {
                    if (use_fill_type == FillTypes.Smooth)
                    {
                        use_fill_type = FillTypes.Minimal;
                    }
                    else
                    {
                        use_fill_type = FillTypes.Trivial;
                    }
                    cur_loops = new List <EdgeLoop>(loops.Loops);
                    goto try_fills_again;
                }

                if (cancel_background_task)
                {
                    return;
                }

                ReplaceEntireMeshChange change = new ReplaceEntireMeshChange(previewSO,
                                                                             previewSO.Mesh, filled_mesh);
                ThreadMailbox.PostToMainThread((Action)(() => {
                    this.Scene.History.PushChange(change, false);
                    this.Scene.History.PushInteractionCheckpoint();
                }));

                is_computing = false;
            }));  // end task
        }
Example #25
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);
        }
Example #26
0
        public static void test_read_thingi10k()
        {
            //const string THINGIROOT = "D:\\meshes\\Thingi10K\\raw_meshes\\";
            const string THINGIROOT = "F:\\Thingi10K\\raw_meshes\\";

            string[] files = Directory.GetFiles(THINGIROOT);

            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>();

            int k = 0;

            gParallel.ForEach(files, (filename) => {
                int i = k;
                Interlocked.Increment(ref k);
                System.Console.WriteLine("{0} : {1}", i, 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);
                    failures.SafeAdd(filename);
                    return;
                }

                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;
                    }

                    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 (is_empty)
                {
                    empty.SafeAdd(filename);
                }
                else if (is_open)
                {
                    open.SafeAdd(filename);
                    if (loops_failed)
                    {
                        boundaries_failed.SafeAdd(filename);
                    }
                }
                else
                {
                    closed.SafeAdd(filename);
                }
            });


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

            TestUtil.WriteTestOutputStrings(empty.List.ToArray(), "thingi10k_empty.txt");
            TestUtil.WriteTestOutputStrings(closed.List.ToArray(), "thingi10k_closed.txt");
            TestUtil.WriteTestOutputStrings(open.List.ToArray(), "thingi10k_open.txt");
            TestUtil.WriteTestOutputStrings(boundaries_failed.List.ToArray(), "thingi10k_boundaries_failed.txt");
        }
        public virtual bool Apply()
        {
            DMesh3 testAgainstMesh = Mesh;

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

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

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

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

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

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

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

            bool cancel = false;

            BitArray vertices = null;

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

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

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

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

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

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

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

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

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

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

            return(true);
        }
        public static void test_tube_generator()
        {
            Polygon2d  circle_path = Polygon2d.MakeCircle(50, 64);
            PolyLine2d arc_path    = new PolyLine2d(circle_path.Vertices.Take(circle_path.VertexCount / 2));
            Polygon2d  irreg_path  = new Polygon2d();

            for (int k = 0; k < circle_path.VertexCount; ++k)
            {
                irreg_path.AppendVertex(circle_path[k]);
                k += k / 2;
            }
            PolyLine2d irreg_arc_path = new PolyLine2d(irreg_path.Vertices.Take(circle_path.VertexCount - 1));

            Polygon2d square_profile = Polygon2d.MakeCircle(7, 32);

            square_profile.Translate(4 * Vector2d.One);
            //square_profile[0] = 20 * square_profile[0].Normalized;

            bool no_shared = true;

            WriteGeneratedMesh(
                new TubeGenerator(circle_path, Frame3f.Identity, square_profile)
            {
                WantUVs = true, NoSharedVertices = no_shared
            },
                "tubegen_loop_standarduv.obj");

            WriteGeneratedMesh(
                new TubeGenerator(irreg_path, Frame3f.Identity, square_profile)
            {
                WantUVs = true, NoSharedVertices = no_shared
            },
                "tubegen_irregloop_standarduv.obj");

            WriteGeneratedMesh(
                new TubeGenerator(arc_path, Frame3f.Identity, square_profile)
            {
                WantUVs = true, NoSharedVertices = no_shared
            },
                "tubegen_arc_standarduv.obj");

            WriteGeneratedMesh(
                new TubeGenerator(irreg_arc_path, Frame3f.Identity, square_profile)
            {
                WantUVs = true, NoSharedVertices = no_shared
            },
                "tubegen_irregarc_standarduv.obj");



            // append tube border around each hole of input mesh
            DMesh3            inMesh   = TestUtil.LoadTestInputMesh("n_holed_bunny.obj");
            Polygon2d         bdrycirc = Polygon2d.MakeCircle(0.25, 6);
            MeshBoundaryLoops loops    = new MeshBoundaryLoops(inMesh);

            foreach (EdgeLoop loop in loops)
            {
                DCurve3       curve = loop.ToCurve().ResampleSharpTurns();
                TubeGenerator gen   = new TubeGenerator(curve, bdrycirc)
                {
                    NoSharedVertices = false
                };
                MeshEditor.Append(inMesh, gen.Generate().MakeDMesh());
            }
            TestUtil.WriteTestOutputMesh(inMesh, "boundary_tubes.obj");
        }
        public static void test_marching_cubes_topology()
        {
            AxisAlignedBox3d bounds = new AxisAlignedBox3d(1.0);
            int    numcells         = 64;
            double cellsize         = bounds.MaxDim / numcells;

            Random r = new Random(31337);

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

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

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

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

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

                c.Generate();

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

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

            //c.Mesh.CheckValidity(false);


            //TestUtil.WriteTestOutputMesh(c.Mesh, "marching_cubes_topotest.obj");
        }
Example #30
0
        public override DMesh3 Cut(CuttingInfo info)
        {
            var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum);

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

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

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

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

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

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

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

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

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

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

            InstantiateNewObjects(info, subMeshes);


            return(info.mesh);
        }