平面剖分三角形Delaunay演算法實現,以及擴充套件到立體空間中的實現(附原始碼)
阿新 • • 發佈:2021-02-02
平面剖分三角形Delaunay演算法實現
void Triangulate() { /* 基於Bowyer-Watson演算法原理 * 通過所有頂點和現有三角形進行判斷, 對包含該點的三角形進行拆分 * 首先構建初始的三角形,包含全部點在內(最後會刪除) * * 遍歷所有頂點 { * 遍歷所有現有三角形 { * 首先判斷是否 [頂點] 在 [三角形] 內, 如果不在則跳過 * [頂點] 和 [三角形] 的3條邊,生成新的3個三角形,加入現有三角形集合中 * } * } */ #region 構建初始三角形,可以包含住所有的點 float minX = Vertices[0].Position.x; float minY = Vertices[0].Position.y; float maxX = minX; float maxY = minY; foreach (var vertex in Vertices) { if (vertex.Position.x < minX) minX = vertex.Position.x; if (vertex.Position.x > maxX) maxX = vertex.Position.x; if (vertex.Position.y < minY) minY = vertex.Position.y; if (vertex.Position.y > maxY) maxY = vertex.Position.y; } float dx = maxX - minX; float dy = maxY - minY; float deltaMax = Mathf.Max(dx, dy) * 2; Vertex p1 = new Vertex(new Vector2(minX - 1, minY - 1)); Vertex p2 = new Vertex(new Vector2(minX - 1, maxY + deltaMax)); Vertex p3 = new Vertex(new Vector2(maxX + deltaMax, minY - 1)); Triangles.Add(new Triangle(p1, p2, p3)); #endregion #region 通過頂點對三角形進行拆分 //遍歷所有頂點 foreach (var vertex in Vertices) { List<Edge> polygon = new List<Edge>(); //遍歷所有現有三角形 foreach (var t in Triangles) { if (t.CircumCircleContains(vertex.Position)) {//如果包含該點 //標記刪除 t.IsBad = true; //記錄下三條邊 polygon.Add(new Edge(t.A, t.B)); polygon.Add(new Edge(t.B, t.C)); polygon.Add(new Edge(t.C, t.A)); } } //刪除標記的三角形 Triangles.RemoveAll((Triangle t) => t.IsBad); //刪除近似的邊 for (int i = 0; i < polygon.Count; i++) { for (int j = i + 1; j < polygon.Count; j++) { if (Edge.AlmostEqual(polygon[i], polygon[j])) { polygon[i].IsBad = true; polygon[j].IsBad = true; } } } polygon.RemoveAll((Edge e) => e.IsBad); //通過點和邊構建新的三角形,加入到現有三角形集合中 foreach (var edge in polygon) { Triangles.Add(new Triangle(edge.U, edge.V, vertex)); } } //刪除所有包含初始點的三角形 Triangles.RemoveAll((Triangle t) => t.ContainsVertex(p1.Position) || t.ContainsVertex(p2.Position) || t.ContainsVertex(p3.Position)); #endregion #region 提取邊資訊 HashSet<Edge> edgeSet = new HashSet<Edge>(); foreach (var t in Triangles) { var ab = new Edge(t.A, t.B); var bc = new Edge(t.B, t.C); var ca = new Edge(t.C, t.A); if (edgeSet.Add(ab)) { Edges.Add(ab); } if (edgeSet.Add(bc)) { Edges.Add(bc); } if (edgeSet.Add(ca)) { Edges.Add(ca); } } #endregion }
擴充套件立體空間中,三角形剖分變為四面體剖分:
void Triangulate() { /* 基於Bowyer-Watson演算法原理 * 通過所有頂點和現有四面體進行判斷, 對包含該點的四面體進行拆分 * 首先構建初始的四面體,包含全部點在內,並在最後刪除 * * 遍歷所有頂點 { * 遍歷所有現有四面體 { * 首先判斷是否 [頂點] 在 [四面體] 內, 如果不在則跳過 * [頂點] 和 [四面體] 的四個三角形,生成新的四個四面體,加入現有四面體集合中 * } * } * * 通過以上步驟可以得到所有點相關的四面體 * 將這些四面體的三角形資訊拿出來即可 */ #region 構建初始四面體,可以包含住所有點的四面體 float minX = Vertices[0].Position.x; float minY = Vertices[0].Position.y; float minZ = Vertices[0].Position.z; float maxX = minX; float maxY = minY; float maxZ = minZ; //1.計算頂點中最大和最小的座標 x,y,z => minX,minY,minZ,maxX,maxY,maxZ foreach (var vertex in Vertices) { if (vertex.Position.x < minX) minX = vertex.Position.x; if (vertex.Position.x > maxX) maxX = vertex.Position.x; if (vertex.Position.y < minY) minY = vertex.Position.y; if (vertex.Position.y > maxY) maxY = vertex.Position.y; if (vertex.Position.z < minZ) minZ = vertex.Position.z; if (vertex.Position.z > maxZ) maxZ = vertex.Position.z; } //2.計算座標差 dx,dy,dz float dx = maxX - minX; float dy = maxY - minY; float dz = maxZ - minZ; float deltaMax = Mathf.Max(dx, dy, dz) * 2; Vertex p1 = new Vertex(new Vector3(minX - 1, minY - 1, minZ - 1)); Vertex p2 = new Vertex(new Vector3(maxX + deltaMax, minY - 1, minZ - 1)); Vertex p3 = new Vertex(new Vector3(minX - 1, maxY + deltaMax, minZ - 1)); Vertex p4 = new Vertex(new Vector3(minX - 1, minY - 1, maxZ + deltaMax)); this.Tetrahedra.Add(new Tetrahedron(p1, p2, p3, p4)); #endregion #region 通過頂點對四面體進行拆分 //遍歷所有頂點 foreach (var vertex in Vertices) { List<Triangle> triangles = new List<Triangle>(); //遍歷所有現有四面體 foreach (var t in this.Tetrahedra) { if (t.CircumCircleContains(vertex.Position)) { //如果包含則標記該四面體需要刪除 t.IsBad = true; //記錄四面體的4個三角形 triangles.Add(new Triangle(t.A, t.B, t.C)); triangles.Add(new Triangle(t.A, t.B, t.D)); triangles.Add(new Triangle(t.A, t.C, t.D)); triangles.Add(new Triangle(t.B, t.C, t.D)); } } //判斷四面體4個三角形是否近似相同,相同則都刪除 for (int i = 0; i < triangles.Count; i++) { for (int j = i + 1; j < triangles.Count; j++) { if (Triangle.AlmostEqual(triangles[i], triangles[j])) { triangles[i].IsBad = true; triangles[j].IsBad = true; } } } //刪除帶標記的四面體和三角形 this.Tetrahedra.RemoveAll((Tetrahedron t) => t.IsBad); triangles.RemoveAll((Triangle t) => t.IsBad); //將剩餘三角形和該頂點重新構成四面體 foreach (var triangle in triangles) { this.Tetrahedra.Add(new Tetrahedron(triangle.U, triangle.V, triangle.W, vertex)); } } //四面體中刪除所有含有初始四面體點的四面體 this.Tetrahedra.RemoveAll((Tetrahedron t) => t.ContainsVertex(p1) || t.ContainsVertex(p2) || t.ContainsVertex(p3) || t.ContainsVertex(p4)); #endregion #region 提取三角形和邊的資訊 HashSet<Triangle> triangleSet = new HashSet<Triangle>(); HashSet<Edge> edgeSet = new HashSet<Edge>(); foreach (var t in this.Tetrahedra) { var abc = new Triangle(t.A, t.B, t.C); var abd = new Triangle(t.A, t.B, t.D); var acd = new Triangle(t.A, t.C, t.D); var bcd = new Triangle(t.B, t.C, t.D); if (triangleSet.Add(abc)) { this.Triangles.Add(abc); } if (triangleSet.Add(abd)) { this.Triangles.Add(abd); } if (triangleSet.Add(acd)) { this.Triangles.Add(acd); } if (triangleSet.Add(bcd)) { this.Triangles.Add(bcd); } var ab = new Edge(t.A, t.B); var bc = new Edge(t.B, t.C); var ca = new Edge(t.C, t.A); var da = new Edge(t.D, t.A); var db = new Edge(t.D, t.B); var dc = new Edge(t.D, t.C); if (edgeSet.Add(ab)) { Edges.Add(ab); } if (edgeSet.Add(bc)) { Edges.Add(bc); } if (edgeSet.Add(ca)) { Edges.Add(ca); } if (edgeSet.Add(da)) { Edges.Add(da); } if (edgeSet.Add(db)) { Edges.Add(db); } if (edgeSet.Add(dc)) { Edges.Add(dc); } } #endregion }
原始碼: