關於貝塞爾曲線的故事


概述

  • 在開始本故事的之前,先來介紹下故事的背景。話說幾百年前,從天而降一座神山,遠遠看去像一天光滑的絲帶,它的名字叫做:“貝塞爾曲線"。有大法師預言登上這座神山可以發現天地大秘但是前途艱險。

定義

  • 摘自百度百科

貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用於二維圖形應用程序的數學曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節點組成,節點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。
“貝賽爾曲線”是由法國數學家Pierre Bézier所發明,由此為計算機矢量圖形學奠定了基礎。它的主要意義在於無論是直線或曲線都能在數學上予以描述。

公式

  • 由於應用用到主要以二階貝塞爾曲線為主,貼下二階的公式:

二次方公式
二次方貝茲曲線的路徑由給定點P0、P1、P2的函數B(t):

如何應用?

  • 為了前往"貝塞爾曲線山",向那些從前登上神山的老前輩請教;

所需的Android知識

  • 畫筆(paint),路徑(path),畫布(canvas)類的api要熟悉

  • View繪制的生命周期

簡單來看:測量-measure 擺放-layout 繪制-draw

  • Android觸摸事件

這里需要了解onTouchEvent方法可以捕捉到觸屏的事件

用手勢畫光滑的曲線

  • 路途艱險,在這里我碰到了大白虎,史前巨獸猛獁象,海天鯤鵬,經歷了生死考驗終於登上神山,恍然大悟,天地大秘原來在此。

  • 讓我們想一想畫東西需要什么?答案:一塊白板,一只筆。

  • 這里的關鍵是手勢與光滑,處理手勢的話就是前面講的重寫Android觸摸事件,聰明的你一定想到了通過二階貝塞爾曲線去做到光滑。

  • 畫一條二階貝塞爾曲線需要3個點,兩個數據點一個控制點,那么手勢落下的點--起始點(x1,y1)與不斷移動的手的觸點是數據點,控制點需要自己創造,那線段的中點是最好計算的,假設第一個手滑動到的點(x2,y2),那么中點就是((x1+x2)/2,(y1+y2)/2)。

  • 重寫Android觸摸事件需要捕捉MOVE類型與DOWN類型的事件,DOWN類型的事件中需要記錄起始點的位置,而MOVE類型事件需要緩沖上一次移動的位置。

  • 1.聲明控制點,曲線,起始點,以及判定滑動的距離

    private  Paint controlPaint;

    private Path mCurrentPath;

    private float startPointX;
    private float startPointY;
    //畫貝塞爾曲線的標識--可以自定義值
    private float offset = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  • 2.初始化畫筆與路徑
  public PaintBeSaiErView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //建立路徑
        mCurrentPath = new Path();
        //繪制時抗鋸齒
        controlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //設置畫筆樣式
        controlPaint.setStyle(Paint.Style.STROKE);
        //設置畫筆的粗細
        controlPaint.setStrokeWidth(8);
        //設置畫筆顏色
        controlPaint.setColor(Color.RED);

    }
  • 3.重寫onTouchEvent,記錄手勢起始點與移動位置並繪制貝塞爾曲線,通過invalidate方法更新UI視圖
  @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //為了方便測試,每次下落之前清空路徑
                mCurrentPath.reset();
                float x = event.getX();
                float y = event.getY();
                startPointX = x;
                startPointY = y;
                //移動到起始點
                mCurrentPath.moveTo(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                float curX= event.getX();
                float curY= event.getY();
                float preX= startPointX;
                float preY= startPointY;
                if(Math.abs(preX-curX)>=offset||Math.abs(preX-curY)>=offset) {
                    mCurrentPath.quadTo((curX + preX) / 2, (curY + preY) / 2, curX, curY);
                    startPointX = curX;
                    startPointY = curY;
                }
                invalidate();
                break;
        }
        return  true;
    }
  • 4.下面對比使用線段畫lineTo(curX, curY)與貝塞爾曲線畫quadTo(avgX,avgY)的效果

左圖為線段畫的,右圖為貝塞爾曲線畫的,看起來更圓潤!why?其實,用線段畫基本上看是一個折線圖,而貝塞爾函數畫是一段段曲線

  • 當然,貝塞爾曲線的應用十分廣泛,上面是簡單的例子,后面將講如何應用模擬翻頁。

總結

  • 總以為登上神山才是最大的收獲,原來一路走來更有收獲。
  • 去了解一個事物的時候,要善於思考,記憶中越來越深刻的,往往思考的越透徹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM