包教包會(huì)-貝塞爾曲線的繪制原理與應(yīng)用

圖來(lái)自pexels

說(shuō)來(lái)話長(zhǎng),這一切都得從PhotoShop中的鋼筆工具開始說(shuō)起...

聲明:本文不含復(fù)雜數(shù)學(xué)公式,學(xué)渣放心閱讀吧??(我仿佛看到了學(xué)渣們留下了激動(dòng)的淚水)

背景

貝塞爾曲線(Bézier curve)是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線,貝塞爾曲線基于多個(gè)點(diǎn)構(gòu)成。它的應(yīng)用非常廣泛,比如說(shuō)PS中的鋼筆工具所繪畫的曲線就是貝塞爾曲線,繪制動(dòng)畫的運(yùn)動(dòng)軌跡等等,而最近一次想用到貝塞爾曲線是想做一個(gè) 路徑動(dòng)畫

簡(jiǎn)介

在iOS開發(fā)中一般通過(guò)UIBezierPath來(lái)實(shí)現(xiàn)貝塞爾曲線的繪制,平時(shí)一般使用繪制二階和三階貝塞爾曲線的方法。而我們要做的遠(yuǎn)超二三階的貝塞爾曲線,本文 iOS Demo在原理上實(shí)現(xiàn)了N階貝塞爾曲線的繪制,未使用任何相關(guān)API,純手動(dòng)繪制貝塞爾曲線,并且可以拖動(dòng)滑塊瀏覽貝塞爾曲線的繪制過(guò)程。

本文 iOS Demo 實(shí)現(xiàn)以下功能:

實(shí)現(xiàn)功能 描述
繪制貝塞爾曲線 1、點(diǎn)擊空白處設(shè)置貝塞爾曲線的點(diǎn) </br>2、可以設(shè)置貝塞爾曲線階數(shù) </br>3、播放貝塞爾曲線繪制過(guò)程 </br> 4、拖動(dòng)滑塊,自由查看繪制過(guò)程每一個(gè)瞬間
簡(jiǎn)易曲線圖表 每?jī)蓚€(gè)點(diǎn)之間都是用3階貝塞爾曲線連接(細(xì)節(jié)待完善)
過(guò)山車 1、在空白處繪制貝塞爾曲線 </br>2、過(guò)山車沿著繪制的貝塞爾曲線行駛</br>3、支持多個(gè)連接的貝塞爾曲線路徑

Demo示例圖

8階貝塞爾曲線繪制過(guò)程

貝塞爾曲線的繪制原理

說(shuō)到繪制原理,如果貼??這張圖,我只能說(shuō):什么鬼!!!我看不懂,聽不見,你說(shuō)什么...
路人甲:簡(jiǎn)單點(diǎn)...說(shuō)話的方式簡(jiǎn)單點(diǎn)~

失敗案例

首先提供一個(gè)可以動(dòng)態(tài)繪制貝塞爾曲線的網(wǎng)站幫助你更好地理解貝塞爾曲線的繪制。

1. 點(diǎn)

貝塞爾曲線點(diǎn)的數(shù)量決定了曲線的階數(shù),一般N個(gè)點(diǎn)構(gòu)成的N-1階貝塞爾曲線,即3個(gè)點(diǎn)為二階,至少由3個(gè)點(diǎn)組成,為什么兩個(gè)點(diǎn)不行,兩個(gè)點(diǎn)組成的是直線。按順序,第一個(gè)點(diǎn)為 起點(diǎn) ,最后一個(gè)點(diǎn)為 終點(diǎn) ,其余點(diǎn)都為 控制點(diǎn)

A起點(diǎn)、B控制點(diǎn) 、C終點(diǎn)以及繪制的貝塞爾曲線

2. 點(diǎn)生線

這里說(shuō)的線不是貝塞爾曲線,而是各個(gè)點(diǎn)按順序連接起來(lái),形成的直線,如上圖ABBC兩條線。在這里我們要將整個(gè)曲線的繪制量化為從0~1的過(guò)程,用progress為當(dāng)前過(guò)程的進(jìn)度,progress的區(qū)間即0~1。每一條線都需要根據(jù)progress生成一個(gè)點(diǎn),如下圖,一個(gè)點(diǎn)從P0移動(dòng)到P1,這是這條線從0~1的過(guò)程。

根據(jù)進(jìn)度點(diǎn)從起點(diǎn)向終點(diǎn)移動(dòng)

下面是繪制一個(gè)二階貝塞爾曲線過(guò)程,先給口訣: 點(diǎn)生線,線生點(diǎn) ??。由ABC這3個(gè)點(diǎn)組成2條線ABBC,2條線根據(jù)progress分別生成2個(gè)移動(dòng)的點(diǎn)DE,而DE又連成一條線,始終保持AD:DB=BE:EC

progress為0.3 的連線

移動(dòng)的線

DE,DE再根據(jù)progress生成點(diǎn)F,只剩一個(gè)點(diǎn),無(wú)法構(gòu)成線,即為最終構(gòu)成貝塞爾曲線的點(diǎn)。紅色點(diǎn)為progress0~1過(guò)程中點(diǎn)F的移動(dòng)過(guò)程,保持AD:DB=BE:EC=DF:FE

progress為0.3 最終的點(diǎn)
點(diǎn)F的移動(dòng)過(guò)程

3. 繪制貝塞爾曲線

經(jīng)過(guò)上面 點(diǎn)生線,線生點(diǎn) 的過(guò)程 ,我們拿到了點(diǎn)F在移動(dòng)中所有點(diǎn)的,將這些點(diǎn)集合連接起來(lái),即形成了貝塞爾曲線。progress自增越慢,點(diǎn)集合的點(diǎn)越多,曲線就越細(xì)致。

繪制二階貝塞爾曲線過(guò)程

4. N階貝塞爾曲線

稍微了解算法的同學(xué)就能發(fā)現(xiàn),其實(shí) 點(diǎn)生線,線生點(diǎn) 是一個(gè)遞歸的過(guò)程,通過(guò)底層的點(diǎn),一步步推算出最高階的點(diǎn)。整個(gè)推導(dǎo)過(guò)程像一個(gè)金字塔,底部點(diǎn)的數(shù)量最多,每高一階點(diǎn)的數(shù)量就減1,直至最高階只有1個(gè)點(diǎn)。

**下面是遞歸代碼: **

// 貝塞爾曲線每高一階  需要遞歸次數(shù)+1
+ (NSArray *)recursionGetsubLevelPointsWithSuperPoints:(NSArray *)points progress:(CGFloat)progress{
    // 得到最終的點(diǎn) 正確結(jié)束遞歸 
    if (points.count == 1) return points;
    
    NSMutableArray *tempArr = [[NSMutableArray alloc] init];
    for (int i = 0; i < points.count-1; i++) {
        // 第一個(gè)點(diǎn) 
        NSValue *preValue = [points objectAtIndex:i];
        CGPoint prePoint = preValue.CGPointValue;
        // 第二個(gè)點(diǎn)
        NSValue *lastValue = [points objectAtIndex:i+1];
        CGPoint lastPoint = lastValue.CGPointValue;

        // 兩點(diǎn)坐標(biāo)差
        CGFloat diffX = lastPoint.x-prePoint.x;
        CGFloat diffY = lastPoint.y-prePoint.y;

        // 根據(jù)當(dāng)前progress得出高一階的點(diǎn)
        CGPoint currentPoint = CGPointMake(prePoint.x+diffX*progress, prePoint.y+diffY*progress);
        [tempArr addObject:[NSValue valueWithCGPoint:currentPoint]];
    }
    // 繼續(xù)下一次遞歸過(guò)程
    return [self recursionGetsubLevelPointsWithSuperPoints:tempArr progress:progress];
}

8階貝塞爾曲線繪制過(guò)程:

8階貝塞爾曲線繪制過(guò)程

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

光講原理脫離實(shí)踐這不是程序員的風(fēng)格,簡(jiǎn)單地寫了2個(gè)貝塞爾曲線的應(yīng)用,都在本文 iOS Demo 里面,歡迎運(yùn)行體驗(yàn)。

1. 過(guò)山車

通過(guò)點(diǎn)擊屏幕收集點(diǎn),將點(diǎn)集合生成貝塞爾曲線,可生成多個(gè)相連的貝塞爾曲線。小車按照生成的貝塞爾曲線路徑前進(jìn)。

a. 畫路徑
通過(guò)計(jì)算貝塞爾曲線的長(zhǎng)度,根據(jù)曲線長(zhǎng)度分配點(diǎn)的數(shù)量,達(dá)到點(diǎn)的相對(duì)均勻分布,使過(guò)山車 勻速前進(jìn)

畫路徑

b. 發(fā)車
每個(gè)點(diǎn)都與前面一個(gè)點(diǎn)連線,通過(guò)計(jì)算得出兩點(diǎn)的連線與水平形成的夾角,將角度賦予過(guò)山車實(shí)現(xiàn) 轉(zhuǎn)向功能

發(fā)車

2. 簡(jiǎn)易曲線圖表

a. 直線圖表
即最簡(jiǎn)單的兩點(diǎn)連成直線。

直線圖表

b. 曲線圖表
曲線圖表的曲線全部由3階貝塞爾曲線構(gòu)成,整個(gè)曲線圖不含任何棱角。

曲線圖表

拓展

PaintCode

推薦一個(gè)iOS畫路徑神器PaintCode,畫好圖形直接生成代碼,用鋼筆工具畫貝塞爾曲線也十分方便。下圖為用鋼筆工具畫一個(gè)圓球(貌似不夠圓??):

生成代碼

總結(jié)

為了準(zhǔn)備這一篇文章差不多理解了貝塞爾曲線的繪制原理,但是在細(xì)節(jié)處,比如說(shuō)真正意義上貝塞爾曲線點(diǎn)的均勻分布還有待完善,求曲線公式也沒(méi)有去研究,貝塞爾曲線在復(fù)雜的動(dòng)畫方向地應(yīng)用也是大有作為。

參考

貝塞爾曲線開發(fā)的藝術(shù)
Android:貝塞爾曲線原理分析

個(gè)人水平有限,歡迎提出建議。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,697評(píng)論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,406評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,772評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,973評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評(píng)論 1 285
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,644評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,953評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容