public Mesh Repair(Mesh sourceMesh, CancellationToken cancellationToken) { var inMesh = sourceMesh; try { if (WeldVertices) { inMesh = sourceMesh.Copy(cancellationToken); if (WeldTolerance > 0) { inMesh.MergeVertices(.01); } else { inMesh.CleanAndMerge(); } if (!FaceOrientation && RemoveMode == RemoveModes.None && !WeldEdges && !FillHoles) { return(inMesh); } } var mesh = inMesh.ToDMesh3(); int repeatCount = 0; int erosionIterations = 5; double repairTolerance = MathUtil.ZeroTolerancef; double minEdgeLengthTol = 0.0001; repeat_all: if (FaceOrientation) { // make sure orientation of connected components is consistent // TODO: what about mobius strip problems? RepairOrientation(mesh, cancellationToken, true); } if (RemoveMode != RemoveModes.None) { // Remove parts of the mesh we don't want before we bother with anything else // TODO: maybe we need to repair orientation first? if we want to use MWN (MeshWindingNumber)... RemoveInside(mesh); cancellationToken.ThrowIfCancellationRequested(); } if (WeldEdges || FillHoles) { // Do safe close-cracks to handle easy cases RepairCracks(mesh, true, repairTolerance); if (mesh.IsClosed()) { goto all_done; } cancellationToken.ThrowIfCancellationRequested(); // Collapse tiny edges and then try easy cases again, and // then allow for handling of ambiguous cases CollapseAllDegenerateEdges(mesh, cancellationToken, repairTolerance * 0.5, true); cancellationToken.ThrowIfCancellationRequested(); RepairCracks(mesh, true, 2 * repairTolerance); cancellationToken.ThrowIfCancellationRequested(); RepairCracks(mesh, false, 2 * repairTolerance); cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // Possibly we have joined regions with different orientation (is it?), fix that // TODO: mobius strips again RepairOrientation(mesh, cancellationToken, true); cancellationToken.ThrowIfCancellationRequested(); // get rid of any remaining single-triangles before we start filling holes MeshEditor.RemoveIsolatedTriangles(mesh); } if (FillHoles) { // Ok, fill simple holes. int nRemainingBowties = 0; FillTrivialHoles(mesh, cancellationToken, out int nHoles, out bool bSawSpans); cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // Now fill harder holes. If we saw spans, that means boundary loops could // not be resolved in some cases, do we disconnect bowties and try again. FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans); cancellationToken.ThrowIfCancellationRequested(); if (bSawSpans) { DisconnectBowties(mesh, out nRemainingBowties); FillAnyHoles(mesh, cancellationToken, out nHoles, out bSawSpans); } cancellationToken.ThrowIfCancellationRequested(); if (mesh.IsClosed()) { goto all_done; } // We may have a closed mesh now but it might still have bowties (eg // tetrahedra sharing vtx case). So disconnect those. DisconnectBowties(mesh, out nRemainingBowties); cancellationToken.ThrowIfCancellationRequested(); // If the mesh is not closed, we will do one more round to try again. if (repeatCount == 0 && mesh.IsClosed() == false) { repeatCount++; goto repeat_all; } // Ok, we didn't get anywhere on our first repeat. If we are still not // closed, we will try deleting boundary triangles and repeating. // Repeat this N times. if (repeatCount <= erosionIterations && mesh.IsClosed() == false) { repeatCount++; var bdry_faces = new MeshFaceSelection(mesh); foreach (int eid in MeshIterators.BoundaryEdges(mesh)) { bdry_faces.SelectEdgeTris(eid); } MeshEditor.RemoveTriangles(mesh, bdry_faces, true); goto repeat_all; } } all_done: // and do a final clean up of the model if (FillHoles) { // Remove tiny edges if (minEdgeLengthTol > 0) { CollapseAllDegenerateEdges(mesh, cancellationToken, minEdgeLengthTol, false); } cancellationToken.ThrowIfCancellationRequested(); // finally do global orientation RepairOrientation(mesh, cancellationToken, true); cancellationToken.ThrowIfCancellationRequested(); } return(mesh.ToMesh()); } catch (OperationCanceledException) { return(inMesh); } }
public bool Apply() { bool do_checks = false; if (do_checks) { Mesh.CheckValidity(); } /* * Remove parts of the mesh we don't want before we bother with anything else * TODO: maybe we need to repair orientation first? if we want to use MWN... */ do_remove_inside(); if (Cancelled()) { return(false); } int repeat_count = 0; repeat_all: /* * make sure orientation of connected components is consistent * TODO: what about mobius strip problems? */ repair_orientation(false); if (Cancelled()) { return(false); } /* * Do safe close-cracks to handle easy cases */ repair_cracks(true, RepairTolerance); if (Mesh.IsClosed()) { goto all_done; } if (Cancelled()) { return(false); } /* * Collapse tiny edges and then try easy cases again, and * then allow for handling of ambiguous cases */ collapse_all_degenerate_edges(RepairTolerance * 0.5, true); if (Cancelled()) { return(false); } repair_cracks(true, 2 * RepairTolerance); if (Cancelled()) { return(false); } repair_cracks(false, 2 * RepairTolerance); if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * Possibly we have joined regions with different orientation (is it?), fix that * TODO: mobius strips again */ repair_orientation(false); if (Cancelled()) { return(false); } if (do_checks) { Mesh.CheckValidity(); } // get rid of any remaining single-triangles before we start filling holes remove_loners(); /* * Ok, fill simple holes. */ int nRemainingBowties = 0; int nHoles; bool bSawSpans; fill_trivial_holes(out nHoles, out bSawSpans); if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * Now fill harder holes. If we saw spans, that means boundary loops could * not be resolved in some cases, do we disconnect bowties and try again. */ fill_any_holes(out nHoles, out bSawSpans); if (Cancelled()) { return(false); } if (bSawSpans) { disconnect_bowties(out nRemainingBowties); fill_any_holes(out nHoles, out bSawSpans); } if (Cancelled()) { return(false); } if (Mesh.IsClosed()) { goto all_done; } /* * We may have a closed mesh now but it might still have bowties (eg * tetrahedra sharing vtx case). So disconnect those. */ disconnect_bowties(out nRemainingBowties); if (Cancelled()) { return(false); } /* * If the mesh is not closed, we will do one more round to try again. */ if (repeat_count == 0 && Mesh.IsClosed() == false) { repeat_count++; goto repeat_all; } /* * Ok, we didn't get anywhere on our first repeat. If we are still not * closed, we will try deleting boundary triangles and repeating. * Repeat this N times. */ if (repeat_count <= ErosionIterations && Mesh.IsClosed() == false) { repeat_count++; MeshFaceSelection bdry_faces = new MeshFaceSelection(Mesh); foreach (int eid in MeshIterators.BoundaryEdges(Mesh)) { bdry_faces.SelectEdgeTris(eid); } MeshEditor.RemoveTriangles(Mesh, bdry_faces, true); goto repeat_all; } all_done: /* * Remove tiny edges */ if (MinEdgeLengthTol > 0) { collapse_all_degenerate_edges(MinEdgeLengthTol, false); } if (Cancelled()) { return(false); } /* * finally do global orientation */ repair_orientation(true); if (Cancelled()) { return(false); } if (do_checks) { Mesh.CheckValidity(); } /* * Might as well compact output mesh... */ Mesh = new DMesh3(Mesh, true); MeshNormals.QuickCompute(Mesh); return(true); }