2013年3月17日 星期日

<分享> circle packing 2D 在vb / c#的差別

目標 : 把circle packing vb component 轉譯成 c# component


描述:
想把之前沒搞懂packing 的邏輯在整理一下,並且翻出之前網路上david 寫好的vb legacy Circle packing component 來轉譯成 C#。起初想說只要把變數名稱還有方法宣告的部分改寫就好,沒想到果然在過程中還是遇到一些問題。像是再判斷創建點是否在現有的圓內時,需要跳出迴圈繼續計算下一個創建點直到創建點都在現有圓外時的部分。

vb 的寫法是
      For Each crc As OnCircle In circles
 If (point.DistanceTo(crc.Center) < crc.radius) Then Continue Do
      Next

C#的寫法如果只用continue 則程式的判斷將不會去除圓內點
  foreach(Circle crc in circles){
          if(point.DistanceTo(crc.Center) < crc.Radius){
            continue;
          }

圓內點沒被排除狀況


C#正確改寫方法應該使用關鍵字 goto; 離開判斷並且到最外層的do while迴圈
  foreach(Circle crc in circles){
          if(point.DistanceTo(crc.Center) < crc.Radius){
            goto label;
          }

正確結果



參考案例 :
http://www.grasshopper3d.com/forum/topics/circle-packing-constraints?commentId=2985220%3AComment%3A105142

http://www.grasshopper3d.com/forum/topics/circle-packing-1


檔案 :

流程步驟 :


Main Code:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
vb legacy parts ->
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    If (C Is Nothing) Then Return
    If (N <= 0) Then Return

    If (Not C.IsClosed) Then
      Print("Your input curve is not closed.")
      Return
    End If

    Dim bbox As OnBoundingBox = C.BoundingBox()
    Dim U As New OnInterval(bbox.Min().x, bbox.Max().x)
    Dim V As New OnInterval(bbox.Min().y, bbox.Max().y)

    Dim rnd As New Random(7)

    Dim circles As New List(Of OnCircle)
    Dim attempts As Int32 = 0

    Do While (attempts < 1000)
      attempts += 1

      Dim point As New On3dPoint(U.ParameterAt(rnd.NextDouble()), V.ParameterAt(rnd.NextDouble()), 0)

      If (Rhutil.RhinoPointInPlanarClosedCurve(point, C, OnPlane.World_xy) <> 1) Then Continue Do
      For Each crc As OnCircle In circles
        If (point.DistanceTo(crc.Center) < crc.radius) Then Continue Do
      Next

      Dim ct As Double : C.GetClosestPoint(point, ct)
      Dim cp As On3dPoint = C.PointAt(ct)
      Dim radius As Double = point.DistanceTo(cp)

      For Each crc As OnCircle In circles
        Dim dist As Double = point.DistanceTo(crc.Center) - crc.radius
        If (dist < radius) Then radius = dist
      Next

      Dim circle As New OnCircle(point, radius)
      circles.add(circle)
      If (circles.Count >= N) Then Exit Do
      attempts = 0
    Loop

    A = circles

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
C# parts ->
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BoundingBox bbox;
    bbox = xCrv.GetBoundingBox(true);

    Interval U = new Interval(bbox.Min.X, bbox.Max.X);
    Interval V = new Interval(bbox.Min.Y, bbox.Max.Y);
    Random rnd = new Random(7);
    List<Circle> circles = new List<Circle>();

    int attempts = 0;

    label: do{

        attempts += 1;

        Point3d point = new Point3d(U.ParameterAt(rnd.NextDouble()), V.ParameterAt(rnd.NextDouble()), 0);

        //point inside existing circle -> regenerate a new point//
        foreach(Circle crc in circles){
          if(point.DistanceTo(crc.Center) < crc.Radius){
            goto label;
          }
        }

        //using bounding points as base point to calculate closest point//
        Double ct;
        xCrv.ClosestPoint(point, out ct);
        Point3d cp = xCrv.PointAt(ct);
        Double radius = point.DistanceTo(cp);;

        //point outside existing circle -> find the closest distance//
        foreach(Circle crc in circles){
          Double dist = point.DistanceTo(crc.Center) - crc.Radius;
          if(dist < radius){
            radius = dist;
          }
        }


        Circle circle = new Circle(point, radius);
        circles.Add(circle);

        if(circles.Count >= Num){
          break;
        }

      }while(attempts < 1000);

    Acrv = circles;


結果圖片:


基本circle packing
顯示圓內點
把之前找尋最近點的function 導入,產生不同圖形
把之前找尋最近點的function 導入,並刪除交集線段,產生不同圖形

套用在不同曲線上



沒有留言:

張貼留言