貝塞爾曲線

貝塞爾曲線的來(lái)歷

貝塞爾曲線的數(shù)學(xué)基礎(chǔ)是早在 1912 年就廣為人知的[伯恩斯坦多項(xiàng)式。直到1959年。當(dāng)時(shí)就職于雪鐵龍的法國(guó)數(shù)學(xué)家 Paul de Casteljau 開始對(duì)伯恩斯坦多項(xiàng)式進(jìn)行了圖形化的嘗試,并且提供了一種數(shù)值穩(wěn)定的德卡斯特里奧(de Casteljau) 算法。(多數(shù)理論公式是建立在大量且系統(tǒng)的數(shù)學(xué)建?;A(chǔ)之上研究的規(guī)律性成果)根據(jù)這個(gè)算法,就可以實(shí)現(xiàn) 通過(guò)很少的控制點(diǎn),去生成復(fù)雜的平滑曲線,也就是貝塞爾曲線。

但貝塞爾曲線的聲名大噪,不得不提到1962年就職于雷諾的法國(guó)工程師皮埃爾·貝塞爾(Pierre Bézier),他使用這種方法來(lái)輔助汽車的車體工業(yè)設(shè)計(jì)(最早計(jì)算機(jī)的誕生則是為了幫助美國(guó)海軍繪制彈道圖),并且廣泛宣傳(典型的理論聯(lián)系實(shí)際并獲得成功的示例),因此大家稱為貝塞爾曲線。

貝塞爾曲線的數(shù)學(xué)理論

貝塞爾曲線的本質(zhì)是通過(guò)數(shù)學(xué)計(jì)算公式去繪制平滑的曲線,這些曲線是用一系列點(diǎn)來(lái)控制曲線狀態(tài)的,可以將這些點(diǎn)分為2類,一類是數(shù)據(jù)點(diǎn),一類是控制點(diǎn)。

N 階貝塞爾

可以理解為有N+1個(gè)點(diǎn) N+1減1 就是 N階貝塞爾,比如3階就是4個(gè)點(diǎn)鏈接的線,4-1=3,就叫做3階貝塞爾

一階貝塞爾

image

可以看到一階貝塞爾是一條直線,可以通過(guò)幾何知識(shí)得到t的坐標(biāo)點(diǎn)


image.png

二階貝塞爾

  • 步驟一:在平面上選擇3個(gè)不同的點(diǎn)用線依次鏈接


    image.png
  • 步驟二:在AB和BC線段上找對(duì)點(diǎn)D和E,并且AD/AB = BE/BC

image
  • 步驟三:鏈接DE,在DE上選擇點(diǎn)F,F(xiàn)點(diǎn)滿足DF/DE=AD/AB = BE/BC


    image
  • 步驟四:根據(jù)DE線段和計(jì)算公式找出所有的F點(diǎn),然后將F點(diǎn)鏈接起來(lái)。連接規(guī)則比如上圖,A-F..F1..F2..C,F(xiàn)點(diǎn)必須滿足DF/DE = AD/AB=BE/BC

    image

Path中貝塞爾曲線的實(shí)現(xiàn)

二階貝塞爾

起點(diǎn)為之前的結(jié)束點(diǎn),或者是下筆點(diǎn)
4個(gè)參數(shù),x1,y1,為控制點(diǎn),也就是B點(diǎn),x2,y2為終點(diǎn)
public void quadTo(float x1, float y1, float x2, float y2)
Path path = new Path();
path.moveTo(100,100); //  起點(diǎn)
path.quadTo(400,200,50,500); //控制點(diǎn)和終點(diǎn)
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawPath(path,paint1);
image.png

三階貝塞爾

起點(diǎn)為之前的結(jié)束點(diǎn),或者是下筆點(diǎn)
x1,y1第一個(gè)控制點(diǎn),x2,y2第二個(gè)控制點(diǎn),x3,y3為終點(diǎn)
public void cubicTo(float x1, float y1, float x2, float y2,
                        float x3, float y3)
Path path  = new Path();
path.moveTo(100,100);
path.cubicTo(400,200,50,500,400,400);
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawCircle(400,400,10,paint);
canvas.drawPath(path,paint1);
image.png

N 階

//生成貝塞爾Path
private ArrayList<PointF> buildBezierPoints() {
        mPath.reset();
        ArrayList<PointF> points = new ArrayList<>();
        int order = controlPoints.size() - 1;//階數(shù)
        //份數(shù)
        float delta = 1.0f / 1000;
        for (float t = 0; t <= 1; t += delta) {
            //bezier點(diǎn)集
            PointF pointF = new PointF(deCastelJau(order, 0, t, true), deCastelJau(order, 0, t, false));//計(jì)算在曲線上點(diǎn)位置
            points.add(pointF);
            if (points.size() == 1) {
                mPath.moveTo(points.get(0).x, points.get(0).y);
            } else {
                mPath.lineTo(pointF.x, pointF.y);
            }
        }
        return points;
    }

    /**
     * p(i,j) =  (1-t) * p(i-1,j)  +  t * p(i-1,j+1);
     *
     * @param i          階數(shù)
     * @param j          控制點(diǎn)
     * @param t          時(shí)間
     * @param calculateX 計(jì)算哪個(gè)坐標(biāo)值 true=x
     * @return
     */
    private float deCastelJau(int i, int j, float t, boolean calculateX) {
        if (i == 1) {
            return calculateX ? (1 - t) * controlPoints.get(j).x + t * controlPoints.get(j + 1).x :
                    (1 - t) * controlPoints.get(j).y + t * controlPoints.get(j + 1).y;
        } else {
            return (1 - t) * deCastelJau(i - 1, j, t, calculateX) + t * deCastelJau(i - 1, j + 1, t, calculateX);
        }
    }


PointF pointF;
        for (int i = 0; i < controlPoints.size(); i++) {
            pointF = controlPoints.get(i);
            if (i > 0) {
                paint.setColor(Color.GRAY);
                canvas.drawLine(controlPoints.get(i - 1).x,
                        controlPoints.get(i - 1).y,
                        pointF.x, pointF.y, paint);
            }

            if (i == 0) {
                //起點(diǎn)為紅色
                paint.setColor(Color.RED);
            } else if (i == controlPoints.size() - 1) {
                //終點(diǎn)為藍(lán)色
                paint.setColor(Color.BLUE);
            }

            canvas.drawCircle(pointF.x
                    , pointF.y, 10, paint);  //畫所有的控制點(diǎn)
        }

        buildBezierPoints();    //生成貝塞爾Path
        canvas.drawPath(mPath, paint1);  //畫線
image.png

參考:http://www.itdecent.cn/p/0c9b4b681724

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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