久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    兩張圖教你使用二三階貝塞爾曲線

     小橋流水ytff06 2018-03-20

    兩張圖教你使用二三階貝塞爾曲線 - CSDN博客 http://blog.csdn.net/DylanZhuang/article/details/51896564

    Bézier curve(貝塞爾曲線)是應用于二維圖形應用程序的數學曲線 曲線定義:起始點、終止點(也稱錨點)、控制點。通過調整控制點,貝塞爾曲線的形狀會發生化。 1962年,法國數學家Pierre Bézier第一個研究了這種矢量繪制曲線的方法,并給出了詳細的計算公式,因此按照這樣的公式繪制出來的曲線就用他的姓氏來命名,稱為貝塞爾曲線。

    線性公式

    給定點p0、p1,線性貝塞爾曲線只是一條兩點之間的直線,公式如下:

    二次方公式

    二次方貝塞爾曲線的路徑由給定點p0、p1、p2的函數B(t),公式如下:


    三次方公式

    p0、p1、p2、p3四個點在平面或在三維空間定義了三次貝塞爾曲線。曲線起始于p0走向p1,并從p2的方向來到p3.一般不會經過p1或者p2;這兩點只是在那里提供了方向資訊。p0和p1之間的間距,決定了曲線在轉而趨進p3之前,走向p2方向的“長度有多長”,公式如下:

    上面這段是摘自百度百科,由上面的動態圖可以看出,一階貝塞爾曲線是由兩點控制的一條直線,二階貝塞爾曲線是由一個控制點控制的曲線,三階貝塞爾曲線是由兩個控制點控制的曲線,至于三階以上的不做研究

    下面看一下二階貝塞爾曲線運行的效果圖:


    設置二階貝塞爾曲線的方法如下

    moveTo(float x, float y) 其中x、y坐標代表圖中曲線靠左邊起點的坐標位置

    quadTo(float x1, float y1, float x2, float y2) 其中x1、y1坐標代表圖中移動點的坐標,也就是我們所說的二階貝塞爾曲線的控制點坐標;x2、y2坐標代表圖中曲線靠右邊終點的坐標位置

    首先我們要重寫view的onTouchEvent的事件,并對該事件進行攔截,也就是返回值為true,代碼如下:

    1. @Override    
    2.  public boolean onTouchEvent(MotionEvent event) {    
    3.      switch (event.getAction()) {    
    4.          case MotionEvent.ACTION_DOWN:    
    5.              break;    
    6.          case MotionEvent.ACTION_MOVE:    
    7.              int moveX = (int) (event.getX());    
    8.              int moveY = (int) (event.getY());    
    9.              mControlPoint.x = moveX;    
    10.              mControlPoint.y = moveY;    
    11.              invalidate();    
    12.              break;    
    13.      }    
    14.      return true;    
    15.  }  

    在move事件中,獲取到控制點的坐標,并在onDraw方法中進行路徑的繪制,代碼如下:

    初始化起始點:

    1. mPaint = new Paint();    
    2. mPaint.setStyle(Paint.Style.STROKE);    
    3.     
    4. DisplayMetrics displayMetrics = getResources().getDisplayMetrics();    
    5. mWidth = displayMetrics.widthPixels;    
    6. mHeight = displayMetrics.heightPixels;    
    7.     
    8. mStartPoint.set(100, mHeight / 2);    
    9. mEndPoint.set(mWidth - 100, mHeight / 2);    
    10. mControlPoint.set(mWidth / 2, 100);  

    進行繪制:

    1. private void drawQuadraticBezier(Canvas canvas) {    
    2.     mPaint.setColor(Color.RED);    
    3.     mPaint.setStrokeWidth(20);    
    4.     mPaint.setStyle(Paint.Style.STROKE);    
    5.     canvas.drawCircle(mControlPoint.x, mControlPoint.y, 10, mPaint);    
    6.     
    7.     mPaint.setStrokeWidth(10);    
    8.     mPaint.setStyle(Paint.Style.FILL);    
    9.     float[] lines = {mStartPoint.x, mStartPoint.y, mControlPoint.x, mControlPoint.y,    
    10.             mControlPoint.x, mControlPoint.y, mEndPoint.x, mEndPoint.y,    
    11.             mEndPoint.x, mEndPoint.y, mStartPoint.x, mStartPoint.y};    
    12.     canvas.drawLines(lines, mPaint);    
    13.     
    14.     mPaint.setColor(Color.GREEN);    
    15.     mPaint.setStyle(Paint.Style.STROKE);    
    16.     Path path = new Path();    
    17.     path.moveTo(mStartPoint.x, mStartPoint.y);    
    18.     path.quadTo(mControlPoint.x, mControlPoint.y, mEndPoint.x, mEndPoint.y);    
    19.     canvas.drawPath(path, mPaint);    
    20. }  

    二階貝塞爾曲線到這里已經介紹完了,接下來介紹下三階貝塞爾曲線,先看下效果圖:

    設置二階貝塞爾曲線的方法如下

    moveTo(float x, float y) 其中x、y坐標代表圖中在圓周上靠左邊起點的坐標位置

    cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 其中x1、y1坐標代表圖中左上角移動點的坐標,x2、y2坐標代表圖中右上角移動點的坐標,x1、y1和x2、y2也就是我們所說的三階貝塞爾曲線的控制點坐標;x3、y3坐標代表圖中在圓周上靠右邊終點的坐標位置

    首先我們要重寫view的onTouchEvent的事件,并對該事件進行攔截,也就是返回值為true,代碼如下:

    1. @Override    
    2.   public boolean onTouchEvent(MotionEvent event) {    
    3.       switch (event.getAction()) {    
    4.           case MotionEvent.ACTION_DOWN:    
    5.               break;    
    6.           case MotionEvent.ACTION_MOVE:    
    7.               int moveX = (int) (event.getX());    
    8.               int moveY = (int) (event.getY());    
    9.     
    10.               int distanceX = Math.abs(mControlPoint.x - moveX);    
    11.               int distanceY = Math.abs(mControlPoint.y - moveY);    
    12.     
    13.               int distanceX1 = Math.abs(mControlPoint1.x - moveX);    
    14.               int distanceY1 = Math.abs(mControlPoint1.y - moveY);    
    15.               if (distanceX < 50 && distanceY < 50) {    
    16.                   mControlPoint.x = moveX;    
    17.                   mControlPoint.y = moveY;    
    18.               } else if (distanceX1 < 50 && distanceY1 < 50) {    
    19.                   mControlPoint1.x = moveX;    
    20.                   mControlPoint1.y = moveY;    
    21.               }    
    22.               invalidate();    
    23.               break;    
    24.       }    
    25.       return true;    
    26.   }    

    在move事件中,判斷當前觸摸的是哪個控制點,并對該控制點進行賦值,繪制代碼如下:初始化數據:

    1. mBloomCenterPoint.set(mWidth / 2, mHeight / 2);    
    2. mStartPoint.set(mWidth / 2, mHeight / 2);    
    3. mEndPoint.set(mWidth / 2, mHeight / 2);    
    4. mControlPoint.set(mWidth / 2 - 200, 100);    
    5. mControlPoint1.set(mWidth / 2 + 200, 100);   

    開始繪制:

    1. private void drawCubicBezier(Canvas canvas) {    
    2.         Point topPoint = new Point(mBloomCenterPoint.x, mBloomCenterPoint.y - mRadius);    
    3.         float angle1 = (mBloomCenterPoint.x - mControlPoint.x) * 1.0f / (mBloomCenterPoint.y - mControlPoint.y);    
    4.         float angle2 = (mBloomCenterPoint.x - mControlPoint1.x) * 1.0f / (mBloomCenterPoint.y - mControlPoint1.y);    
    5.     
    6.         boolean isBig1 = false;    
    7.         boolean isBig2 = false;    
    8.         if (mControlPoint.y > mBloomCenterPoint.y) {    
    9.             isBig1 = true;    
    10.         }    
    11.         if (mControlPoint1.y > mBloomCenterPoint.y) {    
    12.             isBig2 = true;    
    13.         }    
    14.         //獲取三階貝塞爾曲線的起始點的值    
    15.         mStartPoint = getFixPoint(topPoint, angle1, isBig1);    
    16.         mEndPoint = getFixPoint(topPoint, angle2, isBig2);    
    17.     
    18.         mPaint.setColor(Color.RED);    
    19.         mPaint.setStrokeWidth(1);    
    20.         mPaint.setStyle(Paint.Style.STROKE);    
    21.         canvas.drawCircle(mControlPoint.x, mControlPoint.y, 10, mPaint);    
    22.         canvas.drawCircle(mControlPoint1.x, mControlPoint1.y, 10, mPaint);    
    23.         canvas.drawCircle(mBloomCenterPoint.x, mBloomCenterPoint.y, mRadius, mPaint);    
    24.     
    25.         mPaint.setStrokeWidth(10);    
    26.         mPaint.setStyle(Paint.Style.FILL);    
    27.         float[] lines = {mStartPoint.x, mStartPoint.y, mControlPoint.x, mControlPoint.y,    
    28.                 mControlPoint.x, mControlPoint.y, mControlPoint1.x, mControlPoint1.y,    
    29.                 mControlPoint1.x, mControlPoint1.y, mEndPoint.x, mEndPoint.y,    
    30.                 mEndPoint.x, mEndPoint.y, mStartPoint.x, mStartPoint.y};    
    31.         canvas.drawLines(lines, mPaint);    
    32.     
    33.         mPaint.setStrokeWidth(10);    
    34.         mPaint.setColor(Color.GREEN);    
    35.         mPaint.setStyle(Paint.Style.FILL);    
    36.         Path path = new Path();    
    37.         path.moveTo(mStartPoint.x, mStartPoint.y);    
    38.         path.cubicTo(mControlPoint.x, mControlPoint.y, mControlPoint1.x, mControlPoint1.y, mEndPoint.x, mEndPoint.y);    
    39.         canvas.drawPath(path, mPaint);    
    40.     }    

    1. private Point getFixPoint(Point topPoint, float angle, boolean isBig) {    
    2.     double radian = Math.atan(angle);    
    3.     if (isBig) {    
    4.         radian += Math.PI;    
    5.     }    
    6.     double sin = Math.sin(radian);    
    7.     double cos = Math.cos(radian);    
    8.     int x = (int) (topPoint.x - mRadius * sin);    
    9.     int y = (int) (topPoint.y + mRadius * (1 - cos));    
    10.     
    11.     Point point = new Point(x, y);    
    12.     return point;    
    13. }    

    高級進階像360安全衛士清理內存的動態效果大家應該都不陌生吧,我們現在用二階貝塞爾曲線實現這樣的效果,先上效果圖:

    首先我們初始化數據,代碼如下:

    1. private void init() {    
    2.     DisplayMetrics displayMetrics = getResources().getDisplayMetrics();    
    3.     mScreenWidth = displayMetrics.widthPixels;    
    4.     mScreenHeight = displayMetrics.heightPixels;    
    5.     
    6.     int height = mScreenHeight * 7 / 10;    
    7.     mStartPoint.set(mScreenWidth / 10, height);    
    8.     mEndPoint.set(mScreenWidth * 9 / 10, height);    
    9.     
    10.     mRadius = 100;    
    11. }    

    然后重寫onTouchEvent事件,不斷的重繪紅色的球和綠色的曲線,當只有在球與線接觸時,才進行二階貝塞爾曲線的繪制,touch事件的代碼如下:

    1. @Override    
    2. public boolean onTouchEvent(MotionEvent event) {    
    3.     switch (event.getAction()) {    
    4.         case MotionEvent.ACTION_MOVE:    
    5.             int moveX = (int) (event.getX());    
    6.             int moveY = (int) (event.getY());    
    7.     
    8.             mControlPoint.x = moveX;    
    9.             mControlPoint.y = moveY;    
    10.             invalidate();    
    11.             break;    
    12.         case MotionEvent.ACTION_UP:    
    13.             int x = mControlPoint.x;    
    14.             int y = mControlPoint.y;    
    15.             if (y > mStartPoint.y && x > mScreenWidth * 2 / 5    
    16.                     && x < mScreenWidth * 3 / 5) {    
    17.                 startAnim();    
    18.             }    
    19.             break;    
    20.     }    
    21.     return true;    
    22. }    

    當執行ACTION_UP事件時,判斷此時控制點是否進行了二階變換,如果是,則進行動畫的繪制,動畫效果的代碼如下:

    1. private void startAnim() {    
    2.     ValueAnimator valueAnimator = ValueAnimator.ofInt(mControlPoint.y, -10);    
    3.     valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    
    4.         @Override    
    5.         public void onAnimationUpdate(ValueAnimator animation) {    
    6.             mControlPoint.y = (int) animation.getAnimatedValue();    
    7.             invalidate();    
    8.         }    
    9.     });    
    10.     valueAnimator.setDuration(1000);    
    11.     valueAnimator.start();    
    12. }    

    下面看下球跟線接觸時,視圖是怎么繪制的,代碼如下:

    1. @Override    
    2.  protected void onDraw(Canvas canvas) {    
    3.      super.onDraw(canvas);    
    4.     
    5.      Paint paint = new Paint();    
    6.      paint.setStrokeWidth(10);    
    7.      paint.setStyle(Paint.Style.STROKE);    
    8.      paint.setColor(Color.GREEN);    
    9.     
    10.      int x = mControlPoint.x;    
    11.      int y = mControlPoint.y;    
    12.      int height = mStartPoint.y;    
    13.      if (y > mStartPoint.y && x > mScreenWidth * 2 / 5    
    14.              && x < mScreenWidth * 3 / 5) {    
    15.          height = y + y - mStartPoint.y;    
    16.      }    
    17.     
    18.      Path path = new Path();    
    19.      path.moveTo(mStartPoint.x, mStartPoint.y);    
    20.      path.quadTo(mScreenWidth / 2, height, mEndPoint.x, mEndPoint.y);    
    21.      canvas.drawPath(path, paint);    
    22.     
    23.      paint.setStyle(Paint.Style.FILL);    
    24.      paint.setColor(Color.RED);    
    25.      canvas.drawCircle(x, y - mRadius, mRadius, paint);    
    26.  }    

    代碼中控制點高度的計算,是通過二階變換公式相減得到的,到目前為止,該過程的繪制代碼已全部列出。在進行三階貝塞爾曲線變換的時候,綠色部分有點像個花瓣,下面我們用三階貝塞爾曲線,繪制一朵花,效果圖如下:


    我們先用進行下數據的初始化操作,定義些常量,代碼如下:

    1. public interface BloomOption {    
    2.     //用于控制產生隨機花瓣個數范圍    
    3.     int minPetalCount = 8;    
    4.     int maxPetalCount = 12;    
    5.     //用于控制產生延長線倍數范圍    
    6.     float minPetalStretch = 2f;    
    7.     float maxPetalStretch = 3.5f;    
    8.     //用于控制產生花朵半徑隨機數范圍    
    9.     int minBloomRadius = 100;    
    10.     int maxBloomRadius = 300;    
    11. }    

    并進行數據的一些初始化操作:

    1. private void init() {    
    2.     DisplayMetrics displayMetrics = getResources().getDisplayMetrics();    
    3.     int screenWidth = displayMetrics.widthPixels;    
    4.     int screenHeight = displayMetrics.heightPixels;    
    5.     mBloomCenterPoint.set(screenWidth / 2,  screenHeight / 2 - 200);    
    6.     petals = new ArrayList<>();    
    7.     initPetalData();    
    8. }  
    1. private void initPetalData() {    
    2.      int petalCount = RandomUtil.randomInt(minPetalCount, maxPetalCount);    
    3.      //每個花瓣應占用的角度    
    4.      float angle = 360f / petalCount;    
    5.      int startAngle = RandomUtil.randomInt(0, 90);    
    6.     
    7.      for (int i = 0; i < petalCount; i++) {    
    8.          //隨機產生第一個控制點的拉伸倍數    
    9.          float stretchA = RandomUtil.random(minPetalStretch, maxPetalStretch);    
    10.          //隨機產生第二個控制地的拉伸倍數    
    11.          float stretchB = RandomUtil.random(minPetalStretch, maxPetalStretch);    
    12.          //計算每個花瓣的起始角度    
    13.          int beginAngle = startAngle + (int) (i * angle);    
    14.     
    15.          PetalView petal = new PetalView(stretchA, stretchB, beginAngle, angle);    
    16.          petals.add(petal);    
    17.      }    
    18.  }    

    下面進行綠色線條的繪制,代碼如下:

    1. private void drawStem(Canvas canvas) {    
    2.     Paint paint = new Paint();    
    3.     paint.setStrokeWidth(10);    
    4.     paint.setColor(Color.GREEN);    
    5.     paint.setStyle(Paint.Style.STROKE);    
    6.     
    7.     Path path = new Path();    
    8.     path.moveTo(mBloomCenterPoint.x, mBloomCenterPoint.y);    
    9.     path.quadTo(mBloomCenterPoint.x + 50, mBloomCenterPoint.y + 200, mBloomCenterPoint.x - 50, mBloomCenterPoint.y + 600);    
    10.     canvas.drawPath(path, paint);    
    11. }    

    下面進行花的繪制,代碼如下:onDraw方法:

    1. int radius = RandomUtil.randomInt(minBloomRadius, maxBloomRadius);    
    2. int size = petals.size();    
    3. MyPoint point = new MyPoint(mBloomCenterPoint.x, mBloomCenterPoint.y);    
    4. for (int i = 0; i < size; i++) {    
    5.     PetalView petal = petals.get(i);    
    6.     if (petal != null) {    
    7.         petal.render(point, radius, canvas);    
    8.     }    
    9. }    

    PetalView.java:

    1. public class PetalView {    
    2.     private static final String TAG = "PetalView";    
    3.     
    4.     private float stretchA;//第一個控制點延長線倍數    
    5.     private float stretchB;//第二個控制點延長線倍數    
    6.     private float startAngle;//起始旋轉角,用于確定第一個端點    
    7.     private float angle;//兩條線之間夾角,由起始旋轉角和夾角可以確定第二個端點    
    8.     private int radius = 100;//花芯的半徑    
    9.     private Path path = new Path();//用于保存三次貝塞爾曲線    
    10.     private Paint paint = new Paint();    
    11.     
    12.     public PetalView(float stretchA, float stretchB, float startAngle, float angle) {    
    13.         this.stretchA = stretchA;    
    14.         this.stretchB = stretchB;    
    15.         this.startAngle = startAngle;    
    16.         this.angle = angle;    
    17.         paint.setColor(Color.RED);    
    18.     }    
    19.     
    20.     public void render(MyPoint p, int radius, Canvas canvas) {    
    21.         if (this.radius <= radius) {    
    22.             this.radius += 25;    
    23.         }    
    24.         draw(p, canvas);    
    25.     }    
    26.     
    27.     private void draw(MyPoint p, Canvas canvas) {    
    28.         path = new Path();    
    29.         //將向量(0,radius)旋轉起始角度,第一個控制點根據這個旋轉后的向量計算    
    30.         MyPoint t = new MyPoint(0, this.radius).rotate(RandomUtil.degrad(this.startAngle));    
    31.         //第一個端點,為了保證圓心不會隨著radius增大而變大這里固定為3    
    32.         MyPoint v1 = new MyPoint(0, 3).rotate(RandomUtil.degrad(this.startAngle));    
    33.         //第二個端點    
    34.         MyPoint v2 = t.clone().rotate(RandomUtil.degrad(this.angle));    
    35.         //延長線,分別確定兩個控制點    
    36.         MyPoint v3 = t.clone().mult(this.stretchA);    
    37.         MyPoint v4 = v2.clone().mult(this.stretchB);    
    38.         //由于圓心在p點,因此,每個點要加圓心坐標點    
    39.         v1.add(p);    
    40.         v2.add(p);    
    41.         v3.add(p);    
    42.         v4.add(p);    
    43.     
    44.         path.moveTo(v1.x, v1.y);    
    45.         //參數分別是:第一個控制點,第二個控制點,終點    
    46.         path.cubicTo(v3.x, v3.y, v4.x, v4.y, v2.x, v2.y);    
    47.         canvas.drawPath(path, paint);    
    48.     }    
    49. }    

    MyPoint.java:

    1. public class MyPoint {    
    2.     public int x;    
    3.     public int y;    
    4.     
    5.     public MyPoint() {    
    6.     }    
    7.     
    8.     public MyPoint(int x, int y) {    
    9.         this.x = x;    
    10.         this.y = y;    
    11.     }    
    12.     
    13.     //旋轉    
    14.     public MyPoint rotate(float theta) {    
    15.         int x = this.x;    
    16.         int y = this.y;    
    17.         this.x = (int) (Math.cos(theta) * x - Math.sin(theta) * y);    
    18.         this.y = (int) (Math.sin(theta) * x + Math.cos(theta) * y);    
    19.         return this;    
    20.     }    
    21.     
    22.     //乘以一個常數    
    23.     public MyPoint mult(float f) {    
    24.         this.x *= f;    
    25.         this.y *= f;    
    26.         return this;    
    27.     }    
    28.     
    29.     //復制    
    30.     public MyPoint clone() {    
    31.         return new MyPoint(this.x, this.y);    
    32.     }    
    33.     
    34.     //向量相減    
    35.     public MyPoint subtract(MyPoint p) {    
    36.         this.x -= p.x;    
    37.         this.y -= p.y;    
    38.         return this;    
    39.     }    
    40.     
    41.     //向量相加    
    42.     public MyPoint add(MyPoint p) {    
    43.         this.x += p.x;    
    44.         this.y += p.y;    
    45.         return this;    
    46.     }    
    47.     
    48.     public MyPoint set(int x, int y) {    
    49.         this.x = x;    
    50.         this.y = y;    
    51.         return this;    
    52.     }    
    53.     
    54.     @Override    
    55.     public String toString() {    
    56.         return "MyPoint{" +    
    57.                 "x=" + x +    
    58.                 ", y=" + y +    
    59.                 '}';    
    60.     }    
    61. }    

    github地址
    參考文章:http://www./demo/jiaoben1892/index.html

      本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
      轉藏 分享 獻花(0

      0條評論

      發表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 色窝窝无码一区二区三区成人网站| 韩国无码AV片在线观看网站| 亚洲最大成人网色| 久久AV无码精品人妻糸列| 国产午夜亚洲精品国产成人| 日产精品一卡2卡三卡四乱码| 日日摸夜夜添夜夜添高潮喷水| 综合色一色综合久久网| 亚洲AV中文无码乱人伦| 加勒比无码人妻东京热| 英语老师解开裙子坐我腿中间| 免费人成网站免费看视频| 丰满少妇人妻HD高清大乳在线| 亚洲国产精品日韩在线| 看全色黄大色大片免费久久| 视频二区中文字幕在线| 久久精品这里热有精品| 国产高清在线不卡一区| 亚洲AV永久无码精品天堂动漫| 亚洲人成网站77777在线观看| 少妇内射高潮福利炮| 国产在线不卡精品网站| 国产另类ts人妖一区二区| 国产精品爽爽VA在线观看无码 | 男女无遮挡XX00动态图120秒| 国产成AV人片久青草影院| 人人人妻人人澡人人爽欧美一区| 午夜福利高清在线观看| 国产精品福利自产拍在线观看| 高清破外女出血AV毛片| 色噜噜综合亚洲AV中文无码| 精品无码久久久久成人漫画| 一区二区亚洲人妻精品| 少妇高清精品毛片在线视频| 精品卡一卡二卡乱码高清| 韩国无码AV片午夜福利| 狠狠久久亚洲欧美专区| 久久精品这里热有精品| 97在线精品视频免费| 亚洲精品一区二区天堂| 免费又大粗又爽又黄少妇毛片|