static Face FindTriangleWithinPoint(HMesh mesh, Vector3 pos) { foreach (var face in mesh.GetFaces()){ var edges = face.Circulate(); var p1 = edges[0].vert.position; var p2 = edges[1].vert.position; var p3 = edges[2].vert.position; Debug.LogWarning("Points "+p1+", "+p2+", "+p3); if (HMeshMath.LeftOf(p1,p2,pos) && HMeshMath.LeftOf(p2,p3,pos) && HMeshMath.LeftOf(p3,p1,pos)){ return face; } } Debug.LogWarning("Cannot find triangle"); return null; }
static Face FindTriangleWithinPoint(HMesh mesh, Vector3 pos) { foreach (var face in mesh.GetFaces()) { var edges = face.Circulate(); var p1 = edges[0].vert.position; var p2 = edges[1].vert.position; var p3 = edges[2].vert.position; Debug.LogWarning("Points " + p1 + ", " + p2 + ", " + p3); if (HMeshMath.LeftOf(p1, p2, pos) && HMeshMath.LeftOf(p2, p3, pos) && HMeshMath.LeftOf(p3, p1, pos)) { return(face); } } Debug.LogWarning("Cannot find triangle"); return(null); }
// Update is called once per frame public Mesh UpdateMesh() { Mesh mesh = GetMesh(); Debug.Log("UpdateMesh"); List <Vector3> lines = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <int> indices = new List <int>(); HMesh hmesh = this.hmesh; foreach (var face in hmesh.GetFaces()) { foreach (var edge in face.Circulate()) { if (renderType == HMeshRendererType.BoundaryEdges) { if (!edge.IsBoundary()) { continue; } } lines.Add(edge.prev.vert.position); lines.Add(edge.vert.position); normals.Add(Vector3.up); normals.Add(Vector3.up); indices.Add(indices.Count); indices.Add(indices.Count); } } mesh.Clear(); mesh.vertices = lines.ToArray(); mesh.normals = normals.ToArray(); var meshTopology = renderType == HMeshRendererType.Vertices ? MeshTopology.Points : MeshTopology.Lines; mesh.SetIndices(indices.ToArray(), meshTopology, 0); mesh.RecalculateBounds(); mesh.UploadMeshData(false); return(mesh); }
public static void RemoveFacesWithTwoEdges(HMesh hmesh) { var faces = new List <Face>(hmesh.GetFaces()); for (int i = 0; i < faces.Count; i++)// (var face in hmesh.GetFaces()) { var edges = faces[i].Circulate(); if (edges.Count == 2) { var opp1 = edges[0].opp; var opp2 = edges[1].opp; var vert1 = edges[0].vert; var vert2 = edges[1].vert; // reassign vertex halfedges to a he not to be destroyed Halfedge.Glue(opp1, opp2); hmesh.Destroy(faces[i]); hmesh.Destroy(edges[0]); hmesh.Destroy(edges[1]); } } }
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); } }
// Dissolves adjacent edges when face normals are equal. Adjecent vertices are dissolved /*public static int RemoveAdjecentEdges(HMesh hmesh, double thresholdDistance = 0.001, double thresholdAngle = 0.1) * { * bool changed = false; * int count = 0; * do * { * changed = false; * var heCopy = new List<Halfedge>(hmesh.GetHalfedgesRaw()); * for (int i = 0; i < heCopy.Count; i++) * { * var he1 = heCopy[i]; * if (he1.IsDestroyed()) * { * continue; * } * for (int j = i + 1; j < heCopy.Count; j++) * { * var he2 = heCopy[j]; * if (he2.IsDestroyed()) * { * continue; * } * if (he1.opp == he2) * { * continue; * } * var distVert1 = he1.prev.vert.positionD - he2.vert.positionD; * var distVert2 = he1.vert.positionD - he2.prev.vert.positionD; * * bool isAdjacentHalfedges = distVert1.magnitude < thresholdDistance && * distVert2.magnitude < thresholdDistance; * if (isAdjacentHalfedges){ * var he1Normal = he1.face.GetNormal(); * var he2Normal = he2.face.GetNormal(); * bool isSameNormal = Vector3D.Angle(he1Normal, he2Normal) < thresholdAngle; * if (isSameNormal) * { * changed = true; * * //Debug.Log("Joining he "+he1.id+" and "+he2.id); * //Debug.Log("Joining he "+he1.face.ToString() +" and "+he2.face.ToString()); * * var he2Vert = he2.vert; * var he2PrevVert = he2.prev.vert; * //Debug.Log("Replace "+he2Vert.id+" with "+he1.prev.vert.id); * //Debug.Log("Replace "+he2PrevVert.id+" with "+he1.vert.id); * he2Vert.ReplaceVertex(he1.prev.vert); * he2PrevVert.ReplaceVertex(he1.vert); * * //Debug.Log("Destroy vert "+he2Vert.id); * //Debug.Log("Destroy vert "+he2PrevVert.id); * hmesh.Destroy(he2Vert); * hmesh.Destroy(he2PrevVert); * * he1.Glue(he2); * //hmesh.IsValid(HMeshValidationRules.All); * //he1.Dissolve(); * hmesh.IsValid(HMeshValidationRules.All); * count++; * } * } * } * } * } while (changed); * return count; * }*/ /// <summary> /// Enforces triangular mesh /// </summary> /// <param name="hmesh"></param> /// <returns></returns> public static int FixDegenerateFaces(HMesh hmesh) { int count = 0; var faces = new List <Face>(hmesh.GetFaces()); for (int i = 0; i < faces.Count; i++)// (var face in hmesh.GetFaces())} { var face = faces[i]; //var faceWas = face.ToString(); if (face.IsDestroyed()) { continue; } // triangulate if (face.Circulate().Count > 3) { count++; #if HMDebug var str = face.ToString(); var debugFaceExp = face.ExportLocalNeighbourhoodToObj(); System.IO.StringWriter sw = new System.IO.StringWriter(); var res = face.Triangulate(false, sw); #else var res = face.Triangulate(); #endif if (res.Count == 0) { #if HMDebug Debug.LogWarning("Cannot triangulate " + str + " face is now " + face.ToString() + " " + sw.ToString()); Debug.LogWarning(debugFaceExp); #endif // face destroyed continue; } // add new faces for (int j = 0; j < res.Count; j++) { if (res[j] != face) { faces.Add(res[j]); } } } // fix degenerate due to zero length edge foreach (var he in face.Circulate()) { if (he.IsDestroyed()) { continue; } if (he.GetDirection().sqrMagnitude <= hmesh.zeroMagnitudeTresholdSqr) { Vector3D[] positions = { he.GetCenter(), he.vert.positionD, he.prev.vert.positionD }; bool collapsed = false; foreach (var p in positions) { var collapsePrecondition = he.CollapsePrecondition(p, Halfedge.CollapsePreconditionReason.EdgeIsBoundary | Halfedge.CollapsePreconditionReason.VertexIsBoundary | Halfedge.CollapsePreconditionReason.NormalFlipped); if (collapsePrecondition == Halfedge.CollapsePreconditionReason.Ok) { count++; he.Collapse(); collapsed = true; break; } else { Debug.Log("Cannot collapse - precondition failed " + collapsePrecondition); } } if (!collapsed) { he.Collapse(); } } } } RemoveFacesWithTwoEdges(hmesh); // fix degenerate due to zero area faces = new List <Face>(hmesh.GetFaces()); for (int i = faces.Count - 1; i >= 0; i--)// (var face in hmesh.GetFaces())} { var face = faces[i]; if (face.IsDestroyed()) { continue; } if (face.IsDegenerate()) { // find longest edge Halfedge longestEdge = null; double maxLength = -1; var edges = face.Circulate(); Debug.Assert(edges.Count == 3, "Edges was " + edges.Count); foreach (var he in edges) { double length = he.GetDirection().sqrMagnitude; if (length > maxLength) { maxLength = length; longestEdge = he; } } if (longestEdge == null) { Debug.LogError("Face " + face.id + " only has zero length edges"); continue; } var oppVert = longestEdge.next.vert; Vertex oppHeOppVert = null; if (longestEdge.opp != null) { oppHeOppVert = longestEdge.opp.next.vert; } var newVert = longestEdge.Split(); newVert.positionD = oppVert.positionD; var longestEdgeFace = longestEdge.face; var res = longestEdgeFace.Cut(oppVert, newVert); if (res == longestEdgeFace) { Debug.Log("vertices"); } if (oppHeOppVert != null) { var longestEdgeOppFace = longestEdge.opp.face; var newFace = longestEdgeOppFace.Cut(newVert, oppHeOppVert); faces.Add(newFace); faces.Add(longestEdgeOppFace); // reevaluate face } var sharedEdge = oppVert.GetSharedEdge(newVert); if (sharedEdge == null) { Debug.Log("Cannot find shared edge between " + oppVert + " " + newVert); continue; } var sharedEdgeFace1 = sharedEdge.face; var sharedEdgeFace2 = sharedEdge.opp.face; // ensured to exist (just created) var precond = sharedEdge.CollapsePrecondition(true, Halfedge.CollapsePreconditionReason.NormalFlipped); if (precond == Halfedge.CollapsePreconditionReason.Ok) { sharedEdge.Collapse(true); Debug.Assert(sharedEdgeFace1.IsDestroyed()); Debug.Assert(sharedEdgeFace2.IsDestroyed()); count++; } else { Debug.Log(precond); } } } return(count); }