n 階貝塞爾曲線計算公式——Ts實現


1、什麼是貝塞爾曲線

  Bézier curve(貝塞爾曲線)是應用於二維圖形應用程序的數學曲線。 曲線定義:起始點、終止點(也稱錨點)、控制點。通過調整控制點,貝塞爾曲線的形狀會發生變化。 1962年,法國數學家Pierre Bézier第一個研究了這種矢量繪製曲線的方法,並給出了詳細的計算公式,因此按照這樣的公式繪製出來的曲線就用他的姓氏來命名,稱爲貝塞爾曲線。
  看了這段話,相信你還是不大明白貝塞爾曲線到底是怎麼樣的,這裏放上貝塞爾曲線掃盲貼,有需要的自行閱讀!這裏有個貝塞爾遊戲,有興趣的可以體驗一下!

2、常見貝塞爾曲線

  這裏我們放上去常見的貝塞爾曲線效果演示圖:

以下公式中:
B(t)爲t時間下 點的座標
P0爲起點,Pn爲終點,Pi爲控制點

  • 一階貝塞爾曲線(線段)

一階貝塞爾曲線

一階貝塞爾曲線通用公式:
B(t)=(1t)P0+tP1t[0,1] \begin{aligned} B(t)=(1-t)P_0+tP_1, t∈[0,1] \end{aligned}

意義:由 P0 至 P1 的連續點, 描述的一條線段

  • 二階貝塞爾曲線(拋物線)
    二階貝塞爾曲線
    二階貝塞爾曲線通用公式:
    B(t)=(1t)2P0+2t(1t)P1+t2P2t[0,1] \begin{aligned} B(t)=(1-t)^2P_0+2t(1-t)P_1+t^2P_2, t∈[0,1] \end{aligned}

原理:
由 P0 至 P1 的連續點 Q0,描述一條線段。
由 P1 至 P2 的連續點 Q1,描述一條線段。
由 Q0 至 Q1 的連續點 B(t),描述一條二次貝塞爾曲線。
經驗:P1-P0爲曲線在P0處的切線

  • 三階貝塞爾曲線

三階貝塞爾曲線
三階貝塞爾曲線通用公式:
B(t)=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3t[0,1] \begin{aligned} B(t)=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3, t∈[0,1] \end{aligned}

  • 四階貝塞爾曲線

四階貝塞爾曲線

  • 五階貝塞爾曲線

五階貝塞爾曲線

3、貝塞爾曲線通用公式

3.1、貝塞爾曲線通用公式

  我們查閱資料給出的一般公式是這樣的:

B(t)=i=0n(ni)Pi(1t)niti=(n0)P0(1t)nt0+(n1)P1(1t)n1t1+...+(nn1)Pn1(1t)1tn1+(nn)Pn(1t)0tnt[0,1] \begin{aligned} B(t)=\sum_{i=0}^n \begin{pmatrix} n\\i \end{pmatrix} P_i(1-t)^{n-i}t^i= \begin{pmatrix} n\\0 \end{pmatrix} P_0(1-t)^nt^0+ \begin{pmatrix} n\\1 \end{pmatrix} P_1(1-t)^{n-1}t^1+...+ \begin{pmatrix} n\\n-1 \end{pmatrix} P_{n-1}(1-t)^1t^{n-1}+ \begin{pmatrix} n\\n \end{pmatrix} P_n(1-t)^0t^n, t\in[0,1] \end{aligned}

3.2、思路解析

觀察上面公式,可以查看出,其公式是由一個格式固定的表達式之和來表示,這個表達式就是關鍵:
(ni)Pi(1t)nitit[0,1] \begin{aligned} \begin{pmatrix} n\\i \end{pmatrix} P_i(1-t)^{n-i}t^i, t\in[0,1] \end{aligned}

該表達式可分爲四個部分看:

  • 從 i 遞增到 n 的常數部分
  • PiP_i 座標部分
  • (1t)ni(1 - t)^{n - i}
  • tit^i

可以看出這四部分都與 i 的值相關,此外 t 值的計算方式爲:i/(n+1)

  如果直接從上面的公式上找規律比較抽象,那就從具體的例子中找規律吧。
  設 Bt 爲要計算的貝塞爾曲線上的座標,N 爲控制點個數,P0,P1,P2…Pn 爲貝塞爾曲線控制點的座標,當 N 值不同時有如下計算公式:

如 N 爲 3 表示貝塞爾曲線的控制點有 3 個點,這時 n 爲 2 ,這三個點分別用 P0,P1,P2 表示。

  • N = 3  P=(1t)2P0+2(1t)tP1+t2P2P=(1-t)^2P_0 + 2(1-t)tP_1 + t^2P_2
  • N = 4  P=(1t)3P0+3(1t)2tP1+3(1t)t2P2+t3P3P= (1-t)^3P_0 + 3(1-t)^2tP_1 + 3(1-t)t^2P_2 + t^3P_3
  • N = 5  P=(1t)4P0+4(1t)3tP1+6(1t)2t2P2+4(1t)t3P3+t4P4P = (1-t)^4P_0 + 4(1-t)^3tP_1 + 6(1-t)^2t^2P_2 + 4(1-t)t^3P_3 + t^4P_4

  將貝塞爾曲線一般參數公式中的表達式用如下方式表示,設有常數 a,b 和 c,則該表達式可統一表示爲如下形式:
a(1t)btcPn,t[0,1] a(1 - t)^bt^cP_n, t\in[0,1]

  分析當 N 分別爲3,4,5 時對應 a,b,c 的值:
如 N = 3 時,公式有三個表達式,第一個表達式爲 (1t)2P0(1-t)^2P_0,其對應 a,b,c 值分別爲:1,2,0

  • N = 3: 1,2,0 2,1,1 1,0,2
    a: 1 2 1
    b: 2 1 0
    c: 0 1 2
  • N = 4: 1,3,0 3,2,1 3,1,2 1,0,3
    a: 1 3 3 1
    b: 3 2 1 0
    c: 0 1 2 3
  • N = 5: 1,4,0 4,3,1 6,2,2 4,1,3 1,0,4
    a: 1 4 6 4 1
    b: 4 3 2 1 0
    c: 0 1 2 3 4

  根據上面的分析就可以總結出 a,b,c 對應的取值規則:

  • b: (N - 1) 遞減到 0 (b 爲 1-t 的冪)
  • c: 0 遞增到 (N - 1) (c 爲 t 的冪)
  • a: 在 N 分別爲 1,2,3,4,5 時將其值用如下形式表示:
    N=1:———1
    N=2:——–1 1
    N=3:——1 2 1
    N=4:—–1 3 3 1
    N=5:—1 4 6 4 1
    a 值的改變規則爲: 楊輝三角

3.3、實現方法

  好了,到這裏我們基本上已經知道思路了,下面我們使用Ts寫一下:

這裏使用的是:
Vscode
Cocos Creator

/**
  * 
   * @param ctrlPosArr 貝塞爾曲線控制點座標
   * @param precison 精度,需要計算的該條貝塞爾曲線上的點的數目
   * @param resArr 該條貝塞爾曲線上的點(二維座標)
    */
    getBezierPos(ctrlPosArr:Array<cc.Vec2>,precison:number):Array<cc.Vec2>
    {
        cc.log(ctrlPosArr)
        let resArr:Array<cc.Vec2> = new Array<cc.Vec2>();

        /**貝塞爾曲線控制點數目(階數)*/
        let number:number = ctrlPosArr.length;

        if(number < 2)
        {
            cc.log("控制點數不能小於 2");
            return resArr;
        }

        /**楊輝三角數據 */
        let yangHuiArr:Array<number> = this.getYangHuiTriangle(number);

        //計算座標
        for (let i = 0; i < precison; i++) {
            
            let t:number = i/precison;
            let tmpX:number = 0;
            let tmpY:number = 0;

            for (let j = 0; j < number; j++) {
                
                tmpX += Math.pow(1 - t,number - j - 1) * ctrlPosArr[j].x * Math.pow(t,j) * yangHuiArr[j];

                tmpY += Math.pow(1 - t,number - j - 1) * ctrlPosArr[j].y * Math.pow(t,j) * yangHuiArr[j];
            }

            // resArr[i].x = tmpX;
            // resArr[i].y = tmpY;

            resArr[i] = new cc.Vec2(tmpX,tmpY);
        }

        return resArr;
    }

    /**
     * 獲取楊輝三角對應階數的值
     * @param num 楊輝三角階數
     */
    getYangHuiTriangle(num:number):Array<number>
    {
        //計算楊輝三角
        let yangHuiArr = new Array<number>();

        if(num === 1)
        {
            yangHuiArr[0] = 1;
        }
        else
        {
            yangHuiArr[0] = yangHuiArr[1] = 1;

            for (let i = 3; i <= num; i++) 
            {
                let t = new Array<number>();
                for (let j = 0; j < i - 1; j++) 
                {
                    t[j] = yangHuiArr[j];
                }

                yangHuiArr[0] = yangHuiArr[i - 1] = 1;
                for (let j = 0; j < i - 2; j++) 
                {
                    yangHuiArr[j + 1] = t[j] + t[j + 1];            
                }
            }
        }

        cc.log(yangHuiArr);
        return yangHuiArr;
    }

3.4、效果展示

  下面我取幾個點,做一下演示:

let p1:cc.Vec2 = cc.v2(0,0);
        let p2:cc.Vec2 = cc.v2(200,200);
        let p3:cc.Vec2 = cc.v2(400,150);
        let p4:cc.Vec2 = cc.v2(500,200);
        this.drawPoint(p1);
        this.drawPoint(p2);
        this.drawPoint(p3);
        this.drawPoint(p4);
        this.drawLine(p1,p2,cc.Color.GREEN);
        this.drawLine(p2,p3,cc.Color.GREEN);
        this.drawLine(p3,p4,cc.Color.GREEN);
        
        let posArr1:Array<cc.Vec2> = [
            cc.v2(-150,80)
            ,cc.v2(1,80)
            ,cc.v2(48,92)
            ,cc.v2(167,159)
            ,cc.v2(309,271)
            ,cc.v2(421,394)
            ,cc.v2(514,498)
            ,cc.v2(597,572)
            ,cc.v2(658,590)
            ,cc.v2(745,550)
            ,cc.v2(802,465),
            cc.v2(841,320)
            ,cc.v2(866,266)
            ,cc.v2(951,163)
            ,cc.v2(1054,133)
            ,cc.v2(1228,126)
            ,cc.v2(1278,128)
            ,cc.v2(1430,128)
        ]

  爲了驗證正確性,我這裏先用ts自帶的貝塞爾曲線公式,通過四個點來畫出三階貝塞爾曲線,再用自己的點來畫一遍,然後用自己的代碼來畫n階貝塞爾曲線。效果如下:
在這裏插入圖片描述

3.5、Demo下載

  爲了方便大家,當然如果有不明白的童鞋也可以在這裏點此下載Demo示例

4、結束語


The End
  好了,今天的分享就到這裏,如有不足之處,還望大家及時指正,隨時歡迎探討交流!!!


喜歡的朋友們,請收藏、點贊、評論!您的肯定是我寫作的不竭動力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章