public void Flip() { #if UNITY_EDITOR if (IsBoundary()) { Debug.LogError("Cannot flip boundary edge"); return; } if (face.NoEdges() != 3 || opp.face.NoEdges() != 3) { Debug.LogError("Can only flip edge between two triangles"); } Debug.Assert(hmesh.IsValid()); #endif Halfedge oldNext = next; Halfedge oldPrev = prev; Halfedge oldOppNext = opp.next; Halfedge oldOppPrev = opp.prev; Vertex thisVert = vert; Vertex oppVert = opp.vert; Vertex thisOppositeVert = next.vert; Vertex oppOppositeVert = opp.next.vert; // flip halfedges oldOppNext.Link(this); this.Link(oldPrev); oldNext.Link(opp); opp.Link(oldOppPrev); oldOppPrev.Link(oldNext); oldPrev.Link(oldOppNext); // reassign vertices this.vert = thisOppositeVert; thisOppositeVert.halfedge = oldPrev; opp.vert = oppOppositeVert; oppOppositeVert.halfedge = oldOppPrev; thisOppositeVert.halfedge = oldPrev; thisVert.halfedge = oldNext; oppVert.halfedge = oldOppNext; face.halfedge = this; face.ReassignFaceToEdgeLoop(); opp.face.halfedge = opp; opp.face.ReassignFaceToEdgeLoop(); #if UNITY_EDITOR Debug.Assert(hmesh.IsValid()); #endif }
public HMesh Triangulate(bool step = false) { if (step) { var faces = new List <Face>(hmesh.GetFacesRaw()); faces.Sort((face, face1) => { return(-face.Circulate().Count + face1.Circulate().Count); }); Debug.Log("Triangulate " + faces[0]); faces[0].Triangulate(true); } else { hmesh.Triangulate(step); } Debug.Log("Valid hmesh " + hmesh.IsValid()); return(hmesh); }
// return the number of collapsed faces public static int SimplifyByCollapse(HMesh hmesh, int maxIter = int.MaxValue) { List <Halfedge> faceLabelBoundary = new List <Halfedge>(); int collapsed = 0; bool changed; int iter = 0; int collapsedEven = 0; do { iter++; changed = false; // collapse he if vertex contains two parallel halfedges, which separates labels or is boundary) foreach (var vert in hmesh.GetVertices()) { if (vert.IsDestroyed()) { continue; } if (vert.IsBoundary()) { // boundary collapses should be handled by DissolveUnneededBoundaryVertices continue; } faceLabelBoundary.Clear(); var vertCirculate = vert.Circulate(); foreach (var he in vertCirculate) { if (he.face.label != he.opp.face.label) { faceLabelBoundary.Add(he); } } if (faceLabelBoundary.Count == 1) { Debug.Assert(false, "Cannot have a single face label boundary (without the vertex is a boundary"); } else if (faceLabelBoundary.Count == 2) { var dirs = new[] { faceLabelBoundary[0].GetDirection(), faceLabelBoundary[1].GetDirection() }; var line = new LineSegment(faceLabelBoundary[0].vert.positionD, faceLabelBoundary[1].vert.positionD); double distance = line.DistancePoint(faceLabelBoundary[0].prev.vert.positionD); double distThreshold = hmesh.zeroMagnitudeTreshold; var dot = Vector3D.Dot(dirs[0], dirs[1]); bool sameDir = dot < 0.0; if (distance < distThreshold && sameDir) { var position = faceLabelBoundary[0].vert.positionD; if (IsFacesOnSamePlane(hmesh, vertCirculate, faceLabelBoundary[0].face.label) && IsFacesOnSamePlane(hmesh, vertCirculate, faceLabelBoundary[1].face.label)) { if (PreconditionLegalCollapse(faceLabelBoundary[0], position)) { //if (PreconditionLegalCollapse(faceLabelBoundary[0])){ var newVertex = faceLabelBoundary[0].Collapse(faceLabelBoundary[0].vert.positionD); if (newVertex != null) { newVertex.positionD = position; } collapsed++; changed = true; #if HMDebug if (!hmesh.IsValid()) { Debug.Log("Invalid"); } #endif if (debugOneStep) { return(-1); } } else if (PreconditionLegalCollapse(faceLabelBoundary[1], position)) { var newVertex = faceLabelBoundary[1].Collapse(faceLabelBoundary[1].vert.positionD); if (newVertex != null) { newVertex.positionD = position; } collapsed++; changed = true; #if HMDebug if (!hmesh.IsValid()) { Debug.Log("Invalid"); } #endif if (debugOneStep) { return(-1); } } } } } else if (faceLabelBoundary.Count == 0) { if (IsFacesOnSamePlane(hmesh, vertCirculate)) { // search for which to collapse (should not result in flipped vectors) for (int i = 0; i < vertCirculate.Count; i++) { #if HMDebug if (!hmesh.IsValid()) { Debug.LogError("PreInvalid"); } #endif if (PreconditionLegalCollapse(vertCirculate[i], vertCirculate[i].vert.positionD)) { var collapseStr = vertCirculate[i].ToString(); var position = vertCirculate[i].vert.positionD; var newVertex = vertCirculate[i].Collapse(); collapsed++; newVertex.positionD = position; #if HMDebug if (!hmesh.IsValid()) { Debug.Log("Invalid"); Debug.LogError(collapseStr); } #endif i = int.MaxValue - 1; if (debugOneStep) { return(-1); } break; } } } } } } while (changed && iter < maxIter); return(collapsed); }
// <summary> // Collapses boundary edges, such any point of the shape does not move more than (approx) hmesh.zeroMagnitudeTreshold. // </summary> public static int DissolveUnneededBoundaryVertices(HMesh hmesh, bool keepFaceLabels = true) { Debug.Assert(hmesh.IsValid(HMeshValidationRules.All), "Precondition failed: Mesh should be valid"); List <Halfedge> boundaryHalfedges = new List <Halfedge>(); var count = 0; var verts = new List <Vertex>(hmesh.GetVerticesRaw()); foreach (var v in verts) { if (v.IsDestroyed()) { continue; } if (v.IsBoundary()) { var hes = v.Circulate(); var heOpps = v.CirculateOpp(); boundaryHalfedges.Clear(); bool isSameLabel = true; foreach (var he in hes) { if (keepFaceLabels && he.face.label != hes[0].face.label) { isSameLabel = false; break; } if (he.IsBoundary()) { boundaryHalfedges.Add(he); } } foreach (var he in heOpps) { if (keepFaceLabels && he.face.label != hes[0].face.label) { isSameLabel = false; break; } if (he.IsBoundary()) { boundaryHalfedges.Add(he); } } if (!isSameLabel) { continue; } double largeThreshold = hmesh.zeroMagnitudeTresholdSqr; if (!Vector3D.IsParallelDist(boundaryHalfedges[0].GetDirection(), boundaryHalfedges[1].GetDirection(), largeThreshold)) { continue; } // find face plane that are not degenerate bool facesAreOnSamePlane = IsFacesOnSamePlane(hmesh, hes); if (!facesAreOnSamePlane) { continue; } LineSegment virtuelEdge = new LineSegment( boundaryHalfedges[0].vert.positionD, boundaryHalfedges[1].prev.vert.positionD ); if (virtuelEdge.DistancePoint(v.positionD) < hmesh.zeroMagnitudeTreshold) { boundaryHalfedges[0].Collapse(boundaryHalfedges[0].vert.positionD); count++; } } } return(count); }