// // -----o------ // // Remove boundary vertices with two edge (one ingoing + one outgoing) where edges are parallel // Remove non-boundary vertices with only two adjacent faces where edges are parallel // // The method undo edge split by removing the extra vertex public static int DissolveUnneededVertices(HMesh hmesh) { int count = 0; foreach (var vert in hmesh.GetVertices()) { var circ = vert.Circulate(); double parallelEpsilon = 1E-15; if (vert.IsBoundary() && circ.Count == 1 && VertexDistOnDissolve(vert, circ[0].vert, circ[0].prev.prev.vert) < hmesh.zeroMagnitudeTreshold) { vert.Dissolve(); count++; } else if (!vert.IsBoundary() && circ.Count == 2) { if (VertexDistOnDissolve(vert, circ[0].vert, circ[1].vert) < hmesh.zeroMagnitudeTreshold) { vert.Dissolve(); count++; } } } return(count); }
public void ApplyTransform() { var localToWorld = transform.localToWorldMatrix; foreach (var vert in hmesh.GetVertices()) { vert.position = localToWorld.MultiplyPoint(vert.position); } transform.position = Vector3.zero; transform.localScale = Vector3.one; transform.rotation = Quaternion.identity; }
void OnGUI() { if (GUI.Button(new Rect(0,0,200,50), "FaceSplit")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); foreach (var f in hMesh.GetFaces()){ f.Split(); } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(200,0,200,50), "EdgeFlip")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); HashSet<Halfedge> isFlipped = new HashSet<Halfedge>(); foreach (var h in hMesh.GetHalfedges()){ if (!h.IsBoundary() && !isFlipped.Contains(h)){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); h.Flip(); isFlipped.Add(h.opp); } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(400,0,200,50), "EdgeSplit")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); HashSet<Halfedge> isSplit = new HashSet<Halfedge>(); foreach (var h in hMesh.GetHalfedges()){ if (!h.IsBoundary() && !isSplit.Contains(h)){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); isSplit.Add(h.opp); h.Split(); } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(600,0,200,50), "CollapseEdge")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); foreach (var h in hMesh.GetHalfedges()){ if (!h.IsBoundary()){ if (!h.vert.IsBoundary() && !h.prev.vert.IsBoundary() && !isCollapsed.Contains(h)){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); isCollapsed.Add(h.opp); h.Collapse(); break; } } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(0,50,200,50), "CirculateRndVertex")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var vertices = new List<Vertex>(hMesh.GetVertices()); var vertex = vertices[1];//Random.Range(0,vertices.Count-1)]; foreach (var h in vertex.Circulate()){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); } Debug.Log("Circulate vertex: "+vertex.Circulate().Count); } if (GUI.Button(new Rect(200,50,200,50), "CirculateOppRndVertex")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); //HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var vertices = new List<Vertex>(hMesh.GetVertices()); var vertex = vertices[1];//Random.Range(0,vertices.Count-1)]; foreach (var h in vertex.CirculateOpp()){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); } Debug.Log("Circulate vertex: "+vertex.Circulate().Count); } if (GUI.Button(new Rect(400,50,200,50), "Circulate1RingRndVertex")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); // HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var vertices = new List<Vertex>(hMesh.GetVertices()); var vertex = vertices[Random.Range(0,vertices.Count-1)]; foreach (var h in vertex.CirculateOneRing()){ Debug.DrawLine(h.vert.position, h.prev.vert.position,Color.cyan,5); } Debug.Log("Circulate 1-ring: "+vertex.CirculateOneRing().Count); } if (GUI.Button(new Rect(600,50,200,50), "CirculateRndFace")){ HMesh hMesh = new HMesh(); var mf = GetComponent<MeshFilter>(); hMesh.Build(mf.mesh); HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var faces = new List<Face>(hMesh.GetFaces()); var face = faces[Random.Range(0,faces.Count-1)]; foreach (var h in face.Circulate()){ Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); } Debug.Log("Circulate face: "+face.Circulate().Count); } }
void OnGUI() { if (GUI.Button(new Rect(0, 0, 200, 50), "FaceSplit")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); foreach (var f in hMesh.GetFaces()) { f.Split(); } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(200, 0, 200, 50), "EdgeFlip")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); HashSet <Halfedge> isFlipped = new HashSet <Halfedge>(); foreach (var h in hMesh.GetHalfedges()) { if (!h.IsBoundary() && !isFlipped.Contains(h)) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); h.Flip(); isFlipped.Add(h.opp); } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(400, 0, 200, 50), "EdgeSplit")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); HashSet <Halfedge> isSplit = new HashSet <Halfedge>(); foreach (var h in hMesh.GetHalfedges()) { if (!h.IsBoundary() && !isSplit.Contains(h)) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); isSplit.Add(h.opp); h.Split(); } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(600, 0, 200, 50), "CollapseEdge")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); HashSet <Halfedge> isCollapsed = new HashSet <Halfedge>(); foreach (var h in hMesh.GetHalfedges()) { if (!h.IsBoundary()) { if (!h.vert.IsBoundary() && !h.prev.vert.IsBoundary() && !isCollapsed.Contains(h)) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); isCollapsed.Add(h.opp); h.Collapse(); break; } } } var mesh = hMesh.Export(); mesh.RecalculateNormals(); mf.mesh = mesh; } if (GUI.Button(new Rect(0, 50, 200, 50), "CirculateRndVertex")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); HashSet <Halfedge> isCollapsed = new HashSet <Halfedge>(); var vertices = new List <Vertex>(hMesh.GetVertices()); var vertex = vertices[1]; //Random.Range(0,vertices.Count-1)]; foreach (var h in vertex.Circulate()) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); } Debug.Log("Circulate vertex: " + vertex.Circulate().Count); } if (GUI.Button(new Rect(200, 50, 200, 50), "CirculateOppRndVertex")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); //HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var vertices = new List <Vertex>(hMesh.GetVertices()); var vertex = vertices[1]; //Random.Range(0,vertices.Count-1)]; foreach (var h in vertex.CirculateOpp()) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); } Debug.Log("Circulate vertex: " + vertex.Circulate().Count); } if (GUI.Button(new Rect(400, 50, 200, 50), "Circulate1RingRndVertex")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); // HashSet<Halfedge> isCollapsed = new HashSet<Halfedge>(); var vertices = new List <Vertex>(hMesh.GetVertices()); var vertex = vertices[Random.Range(0, vertices.Count - 1)]; foreach (var h in vertex.CirculateOneRing()) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); } Debug.Log("Circulate 1-ring: " + vertex.CirculateOneRing().Count); } if (GUI.Button(new Rect(600, 50, 200, 50), "CirculateRndFace")) { HMesh hMesh = new HMesh(); var mf = GetComponent <MeshFilter>(); hMesh.Build(mf.mesh); HashSet <Halfedge> isCollapsed = new HashSet <Halfedge>(); var faces = new List <Face>(hMesh.GetFaces()); var face = faces[Random.Range(0, faces.Count - 1)]; foreach (var h in face.Circulate()) { Debug.DrawLine(h.vert.position, h.prev.vert.position, Color.cyan, 5); } Debug.Log("Circulate face: " + face.Circulate().Count); } }
// 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); }