跳至內容

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中支持兩種布林值,分別名為truefalse。 一個布林值能以參數的形式傳入條件語句'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)值,這等價於其他程式語言中的nullNULL

一切含有undef值的算數表達式的運算結果皆為undef。在邏輯表達式中,undef等價於false。除了undef==undef的計算結果為true以外,其他有undef參與的關係運算符表達的計算結果皆為false

請注意,數值計算也可能會返回'nan' (並非一個數值) 來表示存在非法參數。例如,0/falseundef, 而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层嵌套
利用len()函數得到元素長度的示例
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主頁