天天看點

貝塞爾曲線開發的藝術

一句話概括貝塞爾曲線:将任意一條曲線轉化為精确的數學公式。

很多繪圖工具中的鋼筆工具,就是典型的貝塞爾曲線的應用,這裡的一個網站可以線上模拟鋼筆工具的使用:

<a href="http://bezier.method.ac/">http://bezier.method.ac/</a>

貝塞爾曲線開發的藝術

貝塞爾曲線中有一些比較關鍵的名詞,解釋如下:

資料點:通常指一條路徑的起始點和終止點

控制點:控制點決定了一條路徑的彎曲軌迹,根據控制點的個數,貝塞爾曲線被分為一階貝塞爾曲線(0個控制點)、二階貝塞爾曲線(1個控制點)、三階貝塞爾曲線(2個控制點)等等。

要想對貝塞爾曲線有一個比較好的認識,可以參考WIKI上的連結:

<a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">https://en.wikipedia.org/wiki/B%C3%A9zier_curve</a>

貝塞爾曲線開發的藝術

在Android中,一般來說,開發者隻考慮二階貝塞爾曲線和三階貝塞爾曲線,SDK也隻提供了二階和三階的API調用。對于再高階的貝塞爾曲線,通常可以将曲線拆分成多個低階的貝塞爾曲線,也就是所謂的降階操作。下面将通過代碼來模拟二階和三階的貝塞爾曲線是如何繪制和控制的。

貝塞爾曲線的一個比較好的動态示範如下所示:

<a href="http://myst729.github.io/bezier-curve/">http://myst729.github.io/bezier-curve/</a>

貝塞爾曲線開發的藝術

二階貝塞爾曲線在Android中的API為:quadTo()和rQuadTo(),這兩個API在原理上是可以互相轉換的——quadTo是基于絕對坐标,而rQuadTo是基于相對坐标,是以後面我都隻以其中一個來進行講解。

先來看下最終的效果:

貝塞爾曲線開發的藝術

從前面的介紹可以知道,二階貝塞爾曲線有兩個資料點和一個控制點,隻需要在代碼中繪制出這些輔助點和輔助線即可,同時,控制點可以通過onTouchEvent來進行傳遞。

三階貝塞爾曲線在Android中的API為:cubicTo()和rCubicTo(),這兩個API在原理上是可以互相轉換的——quadTo是基于絕對坐标,而rCubicTo是基于相對坐标,是以後面我都隻以其中一個來進行講解。

有了二階的基礎,再來模拟三階就非常簡單了,無非是增加了一個控制點而已,先看下效果圖:

貝塞爾曲線開發的藝術

代碼隻需要在二階的基礎上添加一些輔助點即可,下面隻給出一些關鍵代碼,詳細代碼請參考Github:

如下所示的網頁,模拟了三階貝塞爾曲線的繪制,可以通過拖動曲線來擷取兩個控制點的坐标,而起始點分别是(0,0)和(1,1)。

<a href="http://cubic-bezier.com/">http://cubic-bezier.com/</a>

貝塞爾曲線開發的藝術

通過這個網頁,也可以比較友善的擷取三階貝塞爾曲線的控制點坐标。

當在螢幕上繪制路徑時,例如手寫闆,最基本的方法是通過Path.lineTo将各個觸點連接配接起來,而這種方式在很多時候會發現,兩個點的連接配接是非常生硬的,因為它畢竟是通過直線來連接配接的,如果通過二階貝塞爾曲線來将各個觸點連接配接,就會圓滑的多,不會出現太多的生硬連接配接。

先來看下代碼,非常簡單的繪制路徑代碼:

先來看下通過mPath.lineTo來實作的繪圖,效果如下所示:

貝塞爾曲線開發的藝術

圖檔中的拐點有明顯的鋸齒效果,即通過直線的連接配接,再來看下通過貝塞爾曲線來連接配接的效果,通常情況下,貝塞爾曲線的控制點取兩個連續點的中點:

通過二階貝塞爾曲線的連接配接效果如圖所示:

貝塞爾曲線開發的藝術

可以明顯的發現,曲線變得更加圓滑了。

通過控制貝塞爾曲線的控制點,就可以實作對一條路徑的修改。是以,利用貝塞爾曲線,可以實作很多的路徑動畫,例如:

貝塞爾曲線開發的藝術

這裡就是簡單的改變二階貝塞爾曲線的控制點來實作曲線的變形。

網上一些比較複雜的變形動畫效果,也是基于這種實作方式,其原理都是通過改變控制點的位置,進而達到對圖形的變換,例如圓形到心形的變化、圓形到五角星的變換,等等。

波浪的繪制是貝塞爾曲線一個非常簡單的應用,而讓波浪進行波動,其實并不需要對控制點進行改變,而是可以通過位移來實作,這裡我們是借助貝塞爾曲線來實作波浪的繪制效果,效果如圖所示:

貝塞爾曲線開發的藝術

波浪動畫實際上并不複雜,但三角函數确實對一些開發者比較困難,開發者可以通過下面的這個網站來模拟三角函數圖像的繪制:

<a href="https://www.desmos.com/calculator">https://www.desmos.com/calculator</a>

貝塞爾曲線開發的藝術

貝塞爾曲線的另一個非常常用的功能,就是作為動畫的運動軌迹,讓動畫目标能夠沿曲線平滑的實作移動動畫,也就是讓物體沿着貝塞爾曲線運動,而不是機械的直線,本例實作效果如下所示:

貝塞爾曲線開發的藝術

其中,用于改變運動點坐标的關鍵evaluator如下所示:

這裡的TypeEvaluator計算用到了計算貝塞爾曲線上點的計算算法,這個會在後面繼續講解。

求貝塞爾曲線上任意一點的坐标,這一過程,就是利用了De Casteljau算法。

<a href="http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html">http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html</a>

貝塞爾曲線開發的藝術

利用這一算法,有開發者開發了一個示範多階貝塞爾曲線的效果的App,其原理就是通過繪制貝塞爾曲線上的點來進行繪制的,位址如下所示:

<a href="https://github.com/venshine/BezierMaker">https://github.com/venshine/BezierMaker</a>

下面這篇文章就詳細的講解了該算法的應用,我的代碼也從這裡提取而來:

<a href="http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/">http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/</a>

有了公式,隻需要代碼實作就OK了,我們先寫兩個公式:

我們來将路徑繪制到View中,看是否正确:

這次我們并沒有通過API提供的貝塞爾曲線繪制方法來繪制二階、三階貝塞爾曲線,而是通過時間t和起始點來計算一條貝塞爾曲線上的所有點,可以發現,通過算法計算出來的點,與通過API所繪制出來的點,是完全吻合的。

貝塞爾曲線有一個非常常用的動畫效果——MetaBall算法。相信很多開發者都見過類似的動畫,例如QQ的小紅點消除,UC浏覽器的下拉重新整理loading等等。要做好這個動畫,實際上最重要的就是通過貝塞爾曲線來拟合兩個圖形。

效果如圖所示:

貝塞爾曲線開發的藝術

我們來看一下拟合的原理,實際上就是通過貝塞爾曲線來連接配接兩個圓上的四個點,當我們調整下畫筆的填充方式,并繪制一些輔助線,我們來看具體是如何進行拟合的,如圖所示:

貝塞爾曲線開發的藝術

可以發現,控制點為兩圓圓心連線的中點,連接配接線為圖中的這樣一個矩形,當圓比較小時,這種通過矩形來拟合的方式幾乎是沒有問題的,但我們把圓放大,再來看下這種拟合,如圖所示:

貝塞爾曲線開發的藝術

當圓的半徑擴大之後,就可以非常明顯的發現拟合的連接配接點與圓有一定相交的區域,這樣的拟合效果就不好了,我們将畫筆模式調整回來,如圖所示:

貝塞爾曲線開發的藝術

是以,簡單的矩形拟合,在圓半徑小的時候,是可以的,但當圓半徑變大之後,就需要更加嚴格的拟合了。

這裡我們先來講解下,如何計算矩形拟合的幾個關鍵點。

從前面那張線圖可以看出,标紅的兩個角是相等的,而這個角可以通過兩個圓心的坐标來算出,有了這樣一個角度,通過R x cos和 R x sin來計算矩形的一個頂點的坐标,類似的,其它坐标可求,關鍵代碼如下所示:

如前面所說,矩形拟合在半徑較小的情況下,是可以實作完美拟合的,而當半徑變大後,就會出現貝塞爾曲線與圓相交的情況,導緻拟合失敗。

那麼如何來實作完美的拟合呢?實際上,也就是說貝塞爾曲線與圓的連接配接點到貝塞爾曲線的控制點的連線,一定是圓的切線,這樣的話,無論圓的半徑如何變化,貝塞爾曲線一定是與圓拟合的,具體效果如圖所示:

貝塞爾曲線開發的藝術

這時候我們把畫筆模式調整回來看下填充效果,如圖所示:

貝塞爾曲線開發的藝術

這樣拟合是非常完美的。那麼要如何來計算這些拟合的關鍵點呢?在前面的線圖中,我标記出了兩個角,這兩個角分别可以求出,相減,就可以擷取切點與圓心的夾角了,這樣,通過R x cos和R x sin就可以求出切點的坐标了。

其中,小的角可以通過兩個圓心的坐标來求出,而大的角,可以通過直角三角形(圓心、切點、控制點)來求出,即控制點到圓心的距離/半徑。

關鍵代碼如下所示:

貝塞爾曲線做動畫,很多時候都需要使用到圓的特效,而通過二階、三階貝塞爾曲線來拟合圓,也不是一個非常簡單的事情,是以,我直接把結論拿出來了,具體的算法位址如下所示:

<a href="http://spencermortensen.com/articles/bezier-circle/">http://spencermortensen.com/articles/bezier-circle/</a>

<a href="http://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves">http://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves</a>

貝塞爾曲線開發的藝術
貝塞爾曲線開發的藝術

有了貝塞爾曲線的控制點,再對其實作動畫,就非常簡單了,與之前的動畫沒有太大的差別。

本次的講解代碼已經全部上傳到Github :

<a href="https://github.com/xuyisheng/BezierArt">https://github.com/xuyisheng/BezierArt</a>

歡迎大家提issue。

繼續閱讀