二次貝塞爾曲線繪制原理講解

二次貝塞爾曲線

貝塞爾曲線簡介

貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟(jì)埃曲線,是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段節(jié)點(diǎn)組成,節(jié)點(diǎn)是可拖動的支點(diǎn),線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。貝塞爾曲線是計(jì)算機(jī)圖形學(xué)中相當(dāng)重要的參數(shù)曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具,如PhotoShop等。在Flash4中還沒有完整的曲線工具,而在Flash5里面已經(jīng)提供出貝塞爾曲線工具。

貝塞爾曲線于1962,由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所廣泛發(fā)表,他運(yùn)用貝塞爾曲線來為汽車的主體進(jìn)行設(shè)計(jì)。貝塞爾曲線最初由Paul de Casteljau于1959年運(yùn)用de Casteljau演算法開發(fā),以穩(wěn)定數(shù)值的方法求出貝茲曲線。

以上內(nèi)容從采取自百度百科

貝塞爾曲線目前被廣泛應(yīng)用于計(jì)算機(jī)制圖中,可以說貝塞爾曲線奠定了計(jì)算機(jī)制圖的基礎(chǔ)。

Android中繪制Path的API

Android中繪制Path常用方法:

作用 相關(guān)方法 備注
移動起點(diǎn) moveTo 移動下一次操作的起點(diǎn)位置
設(shè)置終點(diǎn) setLastPoint 重置當(dāng)前path中最后一個(gè)點(diǎn)位置,如果在繪制之前調(diào)用,效果和moveTo相同
連接直線 lineTo 添加上一個(gè)點(diǎn)到當(dāng)前點(diǎn)之間的直線到Path
閉合路徑 close 連接第一個(gè)點(diǎn)連接到最后一個(gè)點(diǎn),形成一個(gè)閉合區(qū)域
添加內(nèi)容 addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo 添加(矩形, 圓角矩形, 橢圓, 圓, 路徑, 圓弧) 到當(dāng)前Path (注意addArc和arcTo的區(qū)別)
是否為空 isEmpty 判斷Path是否為空
是否為矩形 isRect 判斷path是否是一個(gè)矩形
替換路徑 set 用新的路徑替換到當(dāng)前路徑所有內(nèi)容
偏移路徑 offset 對當(dāng)前路徑之前的操作進(jìn)行偏移(不會影響之后的操作)
貝塞爾曲線 quadTo, cubicTo 分別為二次和三次貝塞爾曲線的方法
rXxx方法 rMoveTo, rLineTo, rQuadTo, rCubicTo 不帶r的方法是基于原點(diǎn)的坐標(biāo)系(偏移量), rXxx方法是基于當(dāng)前點(diǎn)坐標(biāo)系(偏移量)
填充模式 setFillType, getFillType, isInverseFillType, toggleInverseFillType 設(shè)置,獲取,判斷和切換填充模式
提示方法 incReserve 提示Path還有多少個(gè)點(diǎn)等待加入(這個(gè)方法貌似會讓Path優(yōu)化存儲結(jié)構(gòu))
布爾操作(API19) op 對兩個(gè)Path進(jìn)行布爾運(yùn)算(即取交集、并集等操作)
計(jì)算邊界 computeBounds 計(jì)算Path的邊界
重置路徑 reset, rewind 清除Path中的內(nèi)容

reset不保留內(nèi)部數(shù)據(jù)結(jié)構(gòu),但會保留FillType.
rewind會保留內(nèi)部的數(shù)據(jù)結(jié)構(gòu),但不保留FillType |
| 矩陣操作 | transform | 矩陣變換 |

貝塞爾曲線應(yīng)用簡單場景:

  • QQ小紅點(diǎn)拖拽效果
  • 平滑的折線圖的制作
  • 閱讀軟件的翻書效果

繪制貝塞爾曲線

一階貝塞爾曲線

一階貝塞爾曲線的原理就是一條直線,其實(shí)就是直接畫了一個(gè)Path出來,在這里不做過多的介紹,本文主要介紹的是二階的貝塞爾曲線。

二階貝塞爾曲線

二階貝塞爾曲線效果圖:

bezier

實(shí)現(xiàn)原理

二階曲線由兩個(gè)數(shù)據(jù)點(diǎn)(A 和 C),一個(gè)控制點(diǎn)(B)來描述曲線狀態(tài),大致如下:

image

上圖中紅色曲線部分就是傳說中的二階貝塞爾曲線,那么這條紅色曲線是如何生成的呢?接下來我們就以其中的一個(gè)狀態(tài)分析一下:

image

連接AB BC,并在AB上取點(diǎn)D,BC上取點(diǎn)E,使其滿足條件:


image.png
image

連接DE,取點(diǎn)F,使得:

AD/AB = BE/BC = DF/DE

這樣獲取到的點(diǎn)F就是貝塞爾曲線上的一個(gè)點(diǎn),動態(tài)過程如下:

image

實(shí)現(xiàn)代碼

public class Bezier2 extends View {

    private Paint mPaint;
    private int centerX, centerY;

    private PointF start, end, control;

    public Bezier2(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(8);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTextSize(60);

        start = new PointF(0,0);
        end = new PointF(0,0);
        control = new PointF(0,0);
    }

    public Bezier2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(8);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTextSize(60);

        start = new PointF(0,0);
        end = new PointF(0,0);
        control = new PointF(0,0);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w/2;
        centerY = h/2;

        // 初始化數(shù)據(jù)點(diǎn)和控制點(diǎn)的位置
        start.x = centerX-200;
        start.y = centerY;
        end.x = centerX+200;
        end.y = centerY;
        control.x = centerX;
        control.y = centerY-100;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 根據(jù)觸摸位置更新控制點(diǎn),并提示重繪
        control.x = event.getX();
        control.y = event.getY();
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 繪制數(shù)據(jù)點(diǎn)和控制點(diǎn)
        mPaint.setColor(Color.GRAY);
        mPaint.setStrokeWidth(20);
        canvas.drawPoint(start.x,start.y,mPaint);
        canvas.drawPoint(end.x,end.y,mPaint);
        canvas.drawPoint(control.x,control.y,mPaint);

        // 繪制輔助線
        mPaint.setStrokeWidth(4);
        canvas.drawLine(start.x,start.y,control.x,control.y,mPaint);
        canvas.drawLine(end.x,end.y,control.x,control.y,mPaint);

        // 繪制貝塞爾曲線
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(8);

        Path path = new Path();

        path.moveTo(start.x,start.y);
        path.quadTo(control.x,control.y,end.x,end.y);

        canvas.drawPath(path, mPaint);
    }
}

項(xiàng)目地址:https://github.com/pengMaster/Bezier

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

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