public virtual bool Trim() { if (Spatial == null) { Spatial = new DMeshAABBTree3(new DMesh3(Mesh, false, MeshComponents.None)); Spatial.Build(); } if (seed_tri == -1) { seed_tri = Spatial.FindNearestTriangle(seed_pt); } var loop = new MeshFacesFromLoop(Mesh, TrimLine, Spatial, seed_tri); MeshFaceSelection selection = loop.ToSelection(); selection.LocalOptimize(true, true); var editor = new MeshEditor(Mesh); editor.RemoveTriangles(selection, true); var components = new MeshConnectedComponents(Mesh); components.FindConnectedT(); if (components.Count > 1) { int keep = components.LargestByCount; for (int i = 0; i < components.Count; ++i) { if (i != keep) { editor.RemoveTriangles(components[i].Indices, true); } } } editor.RemoveAllBowtieVertices(true); var loops = new MeshBoundaryLoops(Mesh); bool loopsOK = false; try { loopsOK = loops.Compute(); } catch (Exception) { return(false); } if (!loopsOK) { return(false); } // [TODO] to support trimming mesh w/ existing holes, we need to figure out which // loop we created in RemoveTriangles above! if (loops.Count > 1) { return(false); } int[] loopVerts = loops[0].Vertices; var borderTris = new MeshFaceSelection(Mesh); borderTris.SelectVertexOneRings(loopVerts); borderTris.ExpandToOneRingNeighbours(RemeshBorderRings); var remesh = new RegionRemesher(Mesh, borderTris.ToArray()); remesh.Region.MapVerticesToSubmesh(loopVerts); double target_len = TargetEdgeLength; if (target_len <= 0) { double mine, maxe, avge; MeshQueries.EdgeLengthStatsFromEdges(Mesh, loops[0].Edges, out mine, out maxe, out avge); target_len = avge; } var meshTarget = new MeshProjectionTarget(Spatial.Mesh, Spatial); remesh.SetProjectionTarget(meshTarget); remesh.SetTargetEdgeLength(target_len); remesh.SmoothSpeedT = SmoothingAlpha; var curveTarget = new DCurveProjectionTarget(TrimLine); var multiTarget = new SequentialProjectionTarget(curveTarget, meshTarget); int set_id = 3; MeshConstraintUtil.ConstrainVtxLoopTo(remesh, loopVerts, multiTarget, set_id); for (int i = 0; i < RemeshRounds; ++i) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); // [TODO] output loop somehow...use MeshConstraints.FindConstrainedEdgesBySetID(set_id)... return(true); } // Trim()
public virtual bool Cut() { double invalidDist = double.MinValue; MeshEdgeSelection CutEdgeSet = null; MeshVertexSelection CutVertexSet = null; if (CutFaceSet != null) { CutEdgeSet = new MeshEdgeSelection(Mesh, CutFaceSet); CutVertexSet = new MeshVertexSelection(Mesh, CutEdgeSet); } // compute signs int MaxVID = Mesh.MaxVertexID; double[] signs = new double[MaxVID]; gParallel.ForEach(Interval1i.Range(MaxVID), (vid) => { if (Mesh.IsVertex(vid)) { Vector3d v = Mesh.GetVertex(vid); signs[vid] = (v - PlaneOrigin).Dot(PlaneNormal); } else { signs[vid] = invalidDist; } }); HashSet <int> ZeroEdges = new HashSet <int>(); HashSet <int> ZeroVertices = new HashSet <int>(); HashSet <int> OnCutEdges = new HashSet <int>(); // have to skip processing of new edges. If edge id // is > max at start, is new. Otherwise if in NewEdges list, also new. int MaxEID = Mesh.MaxEdgeID; HashSet <int> NewEdges = new HashSet <int>(); IEnumerable <int> edgeItr = Interval1i.Range(MaxEID); if (CutEdgeSet != null) { edgeItr = CutEdgeSet; } // cut existing edges with plane, using edge split foreach (int eid in edgeItr) { if (Mesh.IsEdge(eid) == false) { continue; } if (eid >= MaxEID || NewEdges.Contains(eid)) { continue; } Index2i ev = Mesh.GetEdgeV(eid); double f0 = signs[ev.a]; double f1 = signs[ev.b]; // If both signs are 0, this edge is on-contour // If one sign is 0, that vertex is on-contour int n0 = (Math.Abs(f0) < MathUtil.Epsilon) ? 1 : 0; int n1 = (Math.Abs(f1) < MathUtil.Epsilon) ? 1 : 0; if (n0 + n1 > 0) { if (n0 + n1 == 2) { ZeroEdges.Add(eid); } else { ZeroVertices.Add((n0 == 1) ? ev[0] : ev[1]); } continue; } // no crossing if (f0 * f1 > 0) { continue; } DMesh3.EdgeSplitInfo splitInfo; MeshResult result = Mesh.SplitEdge(eid, out splitInfo); if (result != MeshResult.Ok) { throw new Exception("MeshPlaneCut.Cut: failed in SplitEdge"); //return false; } // SplitEdge just bisects edge - use plane intersection instead double t = f0 / (f0 - f1); Vector3d newPos = (1 - t) * Mesh.GetVertex(ev.a) + (t) * Mesh.GetVertex(ev.b); Mesh.SetVertex(splitInfo.vNew, newPos); NewEdges.Add(splitInfo.eNewBN); NewEdges.Add(splitInfo.eNewCN); OnCutEdges.Add(splitInfo.eNewCN); if (splitInfo.eNewDN != DMesh3.InvalidID) { NewEdges.Add(splitInfo.eNewDN); OnCutEdges.Add(splitInfo.eNewDN); } } // remove one-rings of all positive-side vertices. IEnumerable <int> vertexSet = Interval1i.Range(MaxVID); if (CutVertexSet != null) { vertexSet = CutVertexSet; } foreach (int vid in vertexSet) { if (signs[vid] > 0 && Mesh.IsVertex(vid)) { Mesh.RemoveVertex(vid, true, false); } } // ok now we extract boundary loops, but restricted // to either the zero-edges we found, or the edges we created! bang!! Func <int, bool> CutEdgeFilterF = (eid) => { if (OnCutEdges.Contains(eid) || ZeroEdges.Contains(eid)) { return(true); } return(false); }; try { MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, false); loops.EdgeFilterF = CutEdgeFilterF; loops.Compute(); CutLoops = loops.Loops; CutSpans = loops.Spans; CutLoopsFailed = false; FoundOpenSpans = CutSpans.Count > 0; } catch { CutLoops = new List <EdgeLoop>(); CutLoopsFailed = true; } return(true); } // Cut()