貝塞爾曲線學(xué)習(xí)筆記

前言

近段時(shí)間我在工作中實(shí)現(xiàn)了一個(gè)動(dòng)畫(huà)功能,其中涉及到動(dòng)畫(huà)元素要按一定的軌跡在屏幕上移動(dòng),運(yùn)動(dòng)軌跡的生成我使用了Path.cubicTo方法來(lái)實(shí)現(xiàn)的,這個(gè)方法其實(shí)是生成了一條有兩個(gè)控制點(diǎn)的貝塞爾曲線。借此機(jī)會(huì)我再收集了一些資料,想系統(tǒng)地學(xué)習(xí)一下貝塞爾曲線相關(guān)的知識(shí)。

簡(jiǎn)介

貝塞爾曲線(Bézier curve)又被稱(chēng)為貝茲曲線或貝濟(jì)埃曲線,是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線,它的數(shù)學(xué)基礎(chǔ)是伯恩斯坦多項(xiàng)式(Bernstein polynomial,since 1912),1959年法國(guó)數(shù)學(xué)家Paul de Casteljau提出了數(shù)值穩(wěn)定的de Casteljau算法,開(kāi)始貝塞爾曲線的圖形化應(yīng)用研究,而貝塞爾曲線的名稱(chēng)來(lái)源于一位就職于雷諾的法國(guó)工程師Pierre Bézier,他在1962年開(kāi)始對(duì)貝塞爾曲線做了廣泛的宣傳,他使用這種只需要很少的控制點(diǎn)就能生成復(fù)雜平滑曲線的方法來(lái)進(jìn)行汽車(chē)車(chē)體的工業(yè)設(shè)計(jì)。

貝塞爾曲線因?yàn)樗刂坪?jiǎn)便卻具有極強(qiáng)的描述能力,迅速在工業(yè)設(shè)計(jì)和計(jì)算機(jī)圖形學(xué)等相關(guān)領(lǐng)域得到了廣泛應(yīng)用。比如在矢量繪圖中,貝塞爾曲線用來(lái)給需要無(wú)限制地縮放的平滑曲線定模,許多繪圖軟件都提供了繪制貝塞爾曲線的功能。貝塞爾曲線還用于動(dòng)畫(huà)時(shí)間控制以實(shí)現(xiàn)美觀逼真的緩動(dòng)效果,還用于機(jī)器人轉(zhuǎn)動(dòng)手臂等方面的設(shè)計(jì)。

de Casteljau算法原理

在向量AB上選擇一個(gè)點(diǎn)C,使得C分向量AB為u:1-u(即|AC|:|AB|=u)。給定點(diǎn)A、B的坐標(biāo)以及u(u∈[0,1])的值,點(diǎn)C的坐標(biāo)則為C=A+(B-A)*u=(1-u)*A+u*B。

de Casteljau算法基本原理

貝塞爾曲線原理

利用de Casteljau算法可以計(jì)算貝塞爾曲線上的點(diǎn)C(u),u∈[0,1]。因此,通過(guò)給定一組u的值,即可算出貝塞爾曲線上的坐標(biāo)序列,從而繪制出貝塞爾曲線。

為了計(jì)算n階貝塞爾曲線上的點(diǎn)C(u),u∈[0,1],首先將控制點(diǎn)連接形成一條折線00-01-02······0(n-1)-0n,利用de Casteljau算法,計(jì)算出折線中每條線段0j-0(j+1)上的一個(gè)點(diǎn)1j,使得點(diǎn)1j分該線段的比為u:1-u,然后在折線10-11-12-······-1(n-1)上遞歸調(diào)用該算法,以此類(lèi)推。最終求得一個(gè)點(diǎn)n0,已證明點(diǎn)n0一定是曲線上的點(diǎn)。

de Casteljau算法的幾何表達(dá)

上圖中u=0.4,這從幾何形狀上形象地說(shuō)明了de Casteljau算法的過(guò)程,可以將上述直觀的幾何描述表達(dá)為算術(shù)方法,如下圖所示:

de Casteljau算法的算術(shù)表達(dá)圖示

首先將所有的控制點(diǎn)排成一列,如上圖最左列,每一對(duì)相鄰的控制點(diǎn)之間畫(huà)出兩個(gè)箭頭,分別指向右下方和右上方,在兩個(gè)箭頭交匯的位置記下一個(gè)新的點(diǎn)。比如控制點(diǎn)ij和i(j+1)生成新的控制點(diǎn)(i+1)j。指向右下方的箭頭表示乘以(1-u),指向右上方的箭頭表示乘以u(píng)。

將上述過(guò)程表達(dá)為下面的算法:

Input:arrayP[0:n] ofn+1 points and real numberuin [0,1]

Output:point on curve,C(u)

Working:point arrayQ[0:n]

fori:= 0 to n do

? ? Q[i] :=P[i]; // save input

fork:= 1 to n do

? ? fori:= 0ton - kdo

? ? ? ? Q[i] := (1 -u)Q[i] +uQ[i+ 1];

returnQ[0];

這個(gè)算法還可以被遞歸地表達(dá),上述點(diǎn)列還有其他有趣的性質(zhì),詳細(xì)描述請(qǐng)參考論文Finding a Point on a Bézier Curve: De Casteljau's Algorithm。

貝塞爾曲線展示

一階貝塞爾曲線

一階貝塞爾曲線動(dòng)畫(huà),t∈[0,1]

給定點(diǎn)P0和P1,一階貝塞爾曲線就是這兩點(diǎn)間的一條直線段,對(duì)應(yīng)公式:

一階貝塞爾曲線的一般方程

二階貝塞爾曲線

二階貝塞爾曲線動(dòng)畫(huà),t∈[0,1]

給定點(diǎn)P0、P1和P2,二階貝塞爾曲線是由下述方程產(chǎn)生的軌跡

二階貝塞爾曲線的直接方程

這個(gè)方程可以被描述為由兩段獨(dú)立的一階貝塞爾曲線再按de Casteljau算法合成的,分別是由 P0 至 P1 的連續(xù)點(diǎn) Q0描述一條線段,由 P1 至 P2 的連續(xù)點(diǎn) Q1描述另一條線段,最后由 Q0 至 Q1 的連續(xù)點(diǎn) B(t)描述了貝塞爾曲線。整理上述公式變成:

二階貝塞爾曲線的一般方程

三階貝塞爾曲線


三階貝塞爾曲線動(dòng)畫(huà),t∈[0,1]

二維平面或者更高維度的空間中的4個(gè)點(diǎn)P0、P1、P2和P3可以定義一條三階的貝塞爾曲線,這條曲線從P0點(diǎn)開(kāi)始向P1點(diǎn)方向運(yùn)動(dòng),最后從來(lái)自P2點(diǎn)的方向運(yùn)動(dòng)到達(dá)P3點(diǎn)。一般點(diǎn)P1和P2不會(huì)在曲線上,它們用來(lái)控制曲線運(yùn)動(dòng)的方向;點(diǎn)P0與P1間的距離將會(huì)影響曲線在轉(zhuǎn)向P2之前向P1點(diǎn)方向運(yùn)動(dòng)的遠(yuǎn)近和快慢。按照二階貝塞爾曲線方程可以表達(dá)為一階的形式,三階貝塞爾曲線也可以由兩個(gè)二階貝塞爾曲線組合來(lái)線性地表達(dá):

三階貝塞爾曲線的線性表達(dá)式

將方程遞歸帶入整理得到:

三階貝塞爾曲線的一般方程

更一般地,n階貝塞爾曲線的一般方程為:

n階貝塞爾曲線的一般表達(dá)式

例如,n=5時(shí),即5階貝塞爾曲線的方程為:

5階貝塞爾曲線的一般方程

貝塞爾曲線應(yīng)用

Android SDK中提供的API

在android.graphics包中的Path類(lèi)提供了幾個(gè)可以繪制貝塞爾曲線的API,常用的就是lineTo、quadTo和cubicTo三個(gè)方法,分別可以繪制一、二、三階的貝塞爾曲線,一階沒(méi)什么可說(shuō)的,就是通常的畫(huà)直線,傳入的參數(shù)就是目標(biāo)點(diǎn)坐標(biāo)。下面分別是quadTo和cubicTo方法的接口說(shuō)明:

Path類(lèi)的quadTo方法說(shuō)明
Path類(lèi)的cubicTo方法說(shuō)明

自定義貝塞爾工具

在實(shí)際項(xiàng)目中我自己寫(xiě)了一個(gè)貝塞爾估值器類(lèi),核心方法如下:

自定義貝塞爾估值器


后記

我第一次寫(xiě)這樣長(zhǎng)篇的技術(shù)博客,慚愧??!想以此加深自己的理解,也方便以后迅速查閱相關(guān)的資料。寫(xiě)博客真的是一個(gè)深入學(xué)習(xí)的好方法,讓我不再流于解決問(wèn)題的表面,通過(guò)查找資料可以透過(guò)直接的技術(shù)方案發(fā)現(xiàn)方案背后的邏輯和原理。理解了最本質(zhì)的東西,其他的都是可以舉一反三、觸類(lèi)旁通的方法變幻。加油,少年!~


Thanks To:

貝塞爾曲線掃盲

貝塞爾曲線初探

Wikipedia:Bézier curve

Finding a Point on a Bézier Curve: De Casteljau's Algorithm

BezierDemo源碼解析-實(shí)現(xiàn)qq消息氣泡拖拽消失的效果

基于貝塞爾曲線的Android動(dòng)畫(huà)差值器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容