//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; } } } } }
/// <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); }
void OnPostRender() { PVector[] sites = voronoiTest.points; HE_Mesh[] voronoiCells = voronoiTest.voronoiCells; CreateGLMaterial(); glMaterial.SetPass(0); GL.PushMatrix(); Matrix4x4 mtx = voronoiTest.transform.localToWorldMatrix; //draw sites///////////////////////////// float size = 0.1f; GL.Begin(GL.QUADS); GL.Color(Color.white); for (int i = 0; i < sites.Length; i++) { PVector pt = sites[i]; //Vector3 pos = pt.pos; Vector3 pos = mtx.MultiplyPoint3x4(pt.pos); GL.Vertex(pos + new Vector3(-size, -size, 0)); GL.Vertex(pos + new Vector3(size, -size, 0)); GL.Vertex(pos + new Vector3(size, size, 0)); GL.Vertex(pos + new Vector3(-size, size, 0)); } GL.End(); //draw voronoi mesh////////////////////////// GL.Begin(GL.LINES); Color color = Color.white; for (int i = 0; i < voronoiCells.Length; i++) { int edgeNum = voronoiCells[i].edges.Count; for (int j = 0; j < edgeNum; j++) { HE_Edge e = voronoiCells[i].edges[j]; //Color color = Color.Lerp(Color.red, Color.blue, (float)j / (float)edgeNum); //Debug.DrawLine(e.halfEdge.vert.pos, e.halfEdge.pair.vert.pos, color); GL.Color(color); Vector3 pos0 = mtx.MultiplyPoint3x4(e.halfEdge.vert.pos); Vector3 pos1 = mtx.MultiplyPoint3x4(e.halfEdge.pair.vert.pos); GL.Vertex(pos0); GL.Vertex(pos1); } } GL.End(); //boxMat.SetPass(0); //voronoiTest.drawVoronoiBox(); GL.PopMatrix(); }
void OnPostRender() { List <HE_Mesh> meshes = voronoiTest.meshes; List <PVector> centers = voronoiTest.centers; CreateGLMaterial(); glMaterial.SetPass(0); GL.PushMatrix(); Matrix4x4 mtx = voronoiTest.transform.localToWorldMatrix; //draw ////////////////////////// GL.Begin(GL.LINES); for (int i = 0; i < meshes.Count; i++) { PVector center = (PVector)centers [i]; HE_Mesh mesh = (HE_Mesh)meshes [i]; Color color = Color.Lerp(Color.red, Color.blue, (float)i / (float)meshes.Count); //mesh.drawEdges(); int edgeNum = mesh.edges.Count; for (int j = 0; j < edgeNum; j++) { HE_Edge e = mesh.edges [j]; GL.Color(color); Vector3 pos0 = mtx.MultiplyPoint3x4(e.halfEdge.vert.pos + center.pos * 0.2f); Vector3 pos1 = mtx.MultiplyPoint3x4(e.halfEdge.pair.vert.pos + center.pos * 0.2f); //Debug.DrawLine (p0 + center.pos * 0.2f, p1 + center.pos * 0.2f, Color.red); GL.Vertex(pos0); GL.Vertex(pos1); } } /* * for(int i=0; i < voronoiCells.Length; i++) * { * int edgeNum = voronoiCells[i].edges.Count; * * for(int j = 0; j < edgeNum; j++){ * HE_Edge e = voronoiCells[i].edges[j]; * Color color = Color.Lerp(Color.red, Color.blue, (float)j / (float)edgeNum); * //Debug.DrawLine(e.halfEdge.vert.pos, e.halfEdge.pair.vert.pos, color); * * GL.Color(color); * Vector3 pos0 = mtx.MultiplyPoint3x4(e.halfEdge.vert.pos); * Vector3 pos1 = mtx.MultiplyPoint3x4(e.halfEdge.pair.vert.pos); * GL.Vertex(pos0); * GL.Vertex(pos1); * } * } */ GL.End(); GL.PopMatrix(); }
// 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); }
//Paul Bourke, http://local.wasp.uwa.edu.au/~pbourke/geometry/planeline/ PVector planeEdgeIntersection(HE_Edge e, Plane P) { PVector p0 = e.halfEdge.vert; PVector p1 = e.halfEdge.pair.vert; float denom = P.A * (p0.x - p1.x) + P.B * (p0.y - p1.y) + P.C * (p0.z - p1.z); if ((denom < HE_Mesh.DELTA) && (denom > -HE_Mesh.DELTA)) { return(null); } float u = (P.A * p0.x + P.B * p0.y + P.C * p0.z + P.D) / denom; if ((u < 0.0f) || (u > 1.0f)) { return(null); } return(new PVector(p0.x + u * (p1.x - p0.x), p0.y + u * (p1.y - p0.y), p0.z + u * (p1.z - p0.z))); }
//FLAWED public void roundEdges(float d) { if (d <= 0) { return; } List <Plane> cutPlanes = new List <Plane> (); PVector center = new PVector(); for (int i = 0; i < vertices.Count; i++) { HE_Vertex v = vertices [i]; center.add(v); } center.div(vertices.Count); for (int i = 0; i < edges.Count; i++) { HE_Edge e = edges [i]; HE_Vertex v1 = e.halfEdge.vert; HE_Vertex v2 = e.halfEdge.pair.vert; HE_Vertex v = new HE_Vertex(0.5f * (v1.x + v2.x), 0.5f * (v1.y + v2.y), 0.5f * (v1.z + v2.z), 0); PVector n = PVector.sub(v, center); //float distanceToVertex=n.mag(); //if(distanceToVertex>d){ float distanceToVertex = n.magSq(); if (distanceToVertex > d * d) { float ratio = (distanceToVertex - d) / distanceToVertex; PVector origin = PVector.mult(n, ratio); origin.add(center); cutPlanes.Add(new Plane(origin, n)); } } for (int i = 0; i < cutPlanes.Count; i++) { Plane P = cutPlanes [i]; cutMesh(P, center); } }
// check all edges for intersection wit a plan List <SplitEdge> retrieveSplitEdges(Plane P) { List <SplitEdge> splitEdges = new List <SplitEdge> (); for (int i = 0; i < edges.Count; i++) { HE_Edge edge = edges [i]; /* * PVector intersection = planeEdgeIntersection(edge,P); * if(intersection != null){ * splitEdges.Add(new SplitEdge(edge,intersection)); * } */ PVector intersection; bool b = planeEdgeIntersection(out intersection, edge, P); if (b) { splitEdges.Add(new SplitEdge(edge, intersection)); } } return(splitEdges); }
// Takes a List containing another List per face with the vertex indexes belonging to that face private bool createFaces(List <List <int> > faceIndexes) { Dictionary <string, int> edgeCount = new Dictionary <string, int>(); Dictionary <string, HE_HalfEdge> existingHalfEdges = new Dictionary <string, HE_HalfEdge>(); Dictionary <HE_HalfEdge, bool> hasTwinHalfEdge = new Dictionary <HE_HalfEdge, bool>(); // Create the faces, edges and half-edges, non-boundary loops and link references when possible; foreach (List <int> indexes in faceIndexes) { HE_Face f = new HE_Face(); Faces.Add(f); List <HE_HalfEdge> tempHEdges = new List <HE_HalfEdge>(indexes.Count); //Create empty half-edges for (int i = 0; i < indexes.Count; i++) { HE_HalfEdge h = new HE_HalfEdge(); tempHEdges.Add(h); } //Fill out each half edge for (int i = 0; i < indexes.Count; i++) { //Edge goes from v0 to v1 int v0 = indexes[i]; int v1 = indexes[(i + 1) % indexes.Count]; HE_HalfEdge h = tempHEdges[i]; // Set previous and next h.Next = tempHEdges[(i + 1) % indexes.Count]; h.Prev = tempHEdges[(i + indexes.Count - 1) % indexes.Count]; h.onBoundary = false; hasTwinHalfEdge.Add(h, false); // Set half-edge & vertex mutually h.Vertex = Vertices[v0]; Vertices[v0].HalfEdge = h; // Set half-edge face & vice versa h.Face = f; f.HalfEdge = h; // Reverse v0 and v1 if v0 > v1 if (v0 > v1) { int temp = v0; v0 = v1; v1 = temp; } string key = v0 + " " + v1; if (existingHalfEdges.ContainsKey(key)) { // If this half-edge key already exists, it is the twin of this current half-edge HE_HalfEdge twin = existingHalfEdges[key]; h.Twin = twin; twin.Twin = h; h.Edge = twin.Edge; hasTwinHalfEdge[h] = true; hasTwinHalfEdge[twin] = true; edgeCount[key] += 1; } else { // Create an edge and set its half-edge HE_Edge e = new HE_Edge(); Edges.Add(e); h.Edge = e; e.HalfEdge = h; // Record the newly created half-edge existingHalfEdges.Add(key, h); edgeCount.Add(key, 1); } } HalfEdges.AddRange(tempHEdges); } // Create boundary edges for (int i = 0; i < HalfEdges.Count; i++) { HE_HalfEdge h = HalfEdges[i]; if (!hasTwinHalfEdge[h]) { HE_Face f = new HE_Face(); Boundaries.Add(f); List <HE_HalfEdge> boundaryCycle = new List <HE_HalfEdge>(); HE_HalfEdge hE = h; do { HE_HalfEdge bH = new HE_HalfEdge(); HalfEdges.Add(bH); boundaryCycle.Add(bH); HE_HalfEdge nextHE = hE.Next; while (hasTwinHalfEdge[nextHE]) { nextHE = nextHE.Twin.Next; } bH.Vertex = nextHE.Vertex; bH.Edge = hE.Edge; bH.onBoundary = true; bH.Face = f; f.HalfEdge = bH; bH.Twin = hE; hE.Twin = bH; hE = nextHE; } while (hE != h); int n = boundaryCycle.Count; for (int j = 0; j < n; j++) { boundaryCycle[j].Next = boundaryCycle[(j + n - 1) % n]; boundaryCycle[j].Prev = boundaryCycle[(j + 1) % n]; hasTwinHalfEdge[boundaryCycle[j]] = true; hasTwinHalfEdge[boundaryCycle[j].Twin] = true; } } if (!h.onBoundary) { HE_Corner corner = new HE_Corner(); corner.HalfEdge = h; h.Corner = corner; Corners.Add(corner); } } // Check mesh for common errors if (HasIsolatedFaces() || HasIsolatedVertices() || HasNonManifoldEdges()) { return(false); } // Index elements indexElements(); return(true); }
/// <summary> /// Calculates the length of the specified edge. /// </summary> /// <returns>The length.</returns> /// <param name="edge">Edge.</param> public static double Length(HE_Edge edge) => Vector(edge.HalfEdge).Length;
public SplitEdge(HE_Edge e, PVector p) { edge = e; splitVertex = new HE_Vertex(p.x, p.y, p.z, 0); }
// 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(); } }