1. 程式人生 > 其它 >平面剖分三角形Delaunay演算法實現,以及擴充套件到立體空間中的實現(附原始碼)

平面剖分三角形Delaunay演算法實現,以及擴充套件到立體空間中的實現(附原始碼)

技術標籤:unity3d

平面剖分三角形Delaunay演算法實現

Delaunay2D.cs下載



  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
  }

擴充套件立體空間中,三角形剖分變為四面體剖分:

Delaunay3D.cs下載

  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
  }

原始碼:

Delaunay2D.cs

Delaunay3D.cs