2014年3月22日 星期六

隨手翻/AADRL 2013/2014 – Extending Rhino+Grasshopper with C# – Session 1: Introduction to scripting

原文出處:www.vitruality.com
Copyright © 2013 VITRUALITY & Andrea Graziano

AADRL 2013/2014 – Rhino+Grasshopper with C# 延伸閱讀 – Session 1: 指令碼寫作初探
AADRL 2013/2014 – Extending Rhino+Grasshopper with C# – Session 1: Introduction to scripting


Grasshopper 本身是個非常強大的工具,能夠產出許多令人驚豔的作品。然而,礙於它的運作方式,有幾種傳統架構的演算法無法套用於這種以元件互連為基底(component-based)的系統。雖然它包含大量的元件,但仍有一些領域,其所缺少的功能必需透過自行撰寫新的元件,來擴展它的功能。此外(如果你有寫作腳本的經驗)經常有會這類的念頭,發現某些需要的元件,可能只是簡單幾行代碼的改寫,就能更貼近你需要的功能。
Grasshopper in its natural state is a very powerful tool capable of many impressive feats. However, due to the way in which it operates there are some kinds of algorithms than are not possible to fully reproduce using its component-based system that would be trivial to implement in a traditional programming language. Although it contains a vast array of components there are still some areas in which it is lacking in functionality and it can therefore be necessary to extend it by writing new components yourself. Also, there are often things which, while possible, require long strings of components to achieve what might be more simply implemented as just a few lines of code.


C#
(讀音:C Sharp)

在此,我們將以 C# 的使用作為我們的指令碼語言。之所以選擇這個語言,是因為它的語法相似於其他的 C 語言 (C,C++,Objective-C),以及那些基於它開發的語言(JAVA、JavaScript、Processing、D、PHP 等),也因為它可用於一些Rhino 的程式編寫 ,例如編寫自訂的 Grasshopper 元件,甚至包含整個 Rhino 外掛程式(Plugins),這是目前 Python 所不能的。然而,上述提到的這些例子,這些與 Grasshopper/Rhino 搭配的也僅只是某些程式的特色,不論你是否你正在使用 C#、Visual Basic 或 Python 結果應該沒有不同(意指程式都應該寫得出來)— 只有程式的語法不同,所以請選你所愛,愛你所選的程式來撰寫。
 C# 
In the examples given here we will be using C# as our scripting language. This has been chosen because its syntax should be familiar to people who have used any other of the ‘C’ family of programming languages (C, C++, Objective-C) or those based on it (Java, JavaScript, Processing, D, PHP etc.) and also because it can be used for some of the more powerful forms of programming within Rhino, such as writing custom Grasshopper components and even whole other Rhino plugins, which Python currently cannot. However, none of the parts of these examples which interact with Grasshopper/Rhino are language specific and should be the same whether you are using C#, Visual Basic or Python – only the language syntax will be different, so you should be able to apply the following to whichever language you prefer to use.


C# 是 C 語言中以 .NET framework 為架構所設計的一支程式語言,意味著它可以與所有標準的.NET 程式庫一起使用,並且相容於其他.NET 語言(如 VB.NET)所編寫的代碼。它是一種列屬於受控代碼*(managed language)的語言,意思你不需要手動刪除物件來釋放記憶體 — 程式語言本身會自動清理。
C# is a version of C designed around the .NET framework, meaning that it can be used with all of the standard .NET libraries and is compatible with code written in other .NET languages (such as VB.NET).  It is a managed language, meaning that you do not need to manually delete objects to free up memory – the language cleans up after itself automatically.
受控代碼的執行*執行代碼時,運行庫編譯器(runtime-aware compiler)在受控執行環境下,將中間語言(Intermediate Language)編譯成本機的機器碼。受控執行環境可為代碼插入垃圾回收異常處理類型安全陣列邊界和索引檢查等,以保證代碼安全的執行。


在之後的介紹裡,不事先作任何 C# 本身知識的假設,我們將專注于部分對於 Grasshopper 特別有幫助的程式語言,不留戀於不相關的語法或程式設計理論 — 如果需要更全面地瞭解語言,你應該看這裡C# Tutorials便於過渡到 C# ,知道它與其它編碼語言的關係將有助於了解這些相似腳本在不同語言間的差異:
For the following, no prior knowledge of C# itself is assumed, however we focus on those parts of the language which are specifically useful for grasshopper without dwelling too much on syntax or programming theory except for where it is particularly relevant – for a more comprehensive overview of the language you should look elsewhere. To ease the transition into C# for those familiar with scripting in a different language it can be useful to know how it relates to other coding languages:
Copyright © 2013 VITRUALITY & Andrea Graziano


C# 最明顯的是受 C 語言和基於它的語言(C++ 和 JAVA 還有 Processing)影響,所以共用相似的語法。然而 C# 也建立在 .NET 架構上,使它能夠與其他 .NET 架構的程式互通有無,像 Visual Basic.NET( Grasshopper 本身就是由 VB.NET 所撰寫)和 IronPython 等語言。
C# is most obviously influenced by C and the languages based on it and so shares similar syntax with C++ and Java (and hence Processing). However it is also built on top of the .NET Framework which allows it to use and interact with code written in other .NET languages such as Visual Basic.NET (in which Grasshopper itself is -currently- written) and IronPython.


RhinoCommon
RhinoCommon 是個資料庫,諸如我們使用的點、 曲線和曲面還有 Rhino 裡的幾何物件等等。
這裡是整件事情可能會令你困惑的地方,尤其當你想找網路上的案例,但是別忘了 RhinoCommon 是一個相對較接近於開發的資料架構 — 在那之前,使用另一個稱為 RhinoDotNet 的資料庫,這是相對更難去使用的(相信我,我知無不言)。有些人仍然使用 RhinoDotNet,網路上也存在很多這架構的例子,因此如果你複製貼上一個範例,它似乎不會運作。舉例來說,當你發現程式裡有很多的類型是從 'On' 和 'MRhino' 開始,這代表你可能在看著一個 RhinoDotNet 的範例,而不是 RhinoCommon 。
 RhinoCommon 
RhinoCommon is the library that we use for interacting with Rhino and geometry objects such as points, curves and surfaces.
Here’s where things could get confusing if you’re looking for examples on the web, however: RhinoCommon is a relatively recent development – before that there was another library that was used for the same thing called RhinoDotNet, which was a lot harder to use (trust me on this, I know whereof I speak).  Some people still use RhinoDotNet, however, and there are a lot of examples of it around on the internet, so be aware of this if you’re copying an example and it seems not to be working – if there are a lot of types starting with ‘On’ and ‘MRhino’ then you’re probably looking at a RhinoDotNet example rather than a RhinoCommon one.  Just a word of warning.


 Example 1: A simple C# scripting component 
首先,打開新的 (空白)Grasshopper。
所有的腳本編寫元件可以在 "Math" 這個分頁找到,在 "Script" 的標題下。按一下 "C# Script" 元件並將其拖曳在畫布上。
 Example 1: A simple C# scripting component 
First, open grasshopper with a new (blank) file.
All the scripting components can be found on the Math tab, under the heading ‘Script’.  Click on the ‘C# Script’ component and drop it onto the canvas.
如果你放大來看,元件應該看起來像這樣: If you zoom in enough, the component should look something like this:


預設情況下,它已放入稱為 "x" 和 "y" 的輸入端,與 "out" 和 'A" 的輸出端。 您可以自訂這些項目。 By default, it has inputs called ‘x’ and ‘y’ and outputs called ‘out’ and ‘A’. You can customize this.


每個輸入與輸出端旁有 "+" 和 "-" 標誌,允許添加和刪除輸出與輸入的接口。 按一下 "y" 旁邊的 "-" 來刪除這個輸入的接口。 The little ‘+’ and ‘-‘ signs next to each of these lets you add and remove 
inputs. Click on the ‘-‘ next to ‘y’ to remove that input.


第一個元件,我們要做超級簡單的東西 — 我們只要將 "X" 這個數值除以2。
但是,我們在開始編碼之前,讓我們告知程式:該元件 x 是一個數值。要執行此操作,請使用滑鼠並對 "x" 按下右鍵。在下拉式功能表中,按一下 "Type hint(類型提示)" 然後點選 "double" 。"double" 是程式語言中用來表達數值的一個名稱 (更具體地說,它是雙精度浮點數/小數點後兩位數值的簡稱)。上面的是 "int" ,是用來儲存整數(即一個數值的整數位)。
For'our first component, we’re going to do something super simple – we are just going to take in a number (‘x’) and divide it by 2.
But, before we start coding, let’s tell the component that x is supposed to be a number. To do this, right click on the ‘x’ input. In the drop-down menu, click on ‘Type hint’ and then ‘double’. ‘double’ is just programmer-speak for a number (more specifically, it is short for double-precision floating point number). Above that is ‘int’, which is for storing integer (i.e. whole) numbers only.

現在產生一個拉桿並將它連接到輸入端 "x"
Now create a slider and plug its output into ‘x’


現在我們已經準備就緒!雙擊 "C#" 單元的中心點
之後將會出現這個視窗
Now we’re ready to rock!  Double click on the centre of the
C# component and the following sight should greet you:

每當元件變動而更新時,該副程式 "RunScript" 會被調用,並將元素內的輸入/輸出端進行參數的傳遞 — 在本例中我們的輸入端為 "x" ,輸出端為 "A" .因此我們可以叫出它們並修改它們。等等,你可能會說,C# 元件上不是有兩個輸出端嗎?"out" 又是怎麼一回事呢?
Whenever the component gets updated, the subroutine RunScript gets called and the inputs and outputs of the component get passed in as parameters – in this case our input ‘x’ and our output ‘A’ so that we can use them and modify them. But hang on, you might say, don’t we have two outputs on this component? What about ‘out’?


"out"' 存在於每個元件,主要是用來將消息發送到外面。舉例來說,我們以老梗中的老梗 "Hello world!" 為例,在副程式 "RunScript" 下的大括弧內,鍵入下圖的訊息:
‘Out’ is there on every scripting component and is used to send messages to the outside world. To demo this, let’s do the traditional ‘hello world’ example. In between the curly brackets below RunScript, type this:

腳本元件具有一個內置的副程式叫做 "Print(列印)",用於輸出內容到 "out" 這個輸出端。如果您在腳本(右下角)視窗上按 OK 按鈕,並將 "panel(面板)" 元件插入 "out"。如果沒有意外,"Hello World!" 應出現其中。
Scripting components have a built-in subroutine called ‘Print’ which is used to output stuff to the ‘out’ output. If you press the OK button on the script window and plug a panel component into ‘out’. If all has gone well, the words “Hello World!” should appear therein.



這是主要用於除錯的時候 — 可以透過訊息的輸出,來提醒使用者該腳本是進行到何處。腳本運行時出現的任何異常也會在這裡顯示。 
讓我們來解密這小小的程式碼片段到底在做什麼。第一行是一個敘述 — '//' 這符號告訴程式解讀器,運行腳本時請完全無視於其內部的內容 — 通常它只是有助於程式撰寫時下註解,實際上在程式內不做任何事。"Print" 是副程式的名稱,後方括弧中包含我們要傳遞的參數 — 在這例子中就是 "Hello World!",由於這串話是由括號所標記,表示它作為字串文本 (即告訴解譯器它是文本,而非變數名稱或其他性質的物件)。";" 是用來對解譯器宣告,這是語句的結尾。
好了,讓我們用腳本來做一些有用的東西。回到腳本編輯器並打入下面的內容:
This is useful mainly for debugging purposes – you can output little messages that tell the user how the script is doing. Any exceptions which occur when the script is running will also be reported here.
Let’s just break down what this little snippet is doing. The first line is just a comment – the ‘//’ denotes it as such and tells the interpreter to completely ignore the rest of the line when running the script – it’s there just for the benefit of the programmer and doesn't actually do anything. ‘Print’ is the name of the subroutine we are calling. The brackets contain the parameters we are passing – in this case ‘Hello World’ which is enclosed in speech marks which denote it as a string literal (i.e. tell the interpreter that it is a bit of text and not a variable name or anything else). The ‘;’ just tells the interpreter that this is the end of the statement.
OK, let’s now make the script do something useful. Back to the script editor and below what we’ve just written put:




在這裡,我們已經宣告(declare)一個 double 類型的變數稱為 '分母(denominator)' ,並分配給它一個等於 2 的值。在 C# 宣告的方式就是先聲明它的類型,然後對這個變數命名。您可以(非必要)在同一行上為這個變數配上一個值。最後別忘記加上代表結尾的 ";" 符號。
Here, we’ve declared a variable of type double called ‘denominator’ and assigned a value of 2 to it. In C# you declare a variable by putting its type name and then it’s own name. You can optionally then assign a value to it on the same line if you wish. Don’t forget the ‘;’.

之後在下面幾行寫下:
Below this, write:



這邊宣告一個名為 "answer" 的新變數,並分配給它計算後的結果,即我們輸入端變數 x 數值除以我們剛定義的 "denominator 分母" 變數。

然後再增加下面這行:

This declares a new variable called ‘answer’ and assigns to it the result of a calculation whereby our input variable x gets divided by the variable ‘denominator’ we just defined.
Now add:




這將分配計算好的答案作為輸出端參數 "A" 的數值。這樣就完成了!

This assigns the answer  we just calculated to our output parameter ‘A’. And we’re done!


按 OK 作為確認,然後將另一個面板連接 "A"。現在你應該發現,無論你輸入多少的數值到 "x" ,"A" 都會產生一半的輸出值.
Press ‘OK’ and then plug another panel into ‘A’.  You should find that whatever value you put into x, you get half of it out of A.


顯而易見地,您可以透過修改這個範例
來進行任何你需要的數學公式撰寫。
Obviously, you could modify this code to use any
mathematical formula that you liked.



 Example 2A: Drawing a line between two points. 
 Objects, Value and Reference types. 

前面的範例固然是很好,但這並不是什麼不能用 function 元件做到的事情。讓我們轉到幾何的操作。首先,我們要試圖撰寫出兩點繪製一直線 - 也就是標準 'Line' 元件的功能。 在 Rhino 介面裡,創建兩個點並在 Grasshopper 介面中使用兩個 "point" 元件來指向它們。
The previous example is all very well but of course it isn't anything that couldn't be done with a function component.  Let’s move on to how to manipulate geometry.  For starters we’re going to duplicate the functionality of the standard ‘Line’ component to draw a line between two points.
In Rhino, create two points and reference them in grasshopper with two point components.


現在添加一個新的 C# 元件。按右鍵的 x 和 y 的輸入端,並分別將其名稱更改為 "PtA" 和 "PtB"。設定 "'Point3d" 為這兩者的類型提示。將輸出端 "A" 的名稱更改為 "Ln"。
Now add a new C# component. Right click on the x and y inputs and change their names to ‘PtA’ and ‘PtB’ respectively. Set both of their type hints to ‘Point3d’. Change the name of output A to ‘Ln’.



將 Grasshopper 的兩個點元件連接到 PtA 和PtB 。現在按兩下打開腳本編輯視窗;請注意 RunScript 的參數有所不同!現在的架構反映我們的輸入端與輸出端 (確認 PtA 和 PtB 現在是 Point3ds 的類型,否則此下一個階段將不會運作)所做的更改。
Plug the two points into PtA and PtB.Now double click to open up the script window. Note that the parameters of RunScript are different now to reflect the changes we have just made to the inputs and outputs (make sure that in particular PtA and PtB are now Point3ds, otherwise this next bit won’t work).



加上:
Add in:

在這裡我們建立一個名為 "myLine" 的新線型物件,做為 PtA 與 PtB 兩點間的連接。我們使用諸多可以生成線的副程式之一,來畫出這兩點之間的線。當你在輸入的同時,智慧感知模組會跳出並顯示接下來可以放置的參數類型:
Here we create a new line object called ‘myLine’ that runs between PtA and PtB.  We are using one of lines built in constructor subroutines that takes in the two points that the line runs between.  As you’re typing the intelligent helper should pop and show the types of parameters you can put in to create the line:


構建線段的方式有以下幾種 — 例如透過放置一個起點並搭配一個向量去跟隨。接下來這些將是比較技術面/相對而言非常重要,所以仔細聽好了:
在 C# 中的 objects 是參考類型,而其他變數,如 double 和我們前面使用的 Point3ds 都是數值類型,這些不同造就了資料的行為方式。這是什麼意思?
There are a couple of other ways of constructing lines – for example by putting in a start point and a vector to follow instead.
This next bit is both technical and incredibly important, so listen up:
In C# objects are reference types, while other variables such as the doubles and Point3ds that we have previously used are value types, which changes the way they act. What does this mean?

這主要是:
Object 變數並不直接持有這些物件本身,僅對物件做指向。如果您想要創建一個新的物件,必須使用上關鍵字 "new"。下面將證明這部分產生的影響:
Mainly this:
Object variables do not hold the objects themselves directly, only references to objects.If you want to create a new object you have to use the ‘new’ keyword.
To demonstrate the implications of this consider:


這將創建一個名為 "a" 的新變數,並分配給它等於 5 的數值。然後創建另一個新變數稱為 "b" ,並將這個數值複製給它,因為 double 是數值型別。a 和 b 兩者都等於 5,但他們仍是兩個單獨的值 — 也就是說如果您更改 a 的值,它不會影響 b。
This will create a new variable called a and assign a value of 5 to it.  It then creates another variable called b and copies the value of a into it, because double is a value type. Both a and b will be equal to 5, but they are still separate values – if you change the value of a then it will not affect b, for example.

物件的工作方式不同:
Objects work differently:


這將創建一個稱為 myLine1 的新變數,然後將它分配一個新的線物件。然後創建一個稱為 myLine2 的新變數,並分配它指向目前的 myLine1。但這實際上並不是去創建一個新的物件 — 而是變相的給了 myLine1 另一個替代名稱 — myLine1 和 myLine2 現在都指向相同的物件。這意味著如果你做些什麼來改變 myLine1,它也會影響 myLine2!
假如你想要創造一個新的物件,源自於 myLine1 自身的複製,你應該這麼做:
This creates a new variable called myLine1 and then assigns it a new line object.  It then creates a new variable called myLine2 and assigns to it the reference currently held by myLine1.  But this does not actually create a new object – instead you have effectively just given the line in myLine1 another alternative name – both myLine1 and myLine2 now both point to the same object.  This means that if you do something to change myLine1, it will also affect myLine2!
span style="color: #999999; font-family: Georgia, Times New Roman, serif;">If you did want to create a new line which was a copy of myLine1 then you should do this instead:

這需要一些時間去習慣,一不注意這將會變成搞混的主因。 總之,回到該範例。完成這個程式只要將 myLine 指定到 Ln ,然後按下OK;恭喜你,你擁有屬於你自己的畫線元件! 
This takes a bit of getting used to and can be a major source of cockups if you’re not paying attention to it. Anyway, back to the example. To finish up just output myLine to Ln:
Click ‘OK’ and you should now have your very own line component.




本次操作的範例可以在這邊下載 
The example files for this session can be downloaded here:
C# Examples 1 & 2A

Popular Posts