一、贝塞尔曲线的数学理论
贝赛尔曲线的本质是通过数学计算公式去绘制平滑的曲线。
下面以二阶贝塞尔曲线说明:
步骤一:在平面内选3个不同线的点并且依次用线段连接。
步骤二:在AB和BC线段上找出点D和点E,使得 AD/AB = BE/BC
步骤三:连接DE,在DE上寻找点F,F点需要满足:DF/DE = AD/AB = BE/BC
步骤四:根据DE线段和计算公式找出所有的F点,然后将其这些点连接起来。
对应的公式为:B(t)=(1-t)2P0+2t(1-t)P1+t2P2,t属于[0,1]
以此类推:三阶贝塞尔曲线如下
二、android贝塞尔曲线相关方法说明
在Path类中四个方法与贝塞尔曲线相关。
//二阶贝赛尔
public void quadTo(float x1, float y1, float x2, float y2)
参数中(x1,y1)是控制点坐标,(x2,y2)是终点坐标,
贝塞尔曲线的起始点是通过Path.moveTo(x,y)来指定的,而如果我们连续调用quadTo(),前一个quadTo()的终点,就是下一个quadTo()函数的起点;如果初始没有调用Path.moveTo(x,y)来指定起始点,则默认以控件左上角(0,0)为起始点;
一般控制点和终点坐标都是设计定义的,开发中根据设计师给的点推导出起点,终点,控制点。
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;
dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;
dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;
dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;
例如:起点为(300,300)控制点为(100,500),终点为(700,300)
path.moveTo(300,300)
Path.quadTo(100,500,700,300)
Path.rQuadTo(100-300,500-300,700-300,300-300)
Path.rQuadTo(-200,200,400,0)
//三阶贝赛尔
3.public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
参数中(x1,y1)是控制点1的坐标,(x2,y2)是控制点2的坐标,(x3,y3)是结束点的坐标。
4.public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
参数中(x1,y1)是控制点1相对起点的坐标,(x2,y2)是控制点2相对起点的坐标,(x3,y3)是结束点相对起点的坐标。
三、应用
Android中手指轨迹的优化。
优化前,手指轨迹是通过连接移动两点之间的距离组成一条线。对一些拐弯的处理,有明显的连接痕迹。不够平滑。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN: {
mPath.moveTo(event.getX(), event.getY());
return true;
}
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
postInvalidate();
break;
default:
break;
}
return super.onTouchEvent(event);
}
优化处理,以滑动的AB连线的中间点为贝塞尔起点,B点为控制点,BC连线的中间点为贝塞尔结束点,画一条贝塞尔曲线。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
}
case MotionEvent.ACTION_MOVE:{
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
invalidate();
}
break;
default:
break;
}
return super.onTouchEvent(event);
}