2013年3月31日 星期日

<心得> Introduce class interface in Grasshopper C#

目標 : 介紹基本介面關係概念以及簡單介面範例。

參考文獻 :

http://www.cnblogs.com/oomusou/archive/2007/05/07/738311.html


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處描述 :

interface用在當一個物件須和其他物件共同合作時,為了確保其他物件有使者想要的method,所以定下interface,interface就是多個class中裡面有許多相同的method,所以抽取出來作為介面讓大家共同使用,可以在各自的介面中使用相同method名子卻可以有不同的功能。

而abstract class是用在整個繼承體系的最上層,用來定義出整個繼承體系該有哪些method,子類別可以對這些method加以override或維持和abstract class相同的功能,使用繼承技術時,我們會將所有method由abstract class去宣告,然後由各子類別去override,若不得已某些class有自己的特殊method,則由該class自行宣告。關於inheritance請參考Introduce class inheritance in Grasshopper C#

在OO主要有兩種技術:繼承(Inheritance)和組合(Composition),而abstract class就是用在使用繼承技術時,而interface則是用在使用組合技術時。

一旦使用組合時時,就牽涉到一個問題,你如何確保被你組合的物件有某個method當你使用繼承時,因為所有的method都會被繼承,這不是問題,但組合就不一樣了,所以你必須建立一個interface,強迫要被你組合的物件,需實做這個interface,這樣當你要使用該物件時,才能確保有某個method可以呼叫。

由Fig01 我們可以看出有兩個基本的父層(Base),然後子層Parent(Parent繼承Base)和子層child(child繼承Parent)分別繼承上層的關係,只有在Parent中創建method 的方法叫做ApplyEdiable然後子層Child 繼承ApplyEdiable的method

由Fig02 因為ParentA 和 ParentB 有共同的method 叫做ApplyEdiable,所以我們創建BaseC讓裡面擁有名為ApplyEdiable的method,再創建一個interface名為IBaseCAction來真正實踐ApplyEdiable的內部功能,這樣在ChildA、ChildB就可以共同使用ApplyEdiable的method


Fig 01. inheritance 繼承關係圖


Fig 02.interface 介面關係圖


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處示範基本interface創建方式

//使用關鍵字interface
//介面名為
IcircleA
//在介面中只能宣告方法名子與參數,真正實作部分必須在各類別使用中實現
  public interface IcircleA{
    Circle createCircle(Point3d center, double rad);
  }

//類型shapeA  使用介面 IcircleA
  public class shapeA : IcircleA{

    public Circle createCircle(Point3d center, double rad){
      Circle mycircle = new Circle(center, rad);
      return mycircle;
    }

  }

//創建類型shapeA  
//並呼叫介面方法且指定參數

  shapeA mytest = new shapeA();
    A = mytest.createCircle(new Point3d(0, 0, 0), 10.0);

輸出圖像:
使用interface所創建的圓

//如果遇上多個interface 中有著相同的名稱method,必須創建原始介面,才能呼叫方法
//參考文章 : http://msdn.microsoft.com/zh-tw/library/ms173157(v=vs.80).aspx

//請注意interface IcircleA and IcircleB擁有相同的mehod 名稱為 createCircle


  public interface IcircleA{
    Circle createCircle(Point3d center, double rad);
  }

  public interface IcircleB{
    Circle createCircle(Point3d center, double rad);
  }


//class shapeA實行介面 IcircleA,IcircleB兩個介面
//注意 Circle IcircleA.createCircle 的Circle 之前不能加上public

 public class shapeA : IcircleA,IcircleB{

    Circle IcircleA.createCircle(Point3d center, double rad){
      Circle mycircle = new Circle(center, rad);
      return mycircle;
    }

    Circle IcircleB.createCircle(Point3d center, double rad){
      Circle mycircle = new Circle(center, rad / 2);
      return mycircle;
    }

  }


//instance shapeA 

    shapeA mytest = new shapeA();

//呼叫createCircle 藉由已經存在的mytest物件中執行名為IcircleA的介面
//因為使用者有兩個相同的method 所以必須明確的告知compiler 哪個才是你想執行的介面方法
//所以呼叫到介面的那層 

    shapeA mytest = new shapeA();

    IcircleA circleA = (IcircleA) mytest;
    A = circleA.createCircle(new Point3d(0, 0, 0), 10.0);

    IcircleB circleB = (IcircleB) mytest;
    B = circleB.createCircle(new Point3d(0, 0, 0), 10.0);



輸出圖像:



使用相同名稱的method所創建的圓



////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//使用時機 


>>何時使用main function
若公司不到5個人,老闆只要將要做的事情規定好(function),然後指定某個人去做某件事情即可(main function),這種『中央集權』的方式就是『程序導向』,在小公司是很有效率的。

>>何時使用OOP
若是上千人的公司,老闆不可能同時指揮所有人,所以有經理,科長等管理階層出現,老闆只要管好經理,經理只要盯住科長,科長再分配事情給小職員做,這種『分層負責』,每個人將自己分內做好的方式,就是『物件導向』,大公司一定得用這種方式才能管理。
軟體開發也是一樣,小程式用『程序導向』方式寫就很方便,但大程式則必須用『物件導向』方式才好管理,軟體開發和企業管理是相通的。
>>何時要用 interface 而不要使用父類別
盡量用 interface, 盡量不要用父類別, 如果例子簡化成 '動物一定有眼睛, 需要由各子類別去實作', 而 '動物' class 裡也沒別的定義, 那麼就是使用 interface 的標準狀況, 完全不必考慮是否要使用 abstract class

>>何時要用父類別而不要使用 interface
如果父類別有寫一些 method, property ,你想讓子類別能繼承後就順便有它們,就用父類別, 至於父類別要宣告成 class or abstract class, 看父類別想不想被 new

在 C#, VB.NET 語言裡, 一個類別只能繼承一個 class, 可以實作多個 interface, 所以換個角度來看, 寫程式時不要這麼 '敗家' , 隨便地就將維一一個繼承的機會就用掉了  


////////////////////////////////////////////////////////////////////////////////////////////////////////////////

2013年3月29日 星期五

<心得> Introduce class inheritance in Grasshopper C#

目標 : 介紹基本繼承關係概念以及簡單繼承範例。


參考文獻 :

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處描述 :

在介紹繼承之前先簡單介紹一下類型(class)

class 中包含三個部分
1.//properties
2.//constructor
3.//methods

在OO(object-oriented )語言中就像C# /C /C++/JAVA所有概念都是由物件所組成,而class就像是藍圖一樣,像室內設計師在設計圖面時,可能會有水電圖、天花圖、平面設計圖、迴路圖等圖面,而藍圖就是管理這些所有圖面的資料夾。

依照設計師的使用需求不同,設計師可以套繪不同的平面在同一個底圖上。我們可以把所有的圖面(水電圖、天花圖)想像成class中的methods,當設計師今天只需要平面設計圖與天花圖的時候,只要從藍圖中抽取出需要的圖面即可,這樣的藍圖(class)也間接的幫設計師做好管理打包(encapsulation)的工作。

單然很多資料夾之間也可能存在著檔案共享或是繼承的關係,所以往後再繼續介紹類型的其他好用之處。

msda介紹class
class is a construct that enables you to create your own custom types by grouping together variables of other types, methods and events. A class is like a blueprint. It defines the data and behavior of a type. 


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處示範基本class 創建方式

//創建圓法一:

  public class WeiA{

//properties
//型態內建的變數我們就稱之為properties

    Point3d point;
    double rad;

//constructor
//你可以有多個constructor在這裏我們有兩個,如果有多個以上constructor我們稱之為overload
//要注意的是constructor 的名稱必須與類型名稱一致,而且當類型一旦被創建的//同時,即是
//呼叫constructor,constructor並不會回傳任何型態。
//此處this 關鍵字是把constructor 中的parameter 當成類型中的argument來使用的意思

    public WeiA(){
    }

    public WeiA(Point3d point, double rad){
      this.point = point;
      this.rad = rad;
    }

//methods
//即是類型中你要使用的方法

    public Circle drawWeiA(){
      Circle circleA = new Circle(point, rad);
      return circleA;
    }

  }


//以此為例 double mynumber
//就像我們常用的double 雙浮點小數
//我們創建名為mynumber的物件,型態為double 
//在此我們創建名為myA 的物件,型態為WeiA 
//而這部分new WeiA();即是呼叫class 中的constructor

 WeiA myA = new WeiA(new Point3d(0, 0, 0), 10.0);


    A = myA.drawWeiA();
當我們創建好myA 的物件時可以看到使用者自訂的類型出現在選單中。而這個選單就是使用者所創建的method

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

輸出圖像:
你已經簡單的使用類型創建出一個圓
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//創建圓法二:


當然你也可以用rhinocommond SDK的方法來直接創建一個圓(因為C#本身就是OO概念,我在這邊只是在示範
如何創建自己的類型,使用畫圓的方式而已)

程式碼:
Circle mycircle = new Circle(new Point3d(0, 0, 0), 10.0);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處補充修飾詞

類別或成員可以使用下面幾個修飾詞來定義存取方式:
public:存取沒有限制。
private:只有類別本身能存取。
protected:只有類別本身和其衍伸類別本身能存取。
internal:只有同一個 Namespace 的地方能看到。

當不希望類別再被繼承下去,可以使用 selected 修飾詞,類別將不可再被繼承,否則會發生錯誤訊息。
EX:public sealed class A{}

相對,如果希望類別一定要被繼承後才可以使用,本身無法直接使用時,可以使用 abstract 修飾詞。
EX:public abstract class B{}



範例:

  private class WeiA{

WeiA(){
}

  }

or 


  protected class WeiA{

WeiA(){
}

  }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處示範基本inheritance 創建方式

//WeiA 是superclass也是父層


public class WeiA{


    Point3d point;
    double rad;

    public WeiA(){
    }

    public WeiA(Point3d point, double rad){
      this.point = point;
      this.rad = rad;
    }

    public  Circle drawWeiA(Point3d point, double rad){
      Circle circleA = new Circle(point, rad);
      return circleA;
    }

  }

//WeiB繼承WeiA的類型內部資料
//WeiB稱之為subclass也是子層

  public class WeiB : WeiA{

    public WeiB(){
    }

  }




    //create object myA//
    WeiA myA = new WeiA();

    //create object myB//
    WeiB myB = new WeiB();


    A = myA.drawWeiA(new Point3d(0, 0, 0), 10.0);
    B = myB.drawWeiA(new Point3d(0, 0, 0), 5.0);

//在此我們要注意的是在myB的物件中並沒有創建drawWeiA的method,使用者卻在創建myB的物件後可以調動
//drawWeiA的方法,原因是 WeiB : WeiA(WeiB類型繼承了WeiA的內部資料)




WeiB類型繼承了WeiA的內部資料
使用inheritance 創建出基本圖形
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//此處示範基本virtual and override創建方式

//當我們從父層繼承而來的方法時卻發現內部資料需要被改變"但method名子"卻又相同時,那該如何解決?
//答案就是使用關鍵字virtual and override
//在父層相同名子的method 中前面加入關鍵字virtual 當作抽象的方法
//在子層相同名子的method 中前面加入關鍵override 當作覆寫的方法


  public class WeiA{

    Point3d point;
    double rad;

    public WeiA(){
    }

    public WeiA(Point3d point, double rad){
      this.point = point;
      this.rad = rad;
    }

    public  virtual Circle drawWei(Point3d point, double rad){
      Circle circleA = new Circle(point, rad);
      return circleA;
    }

  }

  public class WeiB : WeiA{

    public WeiB(){
    }

    public  override Circle drawWei(Point3d point, double rad){
      Circle circleA = new Circle(point, rad / 2);
      return circleA;
    }

  }





    //create object myA//
    WeiA myA = new WeiA();

    //create object myB//
    WeiB myB = new WeiB();


    A = myA.drawWei(new Point3d(0, 0, 0), 10.0);
    B = myB.drawWei(new Point3d(0, 0, 0), 10.0);

//我們可以在此發現都是調用一樣的方法drawWei 輸入一樣的參數 new Point3d(0, 0, 0), 10.0
//那為什麼出來的圖形卻是不一樣,答案就是使用覆寫method內部資料的關鍵字。
// A = myA.drawWei(new Point3d(0, 0, 0), 10.0);
// B = myB.drawWei(new Point3d(0, 0, 0), 10.0);





使用一樣的method 與參數,卻產生出不一樣的半徑圓



////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//此處延續virtual and override補充 new
//資料再補充 override and new 的差別
























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 導入,並刪除交集線段,產生不同圖形

套用在不同曲線上



2013年3月15日 星期五

<分享>Group flatten List with different number

目標 : group flatten List with different number

檔案 : https://dl.dropbox.com/u/29860061/group%20flatten%20List%20with%20different%20number%28web%29.gh

比較 :


圖1 : 因為GH 內建所提供的data mapping 可以把flatten list 群組但是只限定每個branch中有固定的item,實在是不怎麼聰明。




圖2 : 所以我寫一個讓使用者可以依據想要的數量分別對使用者輸入flatten list,後進行不同數量的分組行為,大家可以看到處理後的數值在最右邊,分別依據使用者所輸入的5、4、5..等不同的群組內的數量作grouping

圖3 : 比較data mapping 與 C# grouping 的差別

注意 -----------> 
1.使用者輸入的數值請把它變成flatten list
2.輸入的分組數目總數請等於使用者所輸入flatten list的總數相等

example ->
分組數 3、3、6、2 -> 總數 3+3+6+2 = 14
flatten list的總數 -> 就是要數入14個數值






Main Code:


 DataTree<Point3d> myTree = new DataTree<Point3d>();

    //copy original objects
    List<Point3d> myLsPoints = new List<Point3d>();
    myLsPoints = xpoint;

    int tempInt;
    Print("count ={0}", yint.Count.ToString());
    int tempInt2;

    for(int i = 0;i < yint.Count;i++){

      tempInt = yint[i];

      GH_Path aa = new GH_Path(i);

      if(i == 0){

        tempIntA = 0;
      }else{
        tempInt2 = yint[i - 1];
        tempIntA += tempInt2;

      }
      Print("start index tempIntA ={0}", tempIntA);
      tempIntB = tempIntA + tempInt - 1;
      Print("end index tempIntB ={0}", tempIntB);

      List<Point3d> LsPoints = new List<Point3d>();
      for(int j = tempIntA;j <= tempIntB;j++){
        LsPoints.Add(myLsPoints[j]);
      }

      myTree.AddRange(LsPoints, aa);
    }

    A = myTree;