就歷史而言,曲線繪製是古老的經驗集合而成,然而,重複的手工繪製耗時而容易出錯,缺少通用的描述方式,也不利資訊的溝通,於是,才發想了一連串的公式,透過程式設計來實現。

用公式來描述曲線

人類很簡單地就可以徒手畫出曲線,使用電腦來畫出曲線可就難多了,如果你隨手畫了條曲線,希望電腦能照著畫出相同的曲線,要如何告知電腦這曲線的規則呢?最簡單的是多項式函式,一次函式y=ax+b可以繪出直線,二次函式y=a*x^2+b*x+c可以繪出拋物線,更高次的多項式函式可以畫出更複雜的曲線。

然而,多項式函式只能左右伸展,例如,圓公式y=sqrt(r^2-x^2)怎麼畫呢?可以化為參數式x(t)=a*cos(t)、y(t)=b*sin(t),只要有適當的參數式,就可以畫出任何曲線,例如,可以在Wolfram Alpha知識引擎中,搜尋「Doraemon‐like curve」,這當中能找到曲線繪製出的哆啦A夢,以及曲線的參數式,也可以試著搜尋其他的曲線,像是「pokemon curve」,看看有哪些寶可夢角色的曲線參數式。

只不過要找出對應公式這件事太難或過於麻煩,就算Wolfram Alpha能搜尋到你想要的曲線,把那些參數式鍵入程式?我可不想這麼做!你可能會想到:一些繪圖軟體有貝茲曲線之類的工具,可以藉由幾個控制點來繪出複雜的曲線,但是,有這類程式庫嗎?

當然,例如p5.js中就有bezier函式,可以接受四個控制點座標來繪製曲線,相較於其他曲線函式,貝茲曲線的公式並不複雜,可以在維基的〈Bézier curve〉找到,就算不懂公式的原理,只是照著實作為程式碼,也不難。

如果想控制貝茲曲線的細節,p5.js還提供了bezierDetail之類的函式,另外,p5.js還提供了curve函式,可以繪製另一種曲線,還有curveDetail、curveTightness可以控制更多細節,兩種曲線有何不同呢,那些細節的意義又是?API文件上談到curve實作了Catmull-Rom樣條(spline),查看一下維基上對應的公式,你可能會倒吸一口氣,心想這是什麼鬼?

手繪貝茲與Catmull-Rom

無論是貝茲或Catmull-Rom的公式,其實都是一種內插(Interpolation)公式,以直線來說明內插的話,就是有兩個點,希望在這兩點間安插n個點並連接為直線,類似地,現在是想基於少量的控制點(例如4個),找出以控制點來計算出n個點的規則,而這些點連接必須是曲線。

上述的Catmull-Rom樣條,其實是基於貝茲曲線,因此必須先理解貝茲,然而不建議直接從公式入手,手繪貝茲會是更好的理解方式,也有利於掌握貝茲曲線或其他近似曲線的運用方式,我在先前專欄〈從貝茲曲線到曲面〉曾經談過如何手繪,以及導出公式,你也可以參考我基於p5.js寫的〈貝茲曲線〉文件

對於貝茲曲線,除了第一與最後一個控制點,其他控制點並不在曲線上,然而有時候,我們想在某形狀的輪廓上點選取樣,取樣點不連續,希望有種曲線,可以通過這些取樣點,此時貝茲曲線就不適用,然而可以透過Catmull-Rom樣條來繪製。

以p5.js的curve為例,必須提供4個控制點給它,curve保證能畫出以第2個控制點為起點、第3個控制點為終點的三次貝茲曲線,因此,若有更多的點,每4點用curve繪製一次,就可以保證這曲線,必然通過第1點與終點之外的其他點,p5.js應該是認為:這樣子組合曲線的方式,初接觸繪圖的人會比較容易理解,才提供了curve實作。

Catmull-Rom公式看似極為複雜,然而手繪起來並不難。假設P0、P1、P2、P3是依序指定的四個控制點,連接P0與P2為一直線,然後以P1為出發點,畫出一條與該線平行的線段,終點為P1',同樣地,連接P1與P3為另一直線,以P2為出發點,畫出一條與該線平行的線段,終點為P2'。

現在你有了P1、P1'、P2'、P2等4個點,就可以用來求得三次貝茲曲線,因為是貝茲曲線,就一定會通過起點P1與終點P2,而方才談到的平行線段,長度可以自定,而且,平行的線段越長,曲線就越鬆弛,平行的線段越短,曲線就越緊繃。

在p5.js中,緊繃程度可以透過curveTightness來控制,0為預設的緊繃程度,這時是將平行的線段設為參考來源線段的四分之一,設為1的話是完全緊繃,這時P1會與P1'重合,P2'與P2重合,也就是拉緊為一直線了,你也可以參考我基於p5.js寫的〈Catmull-Rom樣條〉文件,其中有圖解,並可以實際操作Catmull-Rom樣條。

曲線的簡史

手繪貝茲與Catmull-Rom不是奇怪的事,曲線在早期的造船業就是以手工方式繪製,畢竟沒有電腦輔助,當時會在平面上放一些勾形砝碼,用來固定具有一條具有彈性的木條,因為彈性的關係,木條會依砝碼位置而呈現不同的曲線。

在命名上,那些砝碼因為長得有點像鴨子,而被稱duck,砝碼的位置稱為節點(knot),也就是曲線的曲率變化之處,彈性的木條被稱為樣條(spline),這也就是不少曲線的命名中都有「樣條」字樣的原因。若想看看它們的模樣,你可以搜尋「duck spline」,就能找到砝碼製圖的相關圖片。

後來手工繪製的工具雖然有了發展,然後並不改變需要人工(依賴經驗、主觀判斷等)、大量手工重複工作(容易出錯),以及不利保存、分享繪製方式等缺點;50年代末期,雪鐵龍汽車公司開始試著採用將常用的圓、拋物線等幾何函式輸入電腦以控制機器,當時也就面臨了如何輸入曲線的問題。

雪鐵龍在1950年聘請了de Casteljau來解決問題,後來他提出了de Casteljau算法,然而雪鐵龍對其做了嚴格保密,在八年內沒有被公布,儘管如此,雪鐵龍競爭對手雷諾公司的Pierre Bézier獨立地找出曲線的解決方式,而且最終結果與de Casteljau類似,不同的是,在雷諾允許下,Pierre Bézier於1962年發表了他的發現,這也就是今日該曲線以貝茲曲線之名廣為人知的原因。

曲線的發展幾乎就是在同時期不同地方發生,通用汽車的研究人員設計出了B樣條(B-spline),有著更為複雜的數學定義,然而某些程度上,可以將它視為多段曲線打結組合而成,打結處稱為節點,這構成了B樣條的特性,控制點只會影響最接近它的線段,因此不會有單一條貝茲曲線,移動一個控制點整條曲線就變了的問題。

後續在曲線上還有NURBS等發展,主要是在CAD輔助軟體的應用,若想認識曲線歷史發展,可參考〈On the Spline〉

從公式之外認識曲線

如同先前專欄〈螺線之美與用〉中談過的,在接觸圖學相關知識時,我不愛從公式入手,曲線也是如此。在正式的描述場合時,公式確實有其必要,然而,我們也可以從具體的應用、手繪或歷史當中,嘗試理解。

例如,知道方才談及的Catmull-Rom樣條原理,其實完全不用套公式,在p5.js也可以基於bezier函式,很簡單地實現出Catmull-Rom樣條,像這樣從公式之外認識曲線,往往更能掌握曲線的相關特性,甚至加以變化。

作者簡介


Advertisement

更多 iThome相關內容