在〈貝茲曲線〉可以看到,貝茲曲線會以第一個控制點作為起點,最後一個控制點作為終點,其他的控制點不在曲線上,對於如果起點與終點確定,想調整這兩點間的曲線時,是貝茲曲線的應用場合。
然而有時候,我們想在某個形狀的輪廓上點選取樣,取樣點不連續,這時希望有種曲線,可以通過這些取樣點,這時貝茲曲線就不適用。
對於這個需求,p5.js 提供了 curve
,必須提供四個控制點給它,curve
會畫出以第二個控制點為起點、第三個控制點為終點的曲線,例如:
乍看有點不明就理,然而這就是 curve
的特性,保證曲線必然以第二個控制點為起點、第三個控制點為終點;既然如此,若有更多的點,每四點用 curve
繪製一次,不就可以保證這曲線,必然通過第一點與終點之外的其他點嗎?
下圖是使用 6 個控制點,配合 curve
畫出曲線,並顯示了控制點,你可以操作這些控制點,無論如何操作,畫出的曲線都會通過中間的控制點:
那麼 curve
的原理是?想想看貝茲曲線的特性,曲線會通過起點與終點,如果你在某個形狀的輪廓上點選取樣,有沒有可能自動生成一條貝茲曲線,是以兩個鄰接的取樣點作起點與終點?這就是 curve
的原理 Catmull-Rom 樣條(splines)的出發點。
假設 P0、P1、P2、P3 是指定的控制點,先連接 P0 與 P2,再連接 P1 與 P3:
接著在 P1 求得一條與紅線平行的線段,在 P2 求得一條與綠線平行的線段:
平行的線段要多長,是可以自訂的參數,如上圖看到的,兩條平行線段各可以求得 P1'、P2',現在你有了 P1、P1'、P2'、P2 四個點了,不就可以用來求得貝茲曲線?因為是貝茲曲線,就一定通過起點 P1 與終點 P2,這就是我們想要的。
這也就是為什麼,curve
畫出的曲線,只會通過中間兩個控制點,而方才談到,平行的線段要多長,是可以自訂的,就上圖來看,平行的線段越長,曲線就越鬆弛,平行的線段越短,曲線就越緊繃。
在 p5.js 中,這可以透過 curveTightness
來控制,0 為預設的緊繃程度,設為 1 的話是完全緊繃,也就是拉緊為一直線,如果沒什麼特別的需求,通常只要在 0 到 1 之間選個值就可以了。
不過,緊繃程度 0 到 1 只是一個便於理解的方式,curveTightness
其實可以接受大於 1 的值,也可以是負值,這是因為 p5.js 將平行的線段設為參考來源線段的四分之一,將該點的緊繃值設為 0,往控制點的方向是正方向,抵達控制點時的緊繃值為 1,遠離控制點的方向是負方向,以 P1 與 P1' 為例:
P2、P2' 的關係也是同理,從上圖來看,緊繃程度設為 1 時,P1 與 P1' 就重合,P2 與 P2' 也重合,這時就是直線了,緊繃程度越小於 0,上圖的曲線就越上彎曲,緊繃程度大於 1 的話,曲線就扭轉了:
你可以試著將上面可用滑鼠操作控制點的範例,改為四個控制點,適當的拖曳控制點,緊繃度設為 2 來驗證:
與 curve
搭配使用的函式,還有 curveDetail
、curvePoint
與 curveTangent
,它們與〈貝茲曲線〉中談到的 bezierDetail
、bezierPoint
與 bezierTangent
作用相同,用來控制 curve
畫出來的貝茲曲線,就不再贅述了。