向量與矩陣

February 27, 2022

在 2D/3D 幾何運算過程,向量與矩陣是非常強大的工具,OpenSCAD 內建向量與矩陣運算的支援,善加運用的話,程式撰寫起來會簡明許多。

向量運算

向量可以用來表示具有方向與大小的任何東西,例如,座標 (2, 3) 可以看成是 x 座標為 2 像素,y 座標為 3 像素的位置,至今為止的範例也是這麼做的,然而,也可以看成從原點開始,往 x 正方向移動 2 像,往 y 方向移動 3 像素的向量。

向量與矩陣

OpenSCAD 的 list,可以看成是向量,端看你想表示幾維的向量,例如,[2, 3] 就建立了二維向量,[2, 3, 4] 是三維向量,[2, 3, 4, 5] 等是四維向量,依此類推。

如先前文件中談過的,對於二維向量,索引 0、1 的 元素,也可以用 .x.y 取得,對於三維向量,索引 0、1、2 的 元素,也可以用 .x.y.z 取得,例如:

vt = [1, 2, 3];
echo(vt[0] == vt.x);  // ECHO: true
echo(vt[1] == vt.y);  // ECHO: true
echo(vt[2] == vt.z);  // ECHO: true

OpenSCAD 的 list 可以乘上一個數,這其實就是支援向量乘上純量,可用來進行向量縮放,例如,[2, 3] * 2,結果會是 [4, 6],也就是將目前的向量放大兩倍:

向量與矩陣

相同維度的 list 可以彼此加減,這就是在做向量加減,例如,[2, 3] + [4, 1] 結果是向量 [5, 4],這也相當於從座標 (2, 3) 位移 (4, 1) 來到 (5, 4) 座標:

向量與矩陣

如果是 [4, 1] - [2, 3] 結果是向量 [2, -2],也就是下圖綠色向量部份,相當於從座標 (2, 3) 位移 (2, -2) 來到 (4, 1) 座標:

向量與矩陣

若想求向量,可以透過 norm 函式,它可以求向量的〈Euclidean norm〉,也就是向量各分量平方和後的開根號,norm([4, 1] - [2, 3]) 結果為 2.82843,也就是 (2, 3) 與 (4, 1) 間的距離。

相同維度的 list 可以相乘,這是在做向量〈內積〉,也就是各分量相乘後的和,因此 [2, 3] * [4, 1] 得到一個純量 11。

由於內積等於兩個向量的 Euclidean norm 相乘再乘上 cos(θ),θ 為兩個向量的夾角,若想求 [2, 3][3, 4] 向量的夾角,就是 acos(([2, 3] * [4, 1]) / (norm([2, 3]) * norm([4, 1]))),也就是夾角的角度為 42.2737。

若想求向量〈外積〉,可以使用 cross 函式,對於二維向量 a 與 b,cross(a, b) 結果會是個正值或負值,正表示外積後的垂直單位向量為 +z 方向,負就是 -z 方向,值的大小表示向量的大小,例如 corss([2, 3], [3, 4]),結果會是 -1;對於三維向量 a 與 b,cross(a, b) 結果會是個三維向量。

矩陣運算

OpenSCAD 的 list 可以用來表示矩陣(matrix),例如以下的矩陣:

向量與矩陣

想以 OpenSCAD 表示的話採取列為主(row-major),也就是實現時逐列編寫,也就是如下看待陣列中的矩陣元素:

[
    row1
    row2 
    row3 
]

每一列使用一個 list,構成二維的 list:

[
    [1, 0, tx],
    [0, 1, ty],
    [0, 0, 1]
]

這時若使用 * 運算,就是進行〈矩陣乘法〉。例如:

m1 = [
    [1, 2],
    [3, 4]
];

m2 = [
    [5, 6],
    [7, 8]
];

echo(m1 * m2); // ECHO: [[19, 22], [43, 50]]

有時一些線性轉換操作,使用矩陣表示會更簡明而有效率,例如,若 tx、ty 分別為 x、y 軸部份的位移量,位移公式可以表示為:

向量與矩陣

使用矩陣運算表示的話會是:

向量與矩陣

那麼座標 (5, 10) 在 x 方向移動 3,y 方向移動 5,會到哪個座標呢?

function translated(vt, tx, ty) = 
    let(
        r = [
            [1, 0, tx],
            [0, 1, ty],
            [0, 0, 1]
        ] * [vt.x, vt.y, 1]
    )
    [r.x, r.y];
    
tx = 3;
ty = 5;

echo(translated([5, 10], tx, ty)); // ECHO: [8, 15]

multmatrix 模組

OpenSCAD 提供了 multmatrix 模組,可以指定 4 x 4 線性轉換矩陣以及子模組,它會使用線性轉換矩陣,對子模組的每個頂點進行運算,例如來實現位移:

function m_translate(tx, ty, tz = 0) =
    // tx, ty, tz 位移的矩陣運算表示
    [
        [1, 0, 0, tx],
        [0, 1, 0, ty],
        [0, 0, 1, ty],
        [0, 0, 0, 1]
    ];
    
tx = 3;
ty = 5;

multmatrix(m_translate(tx, ty))
    square(5);

這會顯示以下的結果:

向量與矩陣

其實也可以指定一個 4 x 3 線性轉換矩陣,這表示最後一列預設為 [0, 0, 0, 1],因此上例也可以寫為以下,結果不變:

function m_translate(tx, ty, tz) = 
    [
        [1, 0, 0, tx],
        [0, 1, 0, ty],
        [0, 0, 1, tz]
    ];
    
tx = 3;
ty = 5;

multmatrix(m_translate(tx, ty))
    square(5);

透過矩陣運算的好處是,你可以準備好一組矩陣運算結果,再提供給 multmatrix,例如移位、旋轉、鏡像結合運算後的矩陣,再提供給 multmatrix,要留意的是採取後乘(post-post-multiplication)運算,也就是說,若想對模組進行移位、旋轉、鏡像的矩陣分別是 trm,那麼運算時必須是 m * r * t,結果再提供給 multmatrix

運算好的矩陣可以重複使用,若需要對大量頂點進行相同的轉換時,重用既有的矩陣可以避免重複運算,大大地提高幾何運算時的效率。

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