爱的贝塞尔曲线讲解

来源:互联网 发布:abb仿真软件track 编辑:程序博客网 时间:2024/05/18 22:56

1.贝塞尔曲线介绍

贝塞尔曲线的数学基础是早在 1912 年就广为人知的伯恩斯坦多项式。但直到 1959 年,当时就职于雪铁龙的法国数学家 Paul de Casteljau 才开始对它进行图形化应用的尝试,并提出了一种数值稳定的 de Casteljau 算法。然而贝塞尔曲线的得名,却是由于 1962 年另一位就职于雷诺的法国工程师 Pierre Bézier 的广泛宣传。他使用这种只需要很少的控制点就能够生成复杂平滑曲线的方法,来辅助汽车车体的工业设计。正是因为控制简便却具有极强的描述能力,贝塞尔曲线在工业设计领域迅速得到了广泛的应用。不仅如此,在计算机图形学领域,尤其是矢量图形学,贝塞尔曲线也占有重要的地位。

您或许不知道在生活中已经用过贝塞尔曲线,我举个例子您就恍然大悟,Photoshop中的钢笔工具就是用了贝塞尔曲线的原理.在android中,我们利用贝塞尔曲线能做出很炫酷的效果,如下图:
这里写图片描述
是不是很好看,下一章我们就做这么一个效果来练习一下!

2.贝塞尔曲线原理

接下来我们介绍一下贝塞尔曲线的原理:

2.1 贝塞尔二阶曲线

1.在平面内任选 3 个不共线的点,依次用线段连接。如下图:
这里写图片描述
2.在第一条线段上任选一个点 D。计算该点到线段起点的距离 AD,与该线段总长 AB 的比例。如下图:
这里写图片描述
3.根据上一步得到的比例,从第二条线段上找出对应的点 E,使得 AD:AB = BE:BC。如下图:
这里写图片描述
4.连接这两点 DE。如下图:
这里写图片描述
5.从新的线段 DE 上再次找出相同比例的点 F,使得 DF:DE = AD:AB = BE:BC。如下图:
这里写图片描述
到这里,我们就确定了贝塞尔曲线上的一个点 F。接下来,请稍微回想一下中学所学的极限知识,让选取的点 D 在第一条线段上从起点 A 移动到终点 B,找出所有的贝塞尔曲线上的点 F。所有的点找出来之后,我们也得到了这条贝塞尔曲线。如下图:
这里写图片描述
6.如果你实在想象不出这个过程,没关系,看动画!
这里写图片描述
回过头来看这条贝塞尔曲线,为了确定曲线上的一个点,需要进行两轮取点的操作,因此我们称得到的贝塞尔曲线为二次曲线。计算公式如下:
这里写图片描述

2.2 贝塞尔三阶曲线

当控制点个数为 4 时,情况是怎样的?
这里写图片描述
步骤都是相同的,只不过我们每确定一个贝塞尔曲线上的点,要进行三轮取点操作。如图,AE:AB = BF:BC = CG:CD = EH:EF = FI:FG = HJ:HI,其中点 J 就是最终得到的贝塞尔曲线上的一个点。
这里写图片描述
这样我们得到的是一条三次贝塞尔曲线。如下图:
这里写图片描述
三阶贝塞尔曲线的公式如下:
这里写图片描述

2.3 贝塞尔一阶曲线

看过了二次和三次曲线,更高次的贝塞尔曲线大家应该也知道要怎么画了吧。那么比二次曲线更简单的一次(线性)贝塞尔曲线存在吗?长什么样?根据前面的介绍,只要稍作思考,想必你也能猜出来了。哈!就是一条直线~
这里写图片描述

2.4 更复杂的贝塞尔曲线

更复杂的贝塞尔曲线原理是一样的,来看看更复杂的贝塞尔曲线,如下图:
这里写图片描述
好了原理篇就讲到这里吧,大家可以点击进入这个网站去加深一下理解。

3.android贝塞尔曲线简单实现

我们android也支持贝塞尔曲线,只不过sdk最多只支持3阶的。要想支持更多阶得找第三方的了。在android中我们看一下有那些用贝塞尔曲线实现的效果:

1.QQ小红点拖拽效果2.一些炫酷的下拉刷新控件3.阅读软件的翻书效果4.一些平滑的折线图的制作5.其他很多炫酷的动画效果

下面我们讲解一下如何用android实现2阶和3阶贝塞尔曲线。
画贝塞尔曲线我们需要用到的类除了cavans和paint,最重要是是path类,利用path的二个方法:

quadTo()//画二阶贝塞尔曲线cubicTo()//画三阶贝塞尔曲线

3.1 画2阶贝塞尔曲线代码

Path path = new Path();path.moveTo(start.x,start.y);path.quadTo(control.x,control.y,end.x,end.y);

由上面我们知道2阶贝塞尔曲线需要有3个点:

start.x,start.y是起始点的(x,y)坐标control.x,control.y是中间控制点的(x,y)坐标end.x,end.y是结束点的(x,y)坐标#3.2 画3阶贝塞尔曲线代码:

3.2 画3阶贝塞尔曲线代码

 Path path = new Path(); path.moveTo(start.x, start.y); path.cubicTo(control1.x, control1.y, control2.x,control2.y, end.x, end.y); canvas.drawPath(path, mPaint);

由上面我们知道3阶贝塞尔曲线需要有4个点,所以在cubicTo方法只需要2个控制点和一个结束点。

4.讲解一个案例

我们来做一个小小的例子练习一下,以2阶的贝塞尔曲线为例,效果图如下:
这里写图片描述
代码如下:

public class MyLayout extends View {    private Point startPoint;//开始点    private Point endPoint;//结束点    private Point controlPoint;//随意变动的控制点    private Paint mPaint;    public MyLayout(Context context, AttributeSet attrs) {        super(context, attrs);        initPaint();    }    //初始化paint,没什么可说的。    private void initPaint(){        mPaint = new Paint();        mPaint.setStyle(Paint.Style.STROKE);    }    /**     * onlayout中定死2个起始点。     */    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        int padding  = getWidth()/6;        startPoint = new Point(padding,getHeight()/2);        endPoint = new Point(getWidth() - padding , getHeight()/2);        controlPoint = new Point(getWidth()/2,getHeight()/2);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        drawPoint(canvas);//画点        drawHelpLine( canvas);//画辅助线        drawBezierLine(canvas);//画贝塞尔曲线    }    /**     * 绘制贝塞尔曲线     */    private void drawBezierLine(Canvas canvas){        mPaint.setColor(Color.RED);        mPaint.setStrokeWidth(8);        Path path = new Path();        path.moveTo(startPoint.x, startPoint.y);        path.quadTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);        canvas.drawPath(path, mPaint);    }    /**     *  绘制辅助线     */    private void drawHelpLine(Canvas canvas){        mPaint.setColor(Color.LTGRAY);        mPaint.setStrokeWidth(4);        canvas.drawLine(startPoint.x,startPoint.y,controlPoint.x,controlPoint.y,mPaint);        canvas.drawLine(endPoint.x,endPoint.y,controlPoint.x,controlPoint.y,mPaint);    }    /**     * 画起始点和控制点     */    private void  drawPoint(Canvas canvas){        // 绘制数据点和控制点        mPaint.setColor(Color.GRAY);        mPaint.setStrokeWidth(20);        canvas.drawPoint(startPoint.x,startPoint.y,mPaint);        canvas.drawPoint(endPoint.x,endPoint.y,mPaint);        canvas.drawPoint(controlPoint.x,controlPoint.y,mPaint);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // 根据触摸位置更新控制点,并提示重绘        controlPoint.x = (int) event.getX();        controlPoint.y = (int) event.getY();        invalidate();        return true;    }}

这个代码挺简单的,我就不做详细介绍了,大家再想一下,把上面的2阶变成3阶怎么办,其实也很简单,再加一个控制点就行,代码如下:

  // 绘制贝塞尔曲线        mPaint.setColor(Color.RED);        mPaint.setStrokeWidth(8);        Path path = new Path();        path.moveTo(start.x, start.y);        path.cubicTo(control1.x, control1.y, control2.x,control2.y, end.x, end.y);        canvas.drawPath(path, mPaint);

效果图如下:
这里写图片描述

5 补充Path类的API

这里对path类中的方法补充一下。

作用 相关方法 备注 移动起点 moveTo 移动下一次操作的起点位置 设置终点 setLastPoint 重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同 连接直线 lineTo 添加上一个点到当前点之间的直线到Path 闭合路径 close 连接第一个点连接到最后一个点,形成一个闭合区域 添加内容 addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo 添加(矩形, 圆角矩形, 椭圆, 圆, 路径, 圆弧) 到当前Path (注意addArc和arcTo的区别) 是否为空 isEmpty 判断Path是否为空 是否为矩形 isRect 判断path是否是一个矩形 替换路径 set 用新的路径替换到当前路径所有内容 偏移路径 offset 对当前路径之前的操作进行偏移(不会影响之后的操作) 贝塞尔曲线 quadTo, cubicTo 分别为二次和三次贝塞尔曲线的方法 rXxx方法 rMoveTo, rLineTo, rQuadTo, rCubicTo 不带r的方法是基于原点的坐标系(偏移量),rXxx方法是基于当前点坐标系(偏移量) 填充模式 setFillType, getFillType, isInverseFillType, toggleInverseFillType 设置,获取,判断和切换填充模式 提示方法 incReserve 提示Path还有多少个点等待加入(这个方法貌似会让Path优化存储结构) 布尔操作(API19) op 对两个Path进行布尔运算(即取交集、并集等操作) 计算边界 computeBounds 计算Path的边界 重置路径 reset, rewind 清除Path中的内容(reset相当于重置到new Path阶段,rewind会保留Path的数据结构) 矩阵操作 transform 矩阵变换

6.结尾

好了就讲到这里吧,下一章我们做一个文章开头的效果。
在技术上我依旧是个小渣渣,加油,勉励自己!

7.参考文档

【1】Android 贝塞尔曲线的浅析,android贝塞尔浅析
【2】玩玩这个网站加深对贝塞尔曲线的理解
【3】贝塞尔曲线扫盲
【4】安卓自定义View进阶 - 贝塞尔曲线

2 0