跳轉至內容

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(); } }
物件
物件是模型的構建塊,由 2D 和 3D 原語建立。物件以分號 ';' 結束。
示例包括:cube()、sphere()、polygon()、circle() 等。
動作
動作語句包括使用原語建立物件和為變數賦值。動作語句也以分號 ';' 結束。
示例:a=1; b = a+7;
運算子
運算子或變換會修改物件的位置、顏色和其他屬性。當運算子的作用域涵蓋多個動作時,運算子使用大括號 '{}'。同一個動作或動作組可以使用多個運算子。多個運算子從右到左處理,即最靠近動作的運算子先處理。運算子不以分號 ';' 結束,但它們包含的單個動作會結束。
示例
   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++ 風格的註釋

// This is a comment
  
myvar = 10; // The rest of the line is a comment
  
/*
   Multi-line comments
   can span multiple lines.
*/

值和資料型別

[編輯 | 編輯原始碼]

OpenSCAD 中的值可以是數字(例如 42)、布林值(例如 true)、字串(例如 "foo")、範圍(例如 [0: 1: 10])、向量(例如 [1,2,3])或未定義值(undef)。值可以儲存在變數中,作為函式引數傳遞,並作為函式結果返回。

[OpenSCAD 是一種動態型別語言,具有固定的一組資料型別。沒有型別名稱,也沒有使用者定義的型別。]

數字是 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。如果數字結果太大,則結果可以是無窮大(透過 echo 列印為 inf)。
  • 可表示的最小數字約為 -1e308。如果數字結果太小,則結果可以是負無窮大(透過 echo 列印為 -inf)。
  • 如果數字結果無效,則結果可以是 Not A Number(透過 echo 列印為 nan)。
  • 如果非零數字結果太接近於零而無法表示,則如果結果為負,則結果為 -0,否則結果為 0。零 (0) 和負零 (-0) 被一些數學運算視為兩個不同的數字,並且透過 'echo' 列印時也不同,儘管它們比較為相等。

常量 infnan 不被 OpenSCAD 作為數字常量支援,即使你可以計算以這種方式由 'echo' 列印的數字。你可以使用以下方法定義具有這些值的變數

inf = 1e200 * 1e200;
nan = 0 / 0;
echo(inf,nan);

nan 是 OpenSCAD 中唯一不等於任何其他值的值,包括它本身。雖然你可以使用 'x == undef' 測試變數 'x' 是否具有未定義值,但你不能使用 'x == 0/0' 來測試 x 是否為 Not A Number。相反,你必須使用 'x != x' 來測試 x 是否為 nan。

布林值

[編輯 | 編輯原始碼]

布林值是具有兩種狀態的變數,通常在 OpenSCAD 中表示為 true 和 false。布林變數通常由條件測試生成,並由條件語句 'if()' 使用。條件運算子 '? :',並由邏輯運算子 ! (非)、&& (與) 和 || (或) 生成。諸如 if() 之類的語句實際上接受非布林變數,但大多數值在布林上下文中被轉換為 true。以下值被視為 false

  • false
  • 0-0
  • ""
  • []
  • undef

請注意,"false"(字串)、[0](數字向量)、[ [] ](包含空向量的向量)、[false](包含布林值 false 的向量)和 0/0(不是數字)都算作 true。

字串是零個或多個 Unicode 字元的序列。字串值用於在匯入檔案時指定檔名,以及在使用 echo() 時顯示文字以進行除錯。字串也可以與 text() 原語 一起使用,該原語是在版本 2015.03 中新增的。

字串文字被寫成用引號 " 括起來的字元序列,例如:""(空字串)或 "this is a string"

要在字串文字中包含 " 字元,請使用 \"。要在字串文字中包含 \ 字元,請使用 \\。以下以 \ 開頭的轉義序列可以在字串文字中使用

  • \" → "
  • \\ → \
  • \t → 製表符
  • \n → 換行符
  • \r → 回車符
  • \x21 → ! - 僅在 \x01 到 \x7f 的範圍內有效,\x00 生成空格
  • \u03a9 → Ω - 4 位 Unicode 程式碼點,有關 Unicode 字元的更多資訊,請參見 text()
  • \U01f600 → 😀 - 6 位 Unicode 程式碼點

此行為自 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.");
  
 result
ECHO: "The quick brown fox jumps "over" the lazy dog. The quick brown fox. The \lazy\ dog." old result ECHO: "The quick brown fox \tjumps \"over\" the lazy dog. The quick brown fox.\nThe \\lazy\\ dog."

範圍由 for() 迴圈children() 使用。它們有 2 種變體

[<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]

您應該避免使用無法精確表示為二進位制浮點數的步長值。整數是可以的,分數值的分母為 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> 不會生成警告,並且等效於 []。範圍內的 <increment> 可以為負數(對於 2014 年後的版本)。

未定義的值

[編輯 | 編輯原始碼]

未定義的值是一個特殊的值,寫為 undef。它是未分配值的變數的初始值,並且通常由傳遞非法引數的函式或操作返回。最後,undef 可以用作空值,等效於其他程式語言中的 nullNULL

所有包含 undef 值的算術表示式都計算為 undef。在邏輯表示式中,undef 等效於 false。帶有 undef 的關係運算符表示式計算為 false,除了 undef==undeftrue

請注意,數值運算也可能返回 'nan'(非數字)以指示非法引數。例如,0/falseundef,但 0/0 為 'nan'。關係運算符(如 < 和 >)如果傳遞非法引數則返回 false。雖然 undef 是一個語言值,但 'nan' 不是。

OpenSCAD 變數透過帶有名稱或 識別符號 的語句建立,透過表示式進行賦值,並以分號結尾。OpenSCAD 中處理陣列(在許多命令式語言中找到)的角色由向量處理。目前有效的識別符號只能由簡單字元和下劃線 [a-zA-Z0-9_] 組成,並且不允許高 ASCII 或 Unicode 字元。

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];      // member of vector
range1 = [-1.5:0.5:3]; // for() loop range
xx = [0:5];            // alternate for() loop range

OpenSCAD 是一種 函式式 程式語言,因此 變數 繫結到表示式,並且由於 引用透明度 的要求,在其整個生命週期內保持單個值。在 命令式語言(如 C)中,相同行為被視為常量,通常與普通變數形成對比。

換句話說,OpenSCAD 變數更像是常量,但有一個重要的區別。如果變數被多次分配值,則只有最後分配的值在程式碼中的所有地方使用。請參閱 變數在編譯時設定,而不是在執行時設定 的進一步討論。這種行為是由於需要在 命令列 上提供變數輸入,透過使用 -D variable=value 選項。OpenSCAD 目前將該賦值放在原始碼的末尾,因此必須允許變數的值為此目的而改變。

值在執行時不能修改;所有變數實際上都是不會改變的常量。每個變數在編譯時保留其最後分配的值,這與 函式式 程式語言一致。與 命令式 語言(如 C)不同,OpenSCAD 不是一種迭代語言,因此 x = x + 1 的概念是無效的。理解這個概念就能理解 OpenSCAD 的美妙之處。

在 2015.03 版本之前,除了檔案頂層和模組頂層之外,無法在任何地方進行賦值。在 if/else  或 for  迴圈內,需要使用 assign()。

從 2015.03 版本開始,現在可以在任何範圍內分配變數。請注意,賦值僅在定義的範圍內有效——您仍然不允許將值洩漏到外部範圍。有關更多詳細資訊,請參閱 變數的範圍

a=0;
if (a==0) 
  {
 a=1; //  before 2015.03 this line would generate a Compile Error
      //  since 2015.03  no longer an error, but the value a=1 is confined to within the braces {}
  }

未定義的變數

[編輯 | 編輯原始碼]

未賦值的變數具有特殊值 undef。它可以在條件表示式中進行測試,並由函式返回。

 Example
  
 echo("Variable a is ", a);                // Variable a is undef
 if (a==undef) {
   echo("Variable a is tested undefined"); // Variable a is tested undefined
 }

變數的範圍

[編輯 | 編輯原始碼]

當 translate() 和 color() 等運算子需要包含多個操作(操作以 ; 結尾)時,需要使用大括號 {} 來分組操作,從而建立一個新的內部範圍。當只有一個分號時,大括號通常是可選的。

每對大括號都在使用它們的大括號範圍內建立一個新範圍。從 2015.03 版本開始,可以在此新範圍內建立新變數。可以為在外部範圍內建立的變數賦予新值。這些變數及其值也適用於在此範圍內建立的進一步內部範圍,但對該範圍之外的任何內容不可用。變數在範圍內仍然只具有最後分配的值。

                       // scope 1
 a = 6;                // create a
 echo(a,b);            //                6, undef
 translate([5,0,0]){   // scope 1.1
   a= 10;
   b= 16;              // create b
   echo(a,b);          //              100, 16   a=10; was overridden by later a=100;
   color("blue") {     // scope 1.1.1
     echo(a,b);        //              100, 20
     cube();
     b=20;
   }                   // back to 1.1
   echo(a,b);          //              100, 16
   a=100;              // override a in 1.1
 }                     // back to 1   
 echo(a,b);            //                6, undef
 color("red"){         // scope 1.2
   cube();
   echo(a,b);          //                6, undef
 }                     // back to 1
 echo(a,b);            //                6, undef
  
 //In this example, scopes 1 and 1.1 are outer scopes to 1.1.1 but 1.2 is not.
匿名範圍不被視為範圍
 {
   angle = 45;
 }
 rotate(angle) square(10);

For() 迴圈不是關於變數在一個範圍內只具有一個值的規則的例外。迴圈內容的副本將為每次傳遞建立。每次傳遞都擁有自己的範圍,允許任何變數在該傳遞中具有唯一的值。不,您仍然不能做 a=a+1;

變數在編譯時設定,而不是在執行時設定

[編輯 | 編輯原始碼]

由於 OpenSCAD 在編譯時而不是在執行時計算其變數值,因此一個範圍內最後的變數賦值適用於該範圍或其內部範圍中的所有地方。將它們視為可覆蓋的常量而不是變數可能會有所幫助。

// The value of 'a' reflects only the last set value
   a = 0;
   echo(a);  // 5
   a = 3;
   echo(a);  // 5
   a = 5;

雖然這似乎違反直覺,但它允許你做一些有趣的事情:例如,如果你將共享庫檔案設定為在其根級別定義預設值作為變數,當你將該檔案包含在自己的程式碼中時,你可以簡單地為它們分配一個新值來“重新定義”或覆蓋這些常量。因此,更改常量值可以讓你更靈活。如果常量永遠不會改變,當然,你可以始終確定擁有你在任何常量定義中看到的那個值。這裡並非如此。如果你在其他地方看到一個常量值定義,它的值可能不同。這非常靈活。

前面的描述似乎與 OpenSCAD 在 2022 年 5 月 23 日的行為不同。在那個日期,執行上面的示例會導致以下輸出

警告:a 在 "Untitled" 的第 1 行分配,但在檔案 Untitled 的第 3 行被覆蓋 執行中止

特殊變數

[編輯 | 編輯原始碼]

特殊變數提供了傳遞引數給模組和函式的另一種方法。所有以 '$' 開頭的變數都是特殊變數,類似於 lisp 中的特殊變數。因此,它們比普通變數更動態。(有關更多詳細資訊,請參閱 其他語言特性

向量或列表是零個或多個 OpenSCAD 值的序列。向量是數字或布林值、變數、向量、字串或它們的任意組合的集合。它們也可以是表示式,這些表示式計算為其中之一。向量處理在許多命令式語言中找到的陣列的角色。此處的 資訊也適用於使用向量作為其資料的列表和表格。

向量具有方括號 [],其中包含零個或多個專案(元素或成員),用逗號分隔。向量可以包含向量,向量可以包含向量,等等。

示例

   [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] );           // optional spaces shown for clarity
  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] ]  note increased nesting depth

向量可以使用包含在方括號中的 for 迴圈進行初始化。

以下示例用長度為 n 的 10 個值為 a 的值初始化向量 result

n = 10
a = 0;

result = [ for (i=[0:n-1]) a ];
echo(result); //ECHO: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

以下示例展示了一個長度為 n 的 10 個值的向量 result,它使用交替的 ab 值初始化,如果索引位置 i 是偶數或奇數,則分別使用 ab 值。

n = 10
a = 0;
b = 1;
result = [ for (i=[0:n-1]) (i % 2 == 0) ? a : b ];
echo(result); //ECHO: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

索引向量內的元素

[編輯 | 編輯原始碼]

向量內的元素從 0 到 n-1 編號,其中 n 是由 len() 返回的長度。使用以下符號訪問向量內的元素

e[5]           // element no 5 (sixth) at   1st nesting level
e[5][2]        // element 2 of element 5    2nd nesting level
e[5][2][0]     // element 0 of 2 of 5       3rd nesting level
e[5][2][0][1]  // element 1 of 0 of 2 of 5  4th nesting level
具有來自 len() 的長度的示例元素
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ];  // length 6

address       length  element
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]] ]

字串索引

[編輯 | 編輯原始碼]

可以訪問字串的元素(字元)

"string"[2]    //resolves to "r"

點表示法索引

[編輯 | 編輯原始碼]

可以使用備選的點表示法訪問向量的前三個元素

e.x    //equivalent to e[0]
e.y    //equivalent to e[1]
e.z    //equivalent to e[2]

向量運算子

[編輯 | 編輯原始碼]

[注意: 需要版本 2015.03]

concat() 將 2 個或多個向量的元素組合成一個單一向量。不會更改巢狀級別。

 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() 是一個返回向量或字串長度的函式。元素的索引從 [0] 到 [length-1]。

向量
返回此級別的元素數量。
單個值,即不是向量,會引發錯誤。
字串
返回字串中的字元數量。
 a = [1,2,3]; echo(len(a));   //  3

請參閱具有長度的示例元素

矩陣是向量組成的向量。

Example that defines a 2D rotation matrix
mr = [
     [cos(angle), -sin(angle)],
     [sin(angle),  cos(angle)]
    ];

獲取輸入

[編輯 | 編輯原始碼]

沒有從鍵盤獲取變數輸入或從任意檔案讀取的機制。沒有提示機制、輸入視窗、輸入欄位,也沒有任何方法可以在指令碼執行時手動輸入資料。

資料只能在指令碼開始時設定為常量,並透過訪問少數檔案格式(如 stl、dxf、png 等)中的資料來設定。

除了 DXF 檔案之外,指令碼無法訪問這些資料,儘管指令碼可以在有限程度上將資料作為一個整體進行操作。例如,STL 檔案可以在 OpenSCAD 中渲染、平移、裁剪等。但是構成 STL 檔案的內部資料是無法訪問的。

現在我們有了變數,如果能夠將輸入放入它們中,而不是從程式碼中設定值,那就太好了。有一些函式可以從 DXF 檔案中讀取資料,或者你可以在命令列上使用 -D 開關設定變數。

從圖紙中獲取點

[編輯 | 編輯原始碼]

獲取點對於在技術圖紙的二維檢視中讀取原點很有用。函式 dxf_cross 讀取你在指定圖層上的兩條線的交點並返回交點。這意味著該點必須使用 DXF 檔案中的兩條線給出,而不是點實體。

OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin", 
                        origin=[0, 0], scale=1);

獲取尺寸值

[編輯 | 編輯原始碼]

你可以從技術圖紙中讀取尺寸。這對於讀取旋轉角度、拉伸高度或零件之間的間距很有用。在圖紙中,建立一個不顯示尺寸值而是顯示識別符號的尺寸。要讀取該值,你需要在程式中指定該識別符號

TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth",
                        layer="SCAD.Origin", origin=[0, 0], scale=1);

有關這兩個函式的很好示例,請參閱 Example009 以及 OpenSCAD 主頁 上的影像。

華夏公益教科書