Android⾃定义View(四)Path之贝塞尔曲线
⼀、概述
1、贝赛尔曲线来源
在数学的数值分析领域中,贝赛尔曲线(Bézier曲线)是电脑图形学中相当重要的参数曲线。更⾼维度的⼴泛化贝塞尔曲线就称作贝塞尔曲⾯,其中贝塞尔三⾓是⼀种特殊的实例
贝塞尔曲线于1962年,由法国⼯程师⽪埃尔·贝塞尔(Pierre Bézier)⼴泛发表,他运⽤贝塞尔曲线来为汽车的主体进⾏设计。贝塞尔曲线最初由Paul de Casteljau于1959年运⽤de Casteljau算法开发,以稳定数值的⽅法求出贝塞尔曲线
2、贝塞尔曲线公式
⼀阶贝塞尔曲线
公式:B(t)=(1 - t)Po + tP1,t∈[0,1]
动画演⽰为:
其中Po表⽰起点,P1表⽰终点,t表⽰时间,B(t)表⽰计算结果
图中移动的⿊⾊点代表随着时间的增长B(t)的变化规律,其实⼀阶贝塞尔曲线就是在起点和终点之间随着B的变化⽽形成⼀个匀速增长的轨迹,所以说B(t)也是⼀个匀速增长的值
⼆阶贝塞尔曲线
公式:B(t)=(1 - t)²Po + 2t(1 - t)P1 + t²P2,t∈[0,1]
在这⾥Po是起点,P2是终点,P1是控制点,我们给公式做⼀个分解因式的转换
B(t)= (1-t)[(1-t)Po + tP1] + t[(1-t)P1 + tP2]
通过分解因式,我们发现其实⼆阶曲线公式是⼀阶曲线公式的组合,⾸先各⾃取得(Po、P1)和(P1、P2)的⼀阶曲线值,然后再将两个值作为⼀阶曲线的起点和终点计算得到B(t)得到整个⼆阶曲线的值
动画演⽰:
可以看到⼆阶贝塞尔曲线会形成⼀个圆滑的曲线轨迹
下⾯取动画的其中⼀帧来分析⼀下,假设t = 0.25时刻,此时的画⾯如下:
其中点B为公式的计算结果,上⾯我们分析过B(t)的⽣成规律,这⾥我们直观地解析⼀下
⾸先Qo是Po和P1作为⼀阶曲线的⽣成结果,Q1是P1和P2作为⼀阶曲线的⽣成结果,然后Qo和Q1作为⼀阶曲线⽣成的B就是当前⼆阶曲线的最终结果值。随着时间的变化,就形成了上⾯的红⾊轨迹线了
三阶贝塞尔曲线
公式:B(t)= Po(1 - t)^3 + 3P1(1-t)² + 3P2 t²(1-t) + P3 t^3,t∈[0,1]
演⽰动画:
同样我们取t = 0.25时刻,如图
其实三阶曲线的形成规律和⼆阶曲线是⼀样的,这⾥就不再详细解析了,因为三阶曲线在我Android开发中也⽤得很少,当然还有四阶、五阶曲线等等。本篇我们主要讲解⼆阶曲线,下⾯要讲的例⼦也是基于⼆阶曲线实现的
⼆、Android中的贝塞尔曲线
在android的Path类中有四个与贝塞尔曲线相关的⽅法,如下
//⼆阶贝赛尔
public void quadTo(float x1, float y1, float x2, float y2)
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
//三阶贝赛尔
public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
三阶曲线的⽅法这⾥就不作分析了,来看看quadTo⽅法,有四个参数,其中x1和y1是指控制点的坐标,x2和y2是终点的坐标,那起点的坐标在哪⾥呢?其实起点的坐标就是上⼀个曲线的终点坐标,或者Path开始的moveTo⽅法的坐标,如果没有指定起点坐标,那么就会默认为原点了。⾄于rQuadTo⽅法我们后⾯再分析
1.quadTo的使⽤及⾃定义⼿指轨迹画板
下⾯我们⾃定义⼀个可以⽤⼿指画轨迹的画板,代码如下:
public class TrackView extends View {
private Path mPath = new Path();
public TrackView(Context context) {
super(context);
}
public TrackView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (Action()) {
case MotionEvent.ACTION_DOWN: {
return true;
}
case MotionEvent.ACTION_MOVE:
mPath.X(), Y());
invalidate();
break;
default:
break;
}
TouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(mPath, paint);
}
public void reset() {
invalidate();
}
}
很简单,就是根据⼿势轨迹⽣成的⼀系列点⽣成⼀个Path,然后画出来,来看看效果
放⼤了图⽚,可以看到明显的纹路,这是因为在⼿指滑动⾛过的时候,不可能平滑地过渡,坐标之间变化会⽐较剧烈,所以整条线段就会形成很多⽐较明显的转折。好了,到这⾥我们是不是⾃然就想到了可以⽤⼆阶贝塞尔曲线来实现线段的转折呢,从⽽实现线段之间的平滑过渡
来看看⼆阶贝塞尔曲线是怎么实现两个线段的平滑过渡的?下⾯这个图
上⾯假设有三个⼿指触点,然后形成了两条转折的线段,然后把线段AB的中点P0作为曲线的起点,点B作为曲线的控制点,线段BC的中点P1作为曲线的终点,这样就形成了⼀个⽣成⼆阶贝塞尔曲线的条
件了。为什么要取AB和BC的中点作为曲线的起点和终点,⽽不是A和C 呢?因为这个曲线的终点就是下⼀个曲线的起点,⽐如P1会成为下⼀个曲线的起点,⽽点C明显不可能成为下⼀个曲线的起点,所以取线段的中间点作为起点和终点是最合适的
接下来改造⼀下TrackView,如下canvas动画
public class TrackView extends View {
private Path mPath = new Path();
private float mPreX,mPreY;
public TrackView(Context context) {
super(context);
}
public TrackView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (Action()){
case MotionEvent.ACTION_DOWN:{
mPreX = X();
mPreY = Y();
return true;
}
case MotionEvent.ACTION_MOVE:{
float endX = (X())/2;
float endY = (Y())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = X();
mPreY =Y();
invalidate();
}
break;
default:
break;
}
TouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(2);
canvas.drawPath(mPath,paint);
}
public void reset() {
invalidate();
}
}
⽤mPreX和mPreY来记录上⼀个曲线的终点,然后成为下⼀个曲线的起点,我们来看看画图效果

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。