OpenSCAD用戶手冊/綜述
簡介
[編輯]OpenSCAD是一款2D/3D與實體建模程序,它基於函數式編程語言來創建模型並為之提供屏幕預覽,又能將其渲染為3D網格,藉此可將模型以各種2D/3D文件格式導出。
OpenSCAD用一種腳本來創建2D或3D模型。該腳本實質是一系列動作語句的自由格式列表。
object(); variable = value; operator() action(); operator() { action(); action(); } operator() operator() { action(); action(); } operator() { operator() action(); operator() { action(); action(); } }
- 對象
對象(object)是模型的基本組成單元,由2D與3D圖元(primitive)創建而成。對象以分號';'結尾。
- 動作
動作(action)語句包括利用圖元創建對象以及為變量賦值。動作語句同樣以分號';'作為結尾。
- 運算符
運算符(operator)或稱變換(transformation),用於修改對象的位置、顏色以及其他屬性。當運算符的工作域中存在一個以上的動作,就要用大括號'{}'將其圈定起來。多個運算符可執行相同的動作,也可構成一組不同的動作。多個運算符按由右至左的順序處理,即,離動作最近的運算符率先執行。運算符結尾不加分號';',但是不同的動作間要加分號。
示例 cube(5); x = 4+y; rotate(40) square(5,10); translate([10,5]) { circle(5); square(4); } rotate(60) color("red") { circle(5); square(4); } color("blue") { translate([5,3,0]) sphere(5); rotate([45,0,45]) { cylinder(10); cube([5,6,7]); } }
註釋
[編輯]註釋是一種在腳本或代碼中(為你自己或未來研究代碼的程式設計師)撰寫備註的方式,可用它來描述代碼的工作流程或具體行為。編譯器會跳過註釋,且用戶也不應為邏輯一目了然的代碼添加註釋。
OpenSCAD採用C++-風格的註釋
// 这是一行注释 myvar = 10; // 本行的后续部分是一条注释 /* 多行注释 可跨多行 */
數值與數據類型
[編輯]OpenSCAD中,一個數值的類型可能是一個數Number (如42), 一個布爾值Boolean (如true), 一個字符串String (如"foo"), 一個範圍Range (如[0: 1: 10]), 一個向量Vector (如[1,2,3]), 或未定義值Undefined value (undef)。值可存於變量之中,隨之作為參數傳入函數,再以函數計算結果返回。
[OpenSCAD是一種有着固定數據類型集的動態類型語言。不存在類型名,也沒有用戶定義類型。Functions are not values. 事實上,變量與函數各佔據着無交集的命名空間。]
數
[編輯]數(number)是OpenSCAD中最重要的數值類型,其書寫方式與其他語言中常用的十進制表示法相同。例如,-1, 42, 0.5, 2.99792458e+8。[OpenSCAD不支持數的八進制與十六進制表示法。]
除了十進制數以外,下列名稱定義的是一些特殊值:
- PI
OpenSCAD僅支持一種數型,即64位的IEEE浮點數。[OpenSCAD既非將整數與浮點數區分開來,也不支持複數]由於OpenSCAD採用IEEE浮點數標準,因此在數學計算上就會有少量偏差:
- 我們使用二進制浮點數計算法。對於分數而言,只有分母是2的次方時,才能精確地表示出來。例如,0.2 (2/10)的內部表示並不精準,但0.25 (1/4)與0.125 (1/8)卻可以精確表示出來。
- 可表示的最大數約為1e308。如果計算的數值結果過大,那麼將視之為正無窮(打印的反饋結果為inf)。
- 可表示的最小數約為-1e308。如果計算的數值結果過小,那麼將視之為負無窮(打印的反饋結果為-inf)。
- 如果數值的計算結果無效,那麼其結果可能是非數值(打印的反饋結果為nan)
- 如果一個非零的計算數值結果過於接近0而無法表示,那麼當結果為負值時其值為-0,否則其值為0。在一些數學計算中,零(0)與負零(-0)是兩種截然不同的數。而且儘管兩者比較起來相等,但是打印的反饋結果卻不相同。
請注意,儘管能夠通過計算數值從打印的反饋結果中看到'inf'與'nan',但是,在OpenSCAD中並不支持這兩種數值常量。您可以按以下方式,用這些數值來定義變量:
inf = 1e200 * 1e200; nan = 0 / 0; echo(inf,nan);
請注意,'nan'是OpenSCAD中唯一一種不等於其他任意值的值(包括它自己在內)。儘管您可以通過'x == undef'來檢測變量'x'是否為未定義值,但是卻不能利用'x == 0/0'來檢測x是否並非一個數。對此,您必須要通過'x != x'來檢測x是否為一個nan。
布林值
[編輯]布林值皆為真值。OpenSCAD中支持兩種布林值,分別名為true
與false
。
一個布林值能以參數的形式傳入條件語句'if()'、條件運算符'? :'以及邏輯運算符'!'(非)、'&&'(與)、'||'(或)。事實上,在上述所有的結構中,您都可以傳入任意量值。大多數數值都會轉換為布林值下的'true',而將值視作'false'的情況有:
- false
- 0 and -0
- ""
- []
- undef
請注意,"false"
(此字符串), [0]
(一個數值向量),
[ [] ]
(內含一個空向量的向量), [false]
(內含布林值false的向量)與0/0 (並非一個數)都計作true。
字符串
[編輯]一個字符串為0個或多個unicode字符所構成的序列。字符串值常用於在導入文件時確定文件名,以及為調試目的而在使用echo()時顯示文本。字符串亦可與2015.03版加入的新式text()圖元配合使用。
通過雙引號"
圍起字符序列來編寫一個字符串字面值, 就像這樣: ""
(一個空字符串), 或者"this is a string"
.
為了將"
字符包括在字符串字面值里,要這樣寫\"
。為了將\
字符包括在字符串字面值里, 要這樣寫\\
。下列以\
開頭的轉義序列即可用在字符串字面值里:
- \" → "
- \\ → \
- \t → 水平制表符(tab)
- \n → 換行(newline)
- \r → 回車(carriage return)
- \u03a9 → Ω - 關於unicode字符的進一步信息請參見text()
請注意: 這是自OpenSCAD-2011.04版開始新加入的行為。您可以通過下列sed命令來更新舊文件: sed 's/\\/\\\\/g' non-escaped.scad > escaped.scad
示例: echo("The quick brown fox \tjumps \"over\" the lazy dog.\rThe quick brown fox.\nThe \\lazy\\ dog."); 结果
ECHO: "The quick brown fox jumps "over" the lazy dog. The quick brown fox. The \lazy\ dog." 舊结果 ECHO: "The quick brown fox \tjumps \"over\" the lazy dog. The quick brown fox.\nThe \\lazy\\ dog."
範圍
[編輯]範圍(Range)常用於for()循環與children()。它們有兩種變體形式:
- [<start>:<end>]
- [<start>:<increment>:<end>]
儘管由方括號[]包裹,它們卻並非向量。這是因為它們用冒號:作為分隔符,而非句號。
r1 = [0:10]; r2 = [0.5:2.5:20]; echo(r1); // ECHO: [0: 1: 10] echo(r2); // ECHO: [0.5: 2.5: 20]
您應當避免採用不能被表示為精確二進制浮點數的步長值(step)。使用整數沒有問題,因為其分數中分母部分都為2的次方。例如,0.25 (1/4) 與 0.125 (1/8)都是安全的,但是應避免使用0.2 (2/10)這類值。若使用不能精確表示的步長值,由於計算誤差的原因,將導致您所使用的範圍內可能存在比預期更多或更少的元素。
若不填寫<increment>,其默認值為1。若採用[<start>:<end>]形式表示範圍,且<start>值大於<end>值,這將生成一個警告,而其實際邏輯則等價於[<end>: 1: <start>]。若採用[<start>:1:<end>]形式表示範圍,且<start>值大於<end>值,這將生成一個警告,而實際邏輯則等價於[]。對於OpenSCAD 2014之後的版本而言,範圍中的<increment>可取負值。
未定義值
[編輯]未定義值是一種記作undef的特殊值。它是未被賦值的變量的初始值,常用作傳有非法參數的函數或運算所返回的結果。最後要提到的是,可將undef
用作一個空(null)值,這等價於其他程式語言中的null
或NULL
。
一切含有undef
值的算數表達式的運算結果皆為undef
。在邏輯表達式中,undef
等價於false
。除了undef==undef
的計算結果為true
以外,其他有undef
參與的關係運算符表達的計算結果皆為false
。
請注意,數值計算也可能會返回'nan' (並非一個數值) 來表示存在非法參數。例如,0/false
為undef
, 而0/0
為'nan'。如果向< 與 >等關係運算符傳入非法參數,將返回false
。儘管undef
是OpenSCAD語言中定義的值,但'nan'卻不是。
變量
[編輯]利用帶有變量名或標識符的語句來創建OpenSCAD變量, 通過以分號為結尾的表達式來為之賦值。在許多命令式語言中數組所擔當的角色,在OpenSCAD中由向量來扮演。
var = 25; xx = 1.25 * cos(50); y = 2*xx+var; logic = true; MyString = "This is a string"; a_vector = [1,2,3]; rr = a_vector[2]; // 向量中的成员 range1 = [-1.5:0.5:3]; // for()循环范围 xx = [0:5]; // 替代for()循环范围
OpenSCAD是一種函數式程式語言,因此變量都與表達式相綁定,且在整個生命周期中保持為一個恆定的值,這是因為它需要滿足引用透明性這項需求。在類似於C這樣的命令式程式語言中,上述變量行為就如同常量一般,與其中的普通變量形成鮮明對比。
換言之,OpenSCAD中的變量更像是常量,但仍有一點重要的不同之處。如果多次為變量賦值,則在整個代碼中此變量僅存有最後一次所賦的值。這一點可參見進一步的討論設置變量發生在編譯時,而非運行時。此行為的原因是需要通過使用-D variable=value選項,才能在命令行中輸入變量。OpenSCAD當前將賦值過程置於原始碼的結尾處,這樣,一定可使變量的值以上述方式發生改變。
在編譯期變量保留其最後一次賦值結果,這與函數式程式語言的行為相符。與C這樣的命令式程式語言不同,OpenSCAD並不是一種支持迭代的語言,像x = x + 1這樣的語句是不合法的,如果理解了這個概念,您將領略OpenSCAD之美。
- 2015.03之前的版本
除文件頂層(file top-level)與模塊頂層(module top-level)之外,不能在任意作用域內進行賦值。在if/else語句或for循環語句中,需要使用assign()。
- 自2015.03版開始
可在任何作用域內進行賦值。請注意,只有在定義變量的作用域內對其進行賦值才是有效的——您仍然不能將數值「泄露」到外層的作用域中。參見變量的作用域來獲得更多詳情。
a=0; if (a==0) { a=1; // 在2015.03版之前,本行将造成一条编译错误 // 自2015.03版开始,将不再出现错误信息,但是值a=1却被限制在括号{}之内 }
未定義變量
[編輯]未賦值變量具有一種特殊值undef。 此值可作為條件語句的測試對象,且可由函數作為返回值。
示例 echo("Variable a is ", a); // Variable a is undef (变量a为undef) if (a==undef) { echo("Variable a is tested undefined"); // Variable a is tested undefined (变量a是被测试的未定义变量) }
變量的作用域
[編輯]當如translate()與color()這樣的運算符需要囊括一個以上的動作時(動作以;結尾), 就要用大括號{}來將動作打包成組,創建一個新的內部作用域。 如果組中僅有一個分號,那麼大括號可有可無。
每對大括號都在其所在的作用域中創建了一個新的作用域。自2015.03版開始, 可將新變量創建在此新作用域之內。另外,還可以為外部域中創建的變量指定新值。 這些變量及其值可存在於創建它們的作用域的內部作用域中,但是卻不存在於創建它們的作用域的外部域中。變量仍然僅保留作用域中最後一次所賦的值。
// 作用域1 a = 6; // 创建变量a echo(a,b); // 6, undef translate([5,0,0]){ // 作用域1.1 a= 10; b= 16; // 创建变量b echo(a,b); // 100, 16 a=10;在后面被a=100;覆盖 color("blue") { // 作用域1.1.1 echo(a,b); // 100, 20 cube(); b=20; } // 返回作用域1,1 echo(a,b); // 100, 16 a=100; // 在作用域1.1中覆盖a } // 返回作用域1 echo(a,b); // 6, undef color("red"){ // 作用域1.2 cube(); echo(a,b); // 6, undef } // 返回作用域1 echo(a,b); // 6, undef // 在本例中,作用域1与作用域1.1都为作用域1.1.1的外部域,而作用域1.2却不是。
- 不將匿名作用域看作是一種作用域:
{ angle = 45; } rotate(angle) square(10);
對於上述變量規則而言,For()循環也不例外,其中的變量各僅有一個值。OpenSCAD會為每趟循環中的內容創建一個副本。因而每趟循環中都有其自己的作用域,以此令對應循環過程中的變量有其唯一值。所以,您仍然不能使用a=a+1;語句。
設置變量發生在編譯時,而非運行時
[編輯]由於OpenSCAD在編譯時計算其變量值,而非運行時,因此,只有最後一次變量賦值才會在其所在作用域或其內部域中生效。這樣想也許會更有助於理解:即OpenSCAD中的變量是一種可重寫(override-able)的常量而非傳統意義上的「變量」。
// 'a'的值反映了仅最后一次的赋值才会生效 a = 0; echo(a); // 5 a = 3; echo(a); // 5 a = 5;
上述示例表現出了一種「反直覺」的執行效果,這使您可以做一些有趣的事情:例如,如果您建立了自己的共享庫文件,並在其頂層定義了具有默認值的變量,當您將此文件用於自己的項目中時,就可以簡單地通過為之賦予新值的方法來「重新定義」或覆蓋(override)那些常量。
特殊變量
[編輯]特殊變量提供了另一種向模塊或函數傳遞參數的方式。 所有以一個'$'開頭的變量均為特殊變量,這與lisp中的特殊變量很相似。 特殊變量較普通變量而言更為靈活(dynamic動態)。 (對於更多細節可參考其他語言特性)
向量
[編輯]向量是由0或更多的OpenSCAD值按序構成的。因此,向量實為一種由數字、布爾值、變量、向量、字符串或它們之間任意組合而成的集合(或稱列表或表)。They can also be expressions which evaluate to one of these. 向量在OpenSCAD中扮演的角色就相當於大多命令式語言中的數組。 The information here also applies to lists and tables which use vectors for their data.
向量的表示是由中括號(方括號)[]圍起的0或多個項(或稱元素或成員),其間由逗號隔開。向量也能以向量作為元素,而向量元素中還可以包含更多的向量,諸如此類。
- 示例
[1,2,3] [a,5,b] [] [5.643] ["a","b","string"] [[1,r],[x,y,z,4,5]] [3, 5, [6,7], [[8,9],[10,[11,12],13], c, "string"] [4/3, 6*1.5, cos(60)]
在OpenSCAD中使用向量:
cube( [width,depth,height] ); // 空格可选,这里为方便阅读而设 translate( [x,y,z] ) polygon( [ [x0,y0], [x1,y1], [x2,y2] ] );
- 創建向量
創建向量要列出其中元素,以逗號分隔,並用方括號包圍起來。變量元素會以它們的實際值來代替。
cube([10,15,20]); a1 = [1,2,3]; a2 = [4,5]; a3 = [6,7,8,9]; b = [a1,a2,a3]; // [ [1,2,3], [4,5], [6,7,8,9] ] 请注意,这里增加了嵌套的深度(层数)
- 向量中的元素
向量中的元素從0至n-1進行編號,其中的n是len()函數返回的向量長度值。 通過下列方式來定位向量中的元素:
e[5] // 元素5 (第6个元素) 第1层嵌套 e[5][2] // 元素5中的第2个元素 第2层嵌套 e[5][2][0] // 元素5中的第2个元素中的第0个元素 第3层嵌套 e[5][2][0][1] // 元素5中的第2个元素中的第0个元素的第1个元素 第4层嵌套
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ]; // e的长度为6 地址 长度 元素 e[0] 1 [1] e[1] 0 [] e[5] 3 [ [10,11], [12,13,14], [[15,16],[17]] ] e[5][1] 3 [ 12, 13, 14 ] e[5][2] 2 [ [15,16], [17] ] e[5][2][0] 2 [ 15, 16 ] e[5][2][0][1] undef 16 e[3] 6 "string" e[3][2] 1 "r" s = [2,0,5]; a = 2; s[a] undef 5 e[s[a]] 3 [ [10,11], [12,13,14], [[15,16],[17]] ]
- 另一種點表示法
向量中的前三個元素可用另一種點表示法進行訪問:
e.x // 等价于e[0] e.y // 等价于e[1] e.z // 等价于e[2]
向量操作符
[編輯]concat
[編輯][請注意: 需要使用版本 2015.03]
concat()將兩個或兩個以上向量中的元素合併至單個向量。同時並不改變原向量的嵌套層次。
vector1 = [1,2,3]; vector2 = [4]; vector3 = [5,6]; new_vector = concat(vector1, vector2, vector3); // [1,2,3,4,5,6] string_vector = concat("abc","def"); // ["abc", "def"] one_string = str(string_vector[0],string_vector[1]); // "abcdef"
len
[編輯]len()是一個返回向量或字符串長度的函數。 元素的索引值範圍為由[0]至[length-1]。
- 向量
- 返回此層級的元質數量。
- 單值並非向量, 此時將返回undef.
- 字符串
- 返回字符串中的字符數量。
a = [1,2,3]; echo(len(a)); // 3
矩陣
[編輯]向量中的元素為向量是謂矩陣。
定义了一个2D旋转矩阵的示例 mr = [ [cos(angle), -sin(angle)], [sin(angle), cos(angle)] ];
獲取輸入
[編輯]我們現在已掌握了變量,可以用它方便地獲取用戶的輸入,而不僅從代碼中對其進行設置。OpenSCAD中提供了一些函數用於從DXF文件中國讀取數據,或者您可以在命令行中通過-D開關來設置變量。
從圖紙中獲取一點
獲取點功能便於您從技術製圖中的2D視圖內獲取一原始點數據。dxf_cross函數將讀取並返回某層中您指定的兩條線的交點。這意味着必須指定DXF文件中的兩條線才能獲取相應交點,而並非指定對應的點實體。
OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin",
origin=[0, 0], scale=1);
獲取標註值
您可以從技術製圖中讀取其標註。這對於讀取旋轉角、擠壓(extrusion)高度或零件間的空間是很有用處的。在圖紙中,所創建的標註並不能反映其標註值,而僅是一種標識符。為讀取此值,您要在自己的程序中指定此標識符:
TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth",
layer="SCAD.Origin", origin=[0, 0], scale=1);
對於這兩個函數的更佳示例,可參考Example009,其效果圖位於OpenSCAD主頁。