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); }
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(); } } }
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; }
InteractiveRemesher make_remesher(DMesh3 mesh) { var m = new InteractiveRemesher(mesh); m.PreventNormalFlips = true; double mine, maxe, avge; MeshQueries.EdgeLengthStats(mesh, out mine, out avge, out maxe); m.SetTargetEdgeLength(avge * EdgeLengthMultiplier); m.SmoothSpeedT = SmoothSpeed; if (Reproject) { m.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); } if (RemeshBoundary) { MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh); int k = 1; foreach (var loop in loops) { MeshConstraintUtil.ConstrainVtxLoopTo(m, loop.Vertices, new DCurveProjectionTarget(loop.ToCurve()), k++); } } else if (PreserveBoundary) { MeshConstraintUtil.FixAllBoundaryEdges(m); } return(m); }
public 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); }
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); }
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])); }
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); } } }
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); }
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); } }
public static void test_basic_fills() { // test trivial hole-fill //List<int> tests = new List<int>() { 0, 1 }; List <int> tests = new List <int>() { 2 }; foreach (int num_test in tests) { string name; DMesh3 mesh = MakeEditTestMesh(num_test, out name); mesh.EnableTriangleGroups(); MeshBoundaryLoops loops; try { loops = new MeshBoundaryLoops(mesh); Debug.Assert(loops.Loops.Count > 0); } catch (Exception) { System.Console.WriteLine("failed to extract boundary loops for " + name); continue; } System.Console.WriteLine("Closing " + name + " - {0} holes", loops.Loops.Count); bool bOK = true; foreach (EdgeLoop loop in loops.Loops) { SimpleHoleFiller filler = new SimpleHoleFiller(mesh, loop); Debug.Assert(filler.Validate() == ValidationStatus.Ok); bOK = bOK && filler.Fill(1); Debug.Assert(bOK); } System.Console.WriteLine("{0}", (bOK) ? "Ok" : "Failed"); if (bOK) { Debug.Assert(mesh.CachedIsClosed); } TestUtil.WriteTestOutputMesh(mesh, name + "_filled" + ".obj"); } } // test_basic_fills
public 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(); }
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"); }
/// <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); }
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(); } } }
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"); }
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; } } }
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]; }
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 }
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); }
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"); }
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); }