include 與 use
March 5, 2022隨著建模的作品增多,你會建立了許多可重用的程式樣版、模組或函式,這時會想要分門別類地存放在不同的 .scad 檔案,以便於管理,OpenSCAD 為此提供了 include
與 use
,來協助你組織這些 .scad 檔案。
include 程式樣版
如果你有一些程式碼樣版,想在每次撰寫程式時,合併至程式碼之中,可以將程式碼樣版撰寫在 .scad 檔案,然後透過 include
含括至目前的程式碼之中。
例如,你可能定義了一些通用的數學常數,定義在 constants.scad:
TAU = 6.2831853071;
RADIANS_PER_DEGREE = 0.0174532925;
DEGREES_PER_RADIAN = 57.2957795;
這麼一來,若在撰寫程式碼時,需要這些常數,可以將之含括進來,例如在 main.scad 撰寫:
include <constants.scad>
echo(TAU); // ECHO: 6.28319
echo
會在控制台輸出,不過對於浮點數,最多只會輸出小數後五位數。
include
還蠻單純的,就是將指定的 .scad 讀入,與目前的程式碼合併,只要合併後的程式碼符合 OpenSCAD 語法,你可以在任何地方進行 include
,甚至是 function
,例如,若 len_body.scad 內容為 is_undef(text[count]) ? count : length(text, count + 1);
,那麼可以如下 include
:
function length(lt, count = 0) = include <len_body.scad>;
echo(length("XD")); // ECHO: 2
有些開發者會將可重用的模組或函式,分門別類地放在不同的 .scad 檔案,然後在需要使用到某些模組或函式時,將對應的 .scad 以 include
含括進來,這確實是一種管理可重用模組或函式的方式。
不過有個蠻大的缺點,因為 include
就是合併程式碼,不管你怎麼 include
各個 .scad,最後就等同於合併為一個 .scad,如果各個 .scad 檔案裡有重複的名稱,那麼合併後的程式碼中,前面的名稱就會被後續的名稱覆蓋掉,也就是說 include
並沒有名稱空間管理的效果。
use 名稱
如果你要有名稱空間管理的效果,可以將可重用的模組或函式,分門別類地放在不同的 .scad 檔案,然後在需要使用到某些模組或函式時使用 use
,你只能使用被 use
的 .scad 中定義的名稱。
例如,如果有個 length.scad,裡頭定義了一個 length
函式:
function length(text, count = 0) =
is_undef(text[count]) ? count : length(text, count + 1);
若有個 debug.scad,透過 use
可以使用 length.scad 中定義的 length
名稱:
use <length.scad>
module echo_length(lt) {
echo("長度", length(lt));
}
如果有個 main.scad,可以透過 use
使用 debug.scad 定義的 echo_length
名稱:
use <debug.scad>
echo_length("Orz");
不過,main.scad 也只能使用 debug.scad 定義的 echo_length
名稱,雖然 debug.scad 中 use
了 length.scad,然而 length
並不是定義在 debug.scad,不能在 main.scad 使用 length
。
use
的這種特性,可以用來實現名稱空間管理的效果,例如,某個 foo
函式的實現,可能需要切為 _foo1
、_foo2
等子函式來實現,那麼你可以在 _foo_impl.scad 中撰寫:
function _foo1() = 10;
function _foo2() = 20;
然後在 foo.scad 裡 use <_foo_impl.scad>
:
use <_foo_impl.scad>
function foo() = _foo1() + _foo2();
現在若撰寫程式時,需要使用 foo
函式,就可以 use <foo.scad>
,這時只有 foo
名稱可見,看不到 _foo1
、_foo2
,也就不用擔心後續會有名稱覆蓋的問題。
另外,use
只會使用指定的 .scad 檔案定義之名稱,不會執行指定檔案,非定義名稱的程式碼。
相對/絕對路徑
在 include
或 use
時,可以指定相對或絕對路徑,方才的指定方式都是相對路徑,也就是相對於目前的 .scad,例如 main.scad 中若有 use <foo.scad>
,是從 main.scad 所在路徑尋找 foo.scad。
你的 .scad 可能放在其他資料夾底下,例如,foo.scad 所在路徑有 _impl 子資料夾,那麼在 foo.scad 中 use <_impl/_foo_impl.scad>
就可以使用 _impl/_foo_impl.scad 的名稱定義。
如果想使用 foo.scad 上層路徑的 .scad,可以使用 ../
,例如 use <../xyz.scad>
,表示指定上層路徑的 xyz.scad,若是 use <../util/some.scad>
,表示指定上層路徑的子資料夾 util 中的 some.scad。
如果相對於目前 .scad 找不到指定的 .scad,最後會到三個位置尋找:
- OpenSCAD 安裝位置的 libraries 資料夾
- 使用者家目錄中的 OpenSCAD\libraries 資料夾
OPENSCADPATH
環境變數指定的資料夾
例如,若在 main.scad 寫了 use <foo.scad>
,而目前 main.scad 所在位置,並沒有 foo.scad,那麼就依序從以上三個資料夾尋找,看看其中有無 foo.scad,先找到就先使用,若三個位置都沒有,就會發生錯誤。
你可以在 include
或 use
時以 /
指定絕對路徑,這個絕對路徑一定是從方才列出的三個來源起算,例如,use </foo.scad>
,一定是在方才列出的三個來源尋找,先找到先使用,若三個位置都沒有,就會發生錯誤。
為了便於在不同的位置管理 .scad 檔案,通常不會使用絕對路徑。
使用 dotSCAD
在日後建立模型的過程,如果發現有些建模的程式碼有重複或類似的流程,可以試著將之重構為可重用的模組或函式,隨著時間的累積,應該就會逐漸變成你常用的程式庫。
dotSCAD 就是這麼來的,在〈examples〉可以看到許多模型,這些模型中重構出可重用的模組或函式,然後用這些模組、函式重構模型,接著又寫了新的模型,如此重複不斷…
當然,並不是每個人都會想要開發程式庫,特別是對 OpenSCAD 來說,因為撰寫程式或者是面對數學、演算法,都有其挑戰性,如果你只是想找個現成的程式庫,可以減輕數學、演算法等方面的負擔,可以試著使用 dotSCAD。
方式之一是從 Github 上 git clone
,然後設定 OPENSCADPATH
環境變數,我是 dotSCAD 的開發者,對我來說,OPENSCADPATH
指向 src 資料夾是最方便的,因此全部的文件或範例,在 use
時都是從 src 開始搜尋,例如:
use <line2d.scad>
line2d(p1 = [0, 0], p2 = [5, 0], width = 1);
如果你將 OPENSCADPATH
指向 clone 下來的 dotSCAD 資料夾,那麼在 use
時就是從 dotSCAD 開始搜尋,這時必須改為:
use <src/line2d.scad>
line2d(p1 = [0, 0], p2 = [5, 0], width = 1);
基於 git clone
的好處是,隨時可以透過 git pull
取得 dotSCAD 最新的更新,另一個方式是 Download ZIP,解開 zip 後設置 OPENSCADPATH
。
你可能不只使用一個程式庫,這時可以將 src 改名為 dotSCAD,放到你管理程式庫的資料夾,例如 OpenSCAD 安裝位置的 libraries 資料夾,這時 use
時就可以基於資料夾區別,例如:
use <dotSCAD/line2d.scad>
line2d(p1 = [0, 0], p2 = [5, 0], width = 1);
如果你不在乎一定要是 dotSCAD repository 最新的版本,可以下載〈Releases〉的檔案,發佈時的版本號是基於〈語意化版本(Semantic Versioning)〉。
後續的文件,將會談到如何使用 dotSCAD,由於 dotSCAD 的模組與函式很多,逐一介紹沒有意義,因此會是以某些主題的模型,看看如何結合 dotSCAD 的某些模組與函式來完成的方式進行介紹。