什麼是貝塞爾曲線
貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用於二維圖形應用程序的數學曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節點組成,節點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。主要結構:起始點、終止點(也稱錨點)、控制點。通過調整控制點,貝塞爾曲線的形狀會發生變化。
展示:一階貝塞爾曲線(線段)
展示:二階貝塞爾曲線(拋物線)
展示:三階貝塞爾曲線
提供兩個網站幫助我們瞭解貝塞爾曲線:
http://www.html-js.com/article/1628
http://bezier.method.ac/#
Android提供的貝塞爾曲線代碼實現:
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
isSimplePath = false;
native_quadTo(mNativePath, x1, y1, x2, y2);
}
/**
* Add a cubic bezier from the last point, approaching control points
* (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
* made for this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the 1st control point on a cubic curve
* @param y1 The y-coordinate of the 1st control point on a cubic curve
* @param x2 The x-coordinate of the 2nd control point on a cubic curve
* @param y2 The y-coordinate of the 2nd control point on a cubic curve
* @param x3 The x-coordinate of the end point on a cubic curve
* @param y3 The y-coordinate of the end point on a cubic curve
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
isSimplePath = false;
native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
quadTo()方法從上一個點爲起點開始繪製貝塞爾曲線,其中(x1,y1)爲輔助控制點,(x2,y2)爲終點。
cubicTo()方法從上一個點爲起點開始繪製三階貝塞爾曲線,其中(x1,y1),( x2, y2 )爲輔助控制點,(x3,y3)爲終點。
舉個例子:
Path mPath = new Path();
mPath.moveTo(x0,y0);
mPath.quadTo(x1,y1,x2,y2);
如調用以上代碼,即繪製起點(x0,y0),終點(x2,y2),輔助控制點(x1,y1)的貝塞爾曲線。因此,通過不斷改變這三個點的位置,我們可以繪製出各種各樣的曲線。
貼一份二階貝塞爾曲線的測試代碼,幫助理解(將以下自定義View在佈局中引用即可看到效果):
基本思路:
- 1.先準備一支畫筆。
- 2.分別指定起點、終點、以及控制點的值。
- 3.爲了幫助理解,繪製上面3個點以及輔助線的位置。
- 4.繪製貝塞爾曲線。
- 5.簡單重寫滑動事件,讓手指可以改變控制點的位置,使貝塞爾曲線進行重繪,讓我們可以看到改變後的貝塞爾曲線。
package chao.base.view.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 二階曲線
*/
public class Bezier extends View {
private Paint mPaint;
private int centerX, centerY;
private PointF start, end, control;
public Bezier(Context context, 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);
}
public Bezier(Context context) {
this(context, null);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
// 初始化數據點和控制點的位置
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) {
// 根據觸摸位置更新控制點,並提示重繪
control.x = event.getX();
control.y = event.getY();
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 繪製數據點和控制點
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(20);
canvas.drawPoint(start.x, start.y, mPaint);
canvas.drawPoint(end.x, end.y, mPaint);
mPaint.setColor(Color.BLUE);
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);
}
}