Catmull-Rom 樣條

March 9, 2022

在〈貝茲曲線〉可以看到,貝茲曲線會以第一個控制點作為起點,最後一個控制點作為終點,其他的控制點不在曲線上,對於如果起點與終點確定,想調整這兩點間的曲線時,是貝茲曲線的應用場合。

然而有時候,我們想在某個形狀的輪廓上點選取樣,取樣點不連續,這時希望有種曲線,可以通過這些取樣點,這時貝茲曲線就不適用。

curve 函式

對於這個需求,dotSCAD 提供了 curve 函式,必須提供四個控制點以上的控制點,curve 會畫出以第二個控制點為起點、倒數第二個控制點為終點的曲線,例如:

use <curve.scad>
use <polyline_join.scad>

control_points = [
    [28, 2, 1],
    [15, 8, -10],
    [2, 14, 5],
    [28, 14, 2],
    [15, 21, 9],
    [2, 28, 0]
];

t_step = 0.05;    
tightness = 0;

polyline_join(curve(t_step, control_points, tightness))
    sphere(.5);   

// 繪製控制點
# union() {
	for(pt = control_points) {
		translate(pt)
			sphere(1);
	}
	polyline_join(control_points)
		sphere(.1);  
}

這會繪製出以下的結果:

Catmull-Rom 樣條

tightness 是什麼呢?還有,為什麼不是從第一個控制點到最後一個控制點的曲線呢?這是因為 curve 實現了 Catmull-Rom 樣條。

Catmull-Rom 樣條原理

Catmull-Rom 樣條的出發點其實是貝茲曲線,因為貝茲曲線會通過起點與終點,若在某形狀的輪廓上點選取樣,自動生成一條貝茲曲線,是以兩個鄰接的取樣點作起點與終點,適當地將這些貝茲曲線結合起來,就可以滿足我們的需求。

假設 P0、P1、P2、P3 是指定的控制點,先連接 P0 與 P2,再連接 P1 與 P3:

Catmull-Rom 樣條

接著在 P1 求得一條與紅線平行的線段,在 P2 求得一條與綠線平行的線段:

Catmull-Rom 樣條

平行的線段要多長,是可以自訂的參數,如上圖看到的,兩條平行線段各可以求得 P1’、P2’,現在你有了 P1、P1’、P2’、P2 四個點了,不就可以用來求得貝茲曲線?因為是貝茲曲線,就一定通過起點 P1 與終點 P2,這就是我們想要的。

這也就是為什麼,curve 畫出的曲線,只會通過中間兩個控制點,而方才談到,平行的線段要多長,是可以自訂的,就上圖來看,平行的線段越長,曲線就越鬆弛,平行的線段越短,曲線就越緊繃。

在 dotSCAD 中,可以透過 curvetightness 來控制,0 為預設的緊繃程度,設為 1 的話是完全緊繃,也就是拉緊為一直線,如果沒什麼特別的需求,通常只要在 0 到 1 之間選個值就可以了。

不過,緊繃程度 0 到 1 只是一個便於理解的方式,tightness 其實可以接受大於 1 的值,也可以是負值,這是因為 curve 將平行的線段設為參考來源線段的四分之一,將該點的緊繃值設為 0,往控制點的方向是正方向,抵達控制點時的緊繃值為 1,遠離控制點的方向是負方向,以 P1 與 P1’ 為例:

Catmull-Rom 樣條

P2、P2’ 的關係也是同理,從上圖來看,緊繃程度設為 1 時,P1 與 P1’ 就重合,P2 與 P2’ 也重合,這時就是直線了,緊繃程度越小於 0,上圖的曲線就越上彎曲,緊繃程度大於 1 的話,曲線就扭轉了:

Catmull-Rom 樣條

如意旺來之如意

在〈Ruyi pineapple〉有顆鳳梨與如意底座,鳳梨是基於產生球面 Voronoi 的 vrn_sphere 函式建立,至於如意底座,是透過 curve 函式繞出了如意形象的文字:

use <curve.scad>
use <polyline_join.scad>

module ruyi(ruyi_step) { 
    pts = [
        [17, 20],
        [17, 15],
        [-10, 8],
        [-5, 0],
        [14, 20],
        [3, 24],
        [-5, 10],
        [10, 0],
        [21, 7],
        [14, 10],
        [12, 5],
        [30, 0],
        [55, 5],
        [80, 0],
        [85, 15]
    ];

    points = curve(ruyi_step, pts, 0);

    scale([2.4, 3.5, 3.5])
    linear_extrude(12.5, center = true)
    polyline_join(points)
        circle(1);   
}

ruyi_step = 0.1;
$fn = 12;

ruyi(ruyi_step);

這會建立以下的模型:

Catmull-Rom 樣條

這是 curve 的應用之一,你可以手繪一段曲線,試著用 surface 讀入 OpenSCAD,在調整大小之後,適當地取得曲線上的一些座標點,再透過 curve 看看是不是能適切地擬合,取點的疏密要略作嘗試,不過相對來說,是個比較易於建立曲線的方式了。

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