An short introduction to RhinoScript
假 使从字面上来直译,RhinoScript可以翻译为「犀牛脚本」。RhinoScript是架构在VB(Visual Basic)语言之上的Rhino专属程序语言,大致上又可分做Marco与Script两大部分。以下简单地说明Marco与Script两者之间的差 别。
Marcos = Static (fixed, linear sequence)
Scripts = Dynamic (non-linear)
‧flow control (skipping and repeating lines)
‧variable control (logical and mathematical operations)
‧input and output (user interaction)
简单地说,在Rhino中的 Marcos (可以称做宏)就像是一个口令一个动作的叫Rhino帮我们执行「动作」(actions)。Marco 的命令行是线性、静态的,意思就是说,如果我们下了一百条指令,Rhino就会从第一条、第二条、第三条、、、这样一路执行到第一百条命令,无法跳跃也无法重复执行某个命令。相对而言,Scripts
则是动态且非线性的。首先,透过 Flow Control
我们可以跳过或者重复某些命令行,而Variable Control
则可以让我们创造出逻辑与数学的运算,Input and Output (I/O)则是使用者与Rhino之间的互动,使用者可以送进许多种类型的资料 (data),再经过运算之后以许多种数据形态输出。
本文的主要目的在于说明 Marcos
与 Scripts 之间的差别,之后将进一步对 Scripts 的语法、功能与运用方式做进一步的说明与提供范例。
(1) 计数循环 For...Next (2) 条件式循环 Do While...Loop (前侧式)
Do Until...Loop (前侧式)
Do...Loop While (后测式)
Do...Loop Until (后测式)
Do...Loop (无穷)
While...Wend (如果为真) (3) 数组循环 For Each...In...Next
A = B A and B are equal
A <> B A and B are not equal
A > B A is greater then B
A >= B A is greater then or equal to B
A < B A is less then B
A <= B A is less then or equal to B
有了上述比较式的概念,我们就可以开始说明条件式循环了。首先,我们说明两个前侧式循环:Do While...Loop 以及 Do Until...Loop。如果我们用口语的方式来翻译,会像是下面的说法:
Do While...Loop 当符合***,就一直做
Do Until...Loop 一直做,直到符合***为止
An Short Introduction to RhinoScript
在 进入其他VB语言与RhinoScript的说明之前,先提示一点轻松但是却重要的提醒。由于其实写程序就跟做设计很类似,设计者如果没有良好的档案管理 方法,经常会图纸乱丢又乱塞,等到某天想要用时却找不到,就算找到了也忘记这张图到底是何时完成的,或者忘了最后的版本是谁修正的。当我们程序越写越多时 也会面临到上述的情形,因此,保持良好的工作习惯以及建立正确的档案管理统对日后不仅有莫大的帮助也是不可或缺的必备功课。
另一方 面, 一个程序除了要让计算机读得懂之外,常常也要让朋友或者是其他使用者能够阅读,于是乎,有一些共通的标注方式,就变成不同用户要读懂程序的重要媒 介。一般来说,一个较为完整的VBScript会由四个部分所组成:(1) Option Explicit area (语句区块)、(2) Main subroutine (主要的子程序)、(3) Addtional subroutines and functions (附加的子程序与函数式)、(4) Execution commenting (执行批注),其详细说明如下:
Option Explicit
'Script written by <insert name>
'Script copyrighted by <insert company name>
'Script version <insert date, time>
(2) Main subroutine
VB语言中的 Main subroutine 由四个部分所组成,语法如下:
Sub Main()
<Variable declaration area>
<Code(conditional statemenets, loops and I/O code)>
End Sub
上面这个范例句是没有其他子程序 (Subs) 的情况,若有其他的子程序,在 Sub Main( ) 与 End Sub 之间则放放进呼叫其他子程序的语句。
(3) Additional subroutines and functions
在 VB语言中,Subs 与 Functions 几乎可以做相同的事。但是,Functions 可以有「回传值」(returns) 来做运算的动作,而 Subs 则无法。因此,通常我们将一些数值的运算交给 Functions 来做。另外,在 VB中 Function 可以自己呼叫自己,也就是可以做出「递归」(recursion) 运算,而且呼叫 Function 时可以在其左方直接加上一些函数表达式 (像是:Sqr、Cos 或 Chr 等) ,使 Function 就如同是内建函数一般。比较详细的 Function 实际使用方式之后会进一步说明,在此仅作初步介绍。最后,我们经常会在 Sub 或 Function 的名称左方看见一对括号,这表示可以送入变量,在 VB 中, Sub 与 Function 都可以送入变量,而当我们即使不送入变量时,也会以空的括号来表示。
A = 10 + 20
B = 10 + 20 * 0.5
A = Sin(10 + 20) + Sqr(0.5)
B = Tan(10 + 20) / Log(0.5)
在这面的这几个式子中出现了四种类型VB语言码:(1) Numbers (数值)、(2) Variables (变数)、(3) Operators (运算符)、(4) Functions (函数)。 (1) Numbers & (2) Variables
在 VB中,数字可以是整数或具有小数点,同时也可是正数或负数。式子中的 A,B 我们称为变量,在VB语言中所有变量都必须被宣告,而宣告的方式有很多种,最常用的宣告语句是 Dim ,在VB中 Dim 的意思基本上就像是定义。当我们在宣告陈述区写下 Dim A,B,表示计算机会为我们预先留下内存的位置给 A,B 这两个变量。有时后我们也会看见有人这样写:Dim A As String,这样的写法是事先告诉计算机这个变量的数据类型,差别在于因为不同的数据类型计算机为我们预先留下的内存位数 (bits) 是不同的。不过,这时候我们还不需要了解的如此深入,只要知道变量的宣告方式即可,其他的知识大略有个概念即可。另外,上面范例式中的 " = ",在数学式中是表示「等于」的概念,但是在计算机语言中用「等于」来解释会出现问题。譬如说,如果我们写下 N = N+1 的式子,这在数学中是不成立的,因为如果我们将 N 以 1 带入,那 1 = 1+1 当然就不成立。因此,在计算机语言中 " = " 的概念是:将 " = " 右边的答案或结果代入左边的变量。我们用下面的例子做个简单的练习:
Dim A
A = 10 + 20
A = A +1
Call Rhino.Print(A)
我 们会发现答案是 31。为什么?因为VB语言的程序代码是由上往下读的,例子中第一行的意思是:宣告一个 A 变量;第二行的意思是:将 10+20 的结果代入 A,所以这时候的 A 是 30;第三行的意思是:将 A 加 1 之后再带入 A 所以最后 A 是 30+1 也就是 31。 (3) Operators
再 来要说明的就不这么复杂了,在VB语言中的运算符大致可以分为三类:数学运算符、逻辑运算符、布尔值 (boolean values) 运算符。逻辑运算符在先前的文章中已经做过说明本文就不再重复,数学运算符则相当容易理解,基本上数学运算符的功能跟数学上的方法相同只需要了解其在于 VB中表达与运用的方式即可。另外,布尔值运算符在此只做简单的介绍,详细的用法及概念会在之后详细说明。
+ add two values
- substract two values
* mutiply two values
/ divide two values
\ divide two values but return only whole numbers
^ raise a number to the power of an exponent
Mod arithmetic modulus
------------------------------------------------------------------------
And performs a logical conjunction
Eqv performs a logical equivalence
Imp performs a logical implication
Not performs a logical negation
Or performs a logical disjunction
Xor performs a logical exclusion
Sin() Sine of a number
Cos() Cosine of a number
Atn() ArcTangent of a number
Log() Natural logarithm of a number larger than 0
Sqr() Square root of any positive number
Abs() Absolute (positive) value of any number
上 面这个式子一开始设定了一个 Sub 叫做 Check_Number,这个 Sub 可以送入一个数值 N。当 N 送入之后进入一个 If...Then 的条件语句,如果送入的 N 除以 2 余数为 0,则打印 N is an even number,如果不符和上述条件则打印 N is an odd number。我们从这个式子中发现并不是每行的命令行都被执行,而是选择执行某命令行跳过某命令行,这就是本文一开始提到 Flow Control 的特性。到目前为止,如果能够善用 Loops 与 If...Then 其实已经可以写出很多有趣的小程序,也就是说,当能够活用 Loops 与 If...Then 已经可以算是个会写程序的人了。
(2) Select Case
如果说 If...Then 比较像是非此即彼,那么 Select Case 则是可以设定多种条件,所以当我们的条件状态在三种以上时,基本上我们会建议使用 Select Case。我们直接用范例来说明 Select Case 的使用方式,以下的式子是求出某数值除以3之后的余数为何。
Sub Remainder(N)
Select Case (N Mod 3)
Case 0 Call Rhino.Print("the remainder of " & N & "divided by 3 is 0")
Case 1 Call Rhino.Print("the remainder of " & N & "divided by 3 is 1")
Case 2 Call Rhino.Print("the remainder of " & N & "divided by 3 is 2")
End Select
End Sub
上 述的式子一开始设定了一个名为 Remainder 的 Sub,这个 Sub 必须送入一个变量 N。当变量 N 送入之后,求出 N 除以 3 的余数,如果余数为 0 则打印 the remainder of N divied by 3 is 0,如果余数为 1 则打印 the remainder of N divied by 3 is 1,如果余数为 2 则打印 the remainder of N divied by 3 is 2。这个例子很简单地说明了当条件状态不只两个 (非此即彼) 的时候,该如何运用 Select Case 的方式。
本文介绍的两个 Conditonal Statements 功能,各位可以自行设定一些简单的数学题目或是逻辑关系加以进一步练习,因为在日后的实战演练中,我们将经常地使用此两者与 Loops 之间的交互运用。
An Short Introduction to RhinoScript
终于,我们要开始介绍 Array,假使能够把 Array 再搞懂,RhinoScript 中有关VB语言的基本知识就几乎都说明完毕了。Array 我们可以称之为数组,在数学中经常称为矩阵,基本上 Array 就是将某些数据放入一个集合之中的概念。譬如说,我们都知道一个笛卡尔坐标的点可以用 X, Y, Z 轴的坐标值来表示,即 Point A (0,0,0),这时候的 (0,0,0) 就是一个 Array。然而,上述的这个 Array 里头有三个位置,各自存放着不同的数据。Array 里头的数据,我们可以透过 Index 呼叫出来,就此例来说,在RhinoScript中我们应该表示成: Point A = Array(0,0,0),因此,如果我们要得到 Point A 的 X 坐标值则会是 Point A (0),依此类推,Point A 的 Y, Z 坐标值则为 Point A(1), Point A(2)。
大 家一开始可能会有疑问,为何 Array 中的第一个资料的 Index 为 0?这是VB中的规则,不需要去怀疑也不要问为什么,只要记住就好,之后有机会的话会慢慢体会到,这样做有它本身的用意与好处。如果各位过去数学还不差的 话,应该对矩阵还有点印象。在数学中,矩阵可以是一维的、二维的(有点像是矩形排列)、三维的(有点像是立方体排列),那可不可能超过三维?当然可能,四 维、五维、六维、、、原则上维度是无限的。大家或许会觉得很奇怪,超过三维以上的东西是什么?要怎么画出来?这会牵扯到物理与数学之间的差异,在此不多做 详细解释,但我们只要简单地知道数学中的维度不像物理实证一般,它仅是一种数据排列的方式,所以超过三维以上的矩阵是存在的。相同地,在VB中也允许超过 三维以上的数组,不过因为我们毕竟不是真的如同计算机科学的专家写数据库,在 RhinoScript 的实际操作中其实使用到五维以上的机会并不多。以下就用一些实例来说明一维、二维及三维数组的运用: (1) 1D Array Array 的维度表示,放在Array名称之后的括号之中,举例来说:Array A(3) 表示这是个一维的数组,数组中有 4 个可以储存数据的位置;Array B(3,3) 则表示这是个二维的数组,数组中有 4*4 = 16 个可以储存数据的位置;依此类推,其他维度就不再赘述。那么,在 RhinoScript 中 Array 要如何宣告?试看下列式子:
Dim A
A = Array(1, 2, 3, 5, 8, 13, 21, 34, 55)
这个式子一开始宣告一个变量 A,之后将 A 指定为一个一维的数组,数组中含有 9 个数据。这时候,如果我们要印出数组中的第 5 个数据,那我们要这么做:
Call Rhino.Print(A(4))
奇 怪,明明说要印出第 5 个数据,怎么在数组中的 Index 却是放进 4 这个数字?这在一开始我们就说明了,在VB中 Array 的 Index 第一个设定为 0。所以,要找第 5 个数据,其 Index 就会是 4。再试一次,如果我要从这个数组中印出 21 这个数字,那应该怎么呼叫?会像是下面的方式:
Sub Main()
Dim A, i
A = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
For i = 0 To 8
If (A(i) Mod 2 <> 0) Then
Call Rhino.Print(A(i))
Else
Call Rhino.Print(A(i)*2)
End If
Next
End Sub