
一、贝济埃曲线
概述:
在Path系列函数中,除了一些基本的设置和绘图用法外,还有一个强大的工具——贝济埃曲线。它能将利用moveTo、lineTo连续的生硬路径变得平滑,也能够实现很多炫酷的效果,比如水波纹等。
1.贝济埃曲线的来源
1962年法国工程师皮埃尔·贝济埃发表,他运用贝济埃曲线来为汽车的主体进行设计。
在数学的数值分析领域中,贝济埃曲线是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝济埃曲线就称作贝济埃曲面,其中贝济埃三角是一种特殊的实例。
2.贝济埃曲线的公式
1)一阶贝济埃曲线
公式如下:
为起始点,
为终点,
表示当前时间,
表示公式的结果值。
注意:曲线的含义就是随着时间的变化,公式的结果值所形成的轨迹。
在下面动画中,黑色点表示在当前时间下公式的
的取值;而红色的那条线就表示在各个时间点下不同取值的
所形成的轨迹。

总而言之,对于一阶贝济埃曲线,可以理解为在由起始点和终点形成的这条直线上匀速移动的点。
2)二阶贝济埃曲线
是起始点,
是终点,
是控制点。


形成了一条一阶贝济埃曲线,
在这条直线上随时间匀速运动。
也形成了一条一阶贝济埃曲线,
在这条直线上随时间匀速运动。
动态点和
又形成了一条一阶贝济埃曲线,在这条曲线上动态移动的点是
,而
点的移动轨迹就是二阶贝济埃曲线的最终形态。
从上面可知,之所以称为二阶贝济埃曲线,是因为点的移动轨迹是建立在两条一阶贝济埃曲线的中间点
和
的基础上的。
3)三阶贝济埃曲线


同样是起始点,
是终点,
是第一个控件点,
是第二个控件点。
这里有三条一阶贝济埃曲线,分别是、
、
,它们随时间变化的点分别为
、
、
,然后这三点相连形成两条一阶贝济埃曲线,分别是
、
,它们随时间变化的点为
、
。同样,
和
可以连接形成一条一阶贝济埃曲线,在
这条贝济埃曲线上随时间移动的点是
,而
点的移动轨迹就是三阶贝济埃曲线的最终形状。
从上面可看出,所谓几阶贝济埃曲线,全部是由一条条一阶贝济埃曲线搭起来的。在上图中,、
、
、
形成一阶贝济埃曲线,
、
、
形成二阶贝济埃曲线,
、
形成三阶贝济埃曲线。
4)四阶贝济埃曲线

5)五阶贝济埃曲线

对于四阶和五阶贝济埃曲线,在Android中是用不到的,Path最多支持到三阶贝济埃曲线,所以对它们的形成原理也不再介绍了。
3.贝济埃曲线与Photoshop钢笔工具
在绘图工具Photoshop中的钢笔工具,它所使用的路径弯曲效果就是二阶贝济埃曲线。

贝济埃曲线之quadTo:
在Path类中有4个函数与贝济埃曲线相关,分别如下:
// 二阶贝济埃曲线
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)
1.quadTo使用原理
public void quadTo(float x1, float y1, float x2, float y2)
● (x1,y1)是控制点坐标
● (x2,y2)是终点坐标
对于起始点,如果没有调用Path.moveTo(x,y)指定,默认以控件左上角点(0,0)为起始点。
尝试画出一条波浪线。

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path path = new Path();
path.moveTo(100, 300);
path.quadTo(200, 200, 300, 300);
path.quadTo(400, 400, 500, 300);
canvas.drawPath(path, paint);
}
需要注意的是,第一个起始点是需要调用path.moveTo(l00,300)函数来指定的,后一个path.quadTo()函数是以前一个path.quadTo()函数的终点为起始点的。所以,一般在使用贝济埃曲线寻找控制点时,如果没有思路,则可以尝试使用Photoshop的钢笔工具画图分析。而且在自定义控件的时候,如果UED(视觉设计人员)在实现一个效果时使用的是钢笔工具,那么在代码中就可以尝试使用贝济埃曲线来实现。
2.示例:传统捕捉手势轨迹
只需在自定义控件中拦截OnTouchEvent,然后根据手指的移动轨迹来绘制Path即可。
public class NormalGestureTrackView extends View {
private Path mPath = new Path();
private Paint mPaint;
public NormalGestureTrackView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
}
...
}
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);
}
这里有两点要注意:
第一,case MotionEvent.ACTION_DOWN时返回true的问题。返回true表示当前控件已经消费了下按动作,之后的ACTION_MOVE、ACTION_UP动作也会继续传递到当前控件中;如果case MotionEvent.ACTION_DOWN时返回false,那么后续ACTION_MOVE、ACTION_UP动作就不会再传递到这个控件

本文深入探讨了贝济埃曲线在图形绘制中的应用,包括曲线原理、Path类的贝济埃曲线函数、优化手势绘制,以及阴影、发光效果和Shader的使用技巧。
最低0.47元/天 解锁文章
724

被折叠的 条评论
为什么被折叠?



