include 與 use

March 5, 2022

隨著建模的作品增多,你會建立了許多可重用的程式樣版、模組或函式,這時會想要分門別類地存放在不同的 .scad 檔案,以便於管理,OpenSCAD 為此提供了 includeuse,來協助你組織這些 .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 檔案定義之名稱,不會執行指定檔案,非定義名稱的程式碼。

相對/絕對路徑

includeuse 時,可以指定相對或絕對路徑,方才的指定方式都是相對路徑,也就是相對於目前的 .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,先找到就先使用,若三個位置都沒有,就會發生錯誤。

你可以在 includeuse 時以 / 指定絕對路徑,這個絕對路徑一定是從方才列出的三個來源起算,例如,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 的某些模組與函式來完成的方式進行介紹。

分享到 LinkedIn 分享到 Facebook 分享到 Twitter