/// <summary> /// Return true if the collapse results in a valid mesh (otherwise normals may be flipped) /// Return false if boundary face /// </summary> /// <returns></returns> public bool Collapse() { var hm = hmesh; var hes = Circulate(); var center = GetCenter(); Halfedge opp = null; var firstVert = hes[0].vert; List <Vector3D> potentialCollapsedPositions = new List <Vector3D>(); // faces that share edges with this face List <Face> adjacentFaces = new List <Face>(); potentialCollapsedPositions.Add(center); foreach (var he in hes) { potentialCollapsedPositions.Add(he.vert.positionD); if (he.opp != null) { adjacentFaces.Add(he.opp.face); } if (he.vert.IsBoundary()) { // cannot collapse boundary return(false); } } adjacentFaces.Add(this); // faces that are affected by a collapse and their associated face normal Dictionary <Face, Vector3D> affectedFaceNormals = new Dictionary <Face, Vector3D>(); foreach (var he in hes) { if (!he.vert.IsBoundary()) { foreach (var halfedge in he.vert.Circulate()) { var face = halfedge.face; if (!adjacentFaces.Contains(face)) { affectedFaceNormals.Add(face, face.GetNormal()); } } } } // prepare removal/disconnect of face foreach (var he in hes) { if (he.opp != null) { opp = he.opp; he.opp.opp = null; // remove reference to opp } } Vertex survivingVertes = null; foreach (var he in hes) { hm.Destroy(he); } hm.Destroy(this); if (opp != null) { var vert = opp.CollapseBoundaryLoop(); bool validPosition = true; // find position where adjacent faces are not flipped for (int i = 0; i < potentialCollapsedPositions.Count; i++) { vert.positionD = potentialCollapsedPositions[i]; validPosition = true; foreach (var faceNormal in affectedFaceNormals) { var newNormal = faceNormal.Key.GetNormal(); bool isNormalFlipped = Vector3D.Dot(newNormal, faceNormal.Value) < 0; if (isNormalFlipped) { validPosition = false; } } if (validPosition) { return(true); } bool didNotSucceedInFindingValidPosition = (i == potentialCollapsedPositions.Count); if (didNotSucceedInFindingValidPosition) { // assume center position has least damage vert.positionD = potentialCollapsedPositions[i]; } } } return(false); }