//associate each pair of halfedges to a physical edge. void createEdges(List <HE_HalfEdge> halfEdges, List <HE_Edge> target) { int n = halfEdges.Count; for (int i = 0; i < n; i++) { HE_HalfEdge he = halfEdges [i]; for (int j = 0; j < n; j++) { if (i != j) { HE_HalfEdge he2 = halfEdges [j]; if (he.pair == he2) { HE_Edge e = new HE_Edge(); e.halfEdge = he; target.Add(e); he.edge = e; he2.edge = e; break; } } } } }
//go through all the halfedges and find matching pairs void pairHalfEdges(List <HE_HalfEdge> halfEdges) { int n = halfEdges.Count; for (int i = 0; i < n; i++) { HE_HalfEdge he = halfEdges [i]; if (he.pair == null) { for (int j = 0; j < n; j++) { if (i != j) { HE_HalfEdge he2 = halfEdges [j]; if ((he2.pair == null) && (he.vert == he2.next.vert) && (he2.vert == he.next.vert)) { he.pair = he2; he2.pair = he; break; } } } } } }
/// <summary> /// Calculates the midpoint of the specifiec edge. /// </summary> /// <returns>The point.</returns> /// <param name="edge">Edge.</param> public static Point3d MidPoint(HE_Edge edge) { HE_HalfEdge halfEdge = edge.HalfEdge; Point3d a = halfEdge.Vertex; Point3d b = halfEdge.Twin.Vertex; return((a + b) / 2); }
// get clone public HE_Mesh get() { HE_Mesh result = new HE_Mesh(); reindex(); for (int i = 0; i < vertices.Count; i++) { result.vertices.Add((vertices [i]).getClone()); } for (int i = 0; i < faces.Count; i++) { result.faces.Add(new HE_Face()); } for (int i = 0; i < halfEdges.Count; i++) { result.halfEdges.Add(new HE_HalfEdge()); } for (int i = 0; i < edges.Count; i++) { result.edges.Add(new HE_Edge()); } // for (int i = 0; i < vertices.Count; i++) { HE_Vertex sv = vertices [i]; HE_Vertex tv = result.vertices [i]; tv.halfEdge = result.halfEdges [sv.halfEdge.id]; } for (int i = 0; i < faces.Count; i++) { HE_Face sf = faces [i]; HE_Face tf = result.faces [i]; tf.id = i; tf.halfEdge = result.halfEdges [sf.halfEdge.id]; } for (int i = 0; i < edges.Count; i++) { HE_Edge se = edges [i]; HE_Edge te = result.edges [i]; te.halfEdge = result.halfEdges [se.halfEdge.id]; te.id = i; } for (int i = 0; i < halfEdges.Count; i++) { HE_HalfEdge she = halfEdges [i]; HE_HalfEdge the = result.halfEdges [i]; the.pair = result.halfEdges [she.pair.id]; the.next = result.halfEdges [she.next.id]; the.prev = result.halfEdges [she.prev.id]; the.vert = result.vertices [she.vert.id]; the.face = result.faces [she.face.id]; the.edge = result.edges [she.edge.id]; the.id = i; } return(result); }
HE_Mesh getDual() { HE_Mesh result = new HE_Mesh(); reindex(); for (int i = 0; i < faces.Count; i++) { HE_Face f = faces [i]; HE_HalfEdge he = f.halfEdge; PVector faceCenter = new PVector(); int n = 0; do { faceCenter.add(he.vert); he = he.next; n++; } while(he != f.halfEdge); faceCenter.div(n); result.vertices.Add(new HE_Vertex(faceCenter.x, faceCenter.y, faceCenter.z, i)); } //for(int i=0;i<vertices.Count;i++){ //HE_Vertex v=getVertex(i); foreach (HE_Vertex v in vertices) { HE_HalfEdge he = v.halfEdge; HE_Face f = he.face; List <HE_HalfEdge> faceHalfEdges = new List <HE_HalfEdge> (); HE_Face nf = new HE_Face(); result.faces.Add(nf); do { HE_HalfEdge hen = new HE_HalfEdge(); faceHalfEdges.Add(hen); hen.face = nf; hen.vert = result.vertices [f.id]; if (hen.vert.halfEdge == null) { hen.vert.halfEdge = hen; } if (nf.halfEdge == null) { nf.halfEdge = hen; } he = he.pair.next; f = he.face; } while(he != v.halfEdge); cycleHalfEdges(faceHalfEdges, false); result.halfEdges.AddRange(faceHalfEdges); } result.pairHalfEdges(result.halfEdges); result.createEdges(result.halfEdges, result.edges); result.reindex(); return(result); }
/// <summary> /// Computes the cotangent of the angle opposite to a halfedge. /// </summary> /// <returns>The cotan.</returns> /// <param name="hE">H e.</param> public static double Cotan(HE_HalfEdge hE) { if (hE.onBoundary) { return(0.0); } Vector3d u = Vector(hE.Prev); Vector3d v = -Vector(hE.Next); return(u.Dot(v) / u.Cross(v).Length); }
public void buildMesh(float[][] simpleVertices, int[][] simpleFaces) { /* * vertices = new List<HE_Vertex>(); * faces=new List<HE_Face>(); * halfEdges=new List<HE_HalfEdge>(); * edges=new List<HE_Edge>(); */ vertices.Clear(); faces.Clear(); halfEdges.Clear(); edges.Clear(); //Add all input vertices to the mesh, the original index of the vertex is stored as an id. for (int i = 0; i < simpleVertices.Length; i++) { vertices.Add(new HE_Vertex(simpleVertices [i] [0], simpleVertices [i] [1], simpleVertices [i] [2], i)); } //Create a loop of halfedges for each face. We assume the vertices are given in a consistent order. // To extend this for a non-ordered list, sort the vertices of the input vertices here. HE_HalfEdge he; for (int i = 0; i < simpleFaces.Length; i++) { List <HE_HalfEdge> faceEdges = new List <HE_HalfEdge> (); HE_Face hef = new HE_Face(); faces.Add(hef); //TODO: sort face vertices here for (int j = 0; j < simpleFaces[i].Length; j++) { he = new HE_HalfEdge(); faceEdges.Add(he); he.face = hef; if (hef.halfEdge == null) { hef.halfEdge = he; } he.vert = vertices [simpleFaces [i] [j]]; if (he.vert.halfEdge == null) { he.vert.halfEdge = he; } } cycleHalfEdges(faceEdges, false); halfEdges.AddRange(faceEdges); } //associate each halfedge with its pair (belonging to adjacent face) pairHalfEdges(halfEdges); //associate each pair of halfedges to a physical edge createEdges(halfEdges, edges); reindex(); }
/// <summary> /// Compute the centroid of the specified face /// </summary> /// <returns>The centroid.</returns> /// <param name="face">Face.</param> public static Point3d Centroid(HE_Face face) { HE_HalfEdge hE = face.HalfEdge; Point3d a = hE.Vertex; Point3d b = hE.Next.Vertex; Point3d c = hE.Prev.Vertex; if (face.isBoundaryLoop()) { return((a + b) / 2); } return((a + b + c) / 3); }
/// <summary> /// Computes the signed angle (in radians) between the faces adjacent to the specified half-edge. /// </summary> /// <returns>The angle (in radians) between faces.</returns> /// <param name="hE">H e.</param> public static double DihedralAngle(HE_HalfEdge hE) { if (hE.onBoundary || hE.Twin.onBoundary) { return(0.0); } Vector3d n1 = FaceNormal(hE.Face); Vector3d n2 = FaceNormal(hE.Twin.Face); Vector3d w = Vector(hE).Unit(); double cosTheta = n1.Dot(n2); double sinTheta = n1.Cross(n2).Dot(w); return(Math.Atan2(sinTheta, cosTheta)); }
/// <summary> /// Compute the circumcenter the specified face. /// </summary> /// <returns>The circumcenter.</returns> /// <param name="face">Face.</param> public static Point3d Circumcenter(HE_Face face) { HE_HalfEdge hE = face.HalfEdge; Point3d a = hE.Vertex; Point3d b = hE.Next.Vertex; Point3d c = hE.Prev.Vertex; if (face.isBoundaryLoop()) { return((a + b) / 2); } Vector3d ac = c - a; Vector3d ab = b - a; Vector3d w = ab.Cross(ac); Vector3d u = (w.Cross(ab)) * ac.Length2; Vector3d v = (ac.Cross(w)) * ab.Length2; Point3d x = (u + v) / (2 * w.Length2); return(x + a); }
/// <summary> /// Calculate the vector of a specified half-edge. /// </summary> /// <returns>The half-edge vector.</returns> /// <param name="halfEdge">Half edge.</param> public static Vector3d Vector(HE_HalfEdge halfEdge) => halfEdge.Vertex - halfEdge.Next.Vertex;
// draw as a triangular mesh. public Triangle3D draw() { Triangle3D triangle3d = new Triangle3D(); for (int i = 0; i < faces.Count; i++) { HE_Face face = faces [i]; HE_HalfEdge halfEdge = face.halfEdge; HE_Vertex midFace = new HE_Vertex(0, 0, 0, 0); int c = 0; do { HE_Vertex v = halfEdge.vert; midFace.x += v.x; midFace.y += v.y; midFace.z += v.z; halfEdge = halfEdge.next; c++; } while(halfEdge != face.halfEdge); midFace.x /= c; midFace.y /= c; midFace.z /= c; halfEdge = face.halfEdge; HE_Vertex vv; do { HE_Vertex v0 = halfEdge.vert; HE_Vertex v1 = halfEdge.next.vert; triangle3d.vertices.Add(midFace); triangle3d.vertices.Add(v1); triangle3d.vertices.Add(v0); halfEdge = halfEdge.next; } while(halfEdge != face.halfEdge); //beginShape(TRIANGLE_STRIP); /* * GL.Begin(GL.TRIANGLE_STRIP); * HE_Vertex vv; * do{ * vv=halfEdge.vert; * GL.Vertex3(vv.x,vv.y,vv.z); * GL.Vertex3(midFace.x,midFace.y,midFace.z); * halfEdge= halfEdge.next; * } * while(halfEdge!=face.halfEdge); * vv=halfEdge.vert; * GL.Vertex3(vv.x,vv.y,vv.z); * //endShape(); * GL.End(); */ /* * HE_Vertex vv; * do{ * vv=halfEdge.vert; * //GL.Vertex3(vv.x,vv.y,vv.z); * //GL.Vertex3(midFace.x,midFace.y,midFace.z); * triangle3d.vertices.Add(vv); * triangle3d.vertices.Add(midFace); * * halfEdge= halfEdge.next; * } * while(halfEdge!=face.halfEdge); * vv=halfEdge.vert; * * //GL.Vertex3(vv.x,vv.y,vv.z); * triangle3d.vertices.Add(vv); */ } return(triangle3d); }
void splitSurface(Plane P) { List <HE_Vertex> newVertices = new List <HE_Vertex> (); List <HE_Face> newFaces = new List <HE_Face> (); List <HE_HalfEdge> newHalfEdges = new List <HE_HalfEdge> (); List <HE_Edge> newEdges = new List <HE_Edge> (); // get all split edges List <SplitEdge> splitEdges = retrieveSplitEdges(P); //check if the plane cuts the mesh at all, at least one point should be on the other side of the plane. //compared to the first point float[] sides = new float[vertices.Count]; //bool cut=false; for (int i = 0; i < vertices.Count; i++) { HE_Vertex v = vertices [i]; sides [i] = P.side(v); //if(sides[0]*sides[i]<=0f) cut=true; } //loop through all faces. for (int i = 0; i < faces.Count; i++) { HE_Face face = faces [i]; HE_HalfEdge halfEdge = face.halfEdge; List <HE_Vertex> newFaceVertices1 = new List <HE_Vertex> (); List <HE_Vertex> newFaceVertices2 = new List <HE_Vertex> (); List <HE_Vertex> currentFace = newFaceVertices1; //for each face, loop through all vertices and retain the vertices on the correct side. If the edge //is cut, insert the new point in the appropriate place. do { currentFace.Add(halfEdge.vert); for (int j = 0; j < splitEdges.Count; j++) // loop through all split edges to check for the current edge. { SplitEdge se = (SplitEdge)splitEdges [j]; if (halfEdge.edge == se.edge) { newFaceVertices1.Add(se.splitVertex); newFaceVertices2.Add(se.splitVertex); if (currentFace == newFaceVertices1) { currentFace = newFaceVertices2; } else { currentFace = newFaceVertices1; } break; } } halfEdge = halfEdge.next; } while(halfEdge != face.halfEdge); //Create a new face form the vertices we retained,ignore degenerate faces with less than 3 vertices. //Add all face-related information to the data-structure. HE_Face newFace = new HE_Face(); newFaces.Add(newFace); List <HE_HalfEdge> faceEdges = new List <HE_HalfEdge> (); for (int j = 0; j < newFaceVertices1.Count; j++) { HE_Vertex v = newFaceVertices1 [j]; if (!newVertices.Contains(v)) { newVertices.Add(v); } HE_HalfEdge newHalfEdge = new HE_HalfEdge(); faceEdges.Add(newHalfEdge); newHalfEdge.vert = v; v.halfEdge = newHalfEdge; newHalfEdge.face = newFace; if (newFace.halfEdge == null) { newFace.halfEdge = newHalfEdge; } } cycleHalfEdges(faceEdges, false); newHalfEdges.AddRange(faceEdges); if (newFaceVertices2.Count > 0) { newFace = new HE_Face(); newFaces.Add(newFace); faceEdges = new List <HE_HalfEdge> (); for (int j = 0; j < newFaceVertices2.Count; j++) { HE_Vertex v = newFaceVertices2 [j]; if (!newVertices.Contains(v)) { newVertices.Add(v); } HE_HalfEdge newHalfEdge = new HE_HalfEdge(); faceEdges.Add(newHalfEdge); newHalfEdge.vert = v; v.halfEdge = newHalfEdge; newHalfEdge.face = newFace; if (newFace.halfEdge == null) { newFace.halfEdge = newHalfEdge; } } cycleHalfEdges(faceEdges, false); newHalfEdges.AddRange(faceEdges); //}//test } //Add missing information to the datastructure pairHalfEdges(newHalfEdges); createEdges(newHalfEdges, newEdges); } // update the mesh vertices = newVertices; faces = newFaces; halfEdges = newHalfEdges; edges = newEdges; reindex(); }
// Split the mesh in half, retain the part on the same side as the point "center". // Works only on a convex mesh !!! public void cutMesh(Plane P, PVector center) { float centerside = P.side(center); if (centerside != 0) // if center is on the plane, we can't decide which part to keep, ignore. { List <HE_Vertex> newVertices = new List <HE_Vertex> (); List <HE_Face> newFaces = new List <HE_Face> (); List <HE_HalfEdge> newHalfEdges = new List <HE_HalfEdge> (); List <HE_Edge> newEdges = new List <HE_Edge> (); // get all split edges List <SplitEdge> splitEdges = retrieveSplitEdges(P); //check if the plane cuts the mesh at all, at least one point should be on the other side of the plane. //compared to the first point float[] sides = new float[vertices.Count]; //bool cut = false;// add inok for (int i = 0; i < vertices.Count; i++) { HE_Vertex v = vertices [i]; sides [i] = P.side(v); //if(sides[0]*sides[i]<=0f) cut=true;// add inok } //loop through all faces. for (int i = 0; i < faces.Count; i++) { HE_Face face = faces [i]; HE_HalfEdge halfEdge = face.halfEdge; List <HE_Vertex> newFaceVertices1 = new List <HE_Vertex> (); // vertices on the correct side. List <HE_Vertex> newFaceVertices2 = new List <HE_Vertex> (); // vertices on the wrong side, not used right now. //for each face, loop through all vertices and retain the vertices on the correct side. If the edge //is cut, insert the new point in the appropriate place. do { if (sides [halfEdge.vert.id] * centerside >= 0f) { newFaceVertices1.Add(halfEdge.vert); } if (sides [halfEdge.vert.id] * centerside <= 0f) { newFaceVertices2.Add(halfEdge.vert); } for (int j = 0; j < splitEdges.Count; j++) // loop through all split edges to check for the current edge. { SplitEdge se = splitEdges [j]; if (halfEdge.edge == se.edge) { newFaceVertices1.Add(se.splitVertex); newFaceVertices2.Add(se.splitVertex); break; } } halfEdge = halfEdge.next; } while(halfEdge != face.halfEdge); //Create a new face form the vertices we retained,ignore degenerate faces with less than 3 vertices. //Add all face-related information to the data-structure. if (newFaceVertices1.Count > 2) { HE_Face newFace = new HE_Face(); newFaces.Add(newFace); List <HE_HalfEdge> faceEdges = new List <HE_HalfEdge> (); for (int j = 0; j < newFaceVertices1.Count; j++) { HE_Vertex v = newFaceVertices1 [j]; if (!newVertices.Contains(v)) { newVertices.Add(v); } HE_HalfEdge newHalfEdge = new HE_HalfEdge(); faceEdges.Add(newHalfEdge); newHalfEdge.vert = v; v.halfEdge = newHalfEdge; newHalfEdge.face = newFace; if (newFace.halfEdge == null) { newFace.halfEdge = newHalfEdge; } } cycleHalfEdges(faceEdges, false); newHalfEdges.AddRange(faceEdges); } } //Add missing information to the datastructure int n = newHalfEdges.Count; pairHalfEdges(newHalfEdges); createEdges(newHalfEdges, newEdges); //Cutting the mesh not only cuts the faces, it also creates one new planar face looping through all new cutpoints(in a convex mesh). //This hole in the mesh is identified by unpaired halfedges remaining after the pairibg operation. //This part needs to rethought to extend to concave meshes!!! List <HE_HalfEdge> unpairedEdges = new List <HE_HalfEdge> (); for (int i = 0; i < n; i++) { HE_HalfEdge he = newHalfEdges [i]; if (he.pair == null) { unpairedEdges.Add(he); } } if (unpairedEdges.Count > 0) { //Create a closed loop out of the collection of unpaired halfedges and associate a new face with this. //Easy to explain with a drawing, beyond my skill with words. HE_Face cutFace = new HE_Face(); List <HE_HalfEdge> faceEdges = new List <HE_HalfEdge> (); HE_HalfEdge he = unpairedEdges [0]; HE_HalfEdge hen = he; do { HE_HalfEdge _hen = he.next; HE_HalfEdge _hep = he.next.pair; if (_hep != null) //add inok { hen = he.next.pair.next; while (!unpairedEdges.Contains(hen)) { hen = hen.pair.next; } } else { hen = hen.next; Debug.LogWarning("LogWarning: null"); } HE_HalfEdge newhe = new HE_HalfEdge(); faceEdges.Add(newhe); if (cutFace.halfEdge == null) { cutFace.halfEdge = newhe; } newhe.vert = hen.vert; newhe.pair = he; he.pair = newhe; HE_Edge e = new HE_Edge(); e.halfEdge = newhe; he.edge = e; newhe.edge = e; newEdges.Add(e); newhe.face = cutFace; he = hen; } while(hen != unpairedEdges[0]); cycleHalfEdges(faceEdges, true); newHalfEdges.AddRange(faceEdges); newFaces.Add(cutFace); } // update the mesh vertices = newVertices; faces = newFaces; halfEdges = newHalfEdges; edges = newEdges; reindex(); } }
/// <summary> /// Compute the intersection between a mesh face perimeter and a ray tangent to the face. /// </summary> /// <param name="ray">The tangent ray.</param> /// <param name="Face">The mesh face.</param> /// <param name="result">The resulting intersection point.</param> /// <param name="halfEdge">The half-edge on where the intersection lies.</param> /// <returns></returns> public static ISRayFacePerimeter RayFacePerimeter(Ray ray, HE_Face Face, out Point3d result, out HE_HalfEdge halfEdge) { Vector3d faceNormal = HE_MeshGeometry.FaceNormal(Face); Vector3d biNormal = Vector3d.CrossProduct(ray.Direction, faceNormal); Plane perpPlane = new Plane(ray.Origin, ray.Direction, faceNormal, biNormal); List <HE_Vertex> vertices = Face.adjacentVertices(); Point3d temp = new Point3d(); Line line = new Line(vertices[0], vertices[1]); if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return(ISRayFacePerimeter.Point); } // No intersection found if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return(ISRayFacePerimeter.Point); } // Intersection found line = new Line(vertices[1], vertices[2]); if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return(ISRayFacePerimeter.NoIntersection); } // No intersection found if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return(ISRayFacePerimeter.Point); } // Intersection found line = new Line(vertices[2], vertices[0]); if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return(ISRayFacePerimeter.NoIntersection); } if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return(ISRayFacePerimeter.Point); } else { result = null; halfEdge = null; return(ISRayFacePerimeter.Error); } }