贝塞尔曲线学习总结

前言

贝塞尔曲线是一个强大而又神秘的东西。今天有时间彻底梳理了一下,神秘的面纱被揭下来了,剩下了只有强大。下面就一步一步来了解一下贝塞尔曲线。

贝塞尔曲线是什么

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。

上面是网上找的定义,算了还是看图体会吧。

贝塞尔曲线学习总结_第1张图片
0.gif

看了这图后对贝塞尔曲线差不多就有点数了。

Android中实现贝塞尔曲线的API

了解自定义View的小伙伴,肯定知道 canvas.drawPath(),这个方法。该方法接受的第一个对象是Path,这个Path,提供了两个方法:quadTo()、cubicTo(),分别实现二阶贝塞尔曲线、三阶贝塞尔曲线。接下来我们就写代码来看看效果。

quadTo()的使用

代码:

   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Path path = new Path();
        path.moveTo(50, 200);
        path.quadTo(150, 50, 400, 200);
        canvas.drawPath(path, paint);
    }

效果:


贝塞尔曲线学习总结_第2张图片
Paste_Image.png

标注有点简陋,聪明的你,应该看得懂的哈。

cubicTo()的使用

代码:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Path path = new Path();
        path.moveTo(50, 200);
        path.cubicTo(100, 50, 350, 50, 500, 200);
        canvas.drawPath(path, paint);
    }

效果:


贝塞尔曲线学习总结_第3张图片
Paste_Image.png

以上就是用Android API实现二阶贝塞尔曲线和三阶贝塞尔取消,是不是很简单呢。

作为一名有梦想的程序员,你一定不会满足的。接下来就研究一下贝塞尔曲线的原理。

贝塞尔曲线公式。

1.贝塞尔曲线的通用公式
Paste_Image.png

在网上查资料得到这个公式,这个公式是贝塞尔曲线的通用公式,通过这个公式可以得到一阶、二阶、三阶。。。。N阶贝塞尔曲线。
第一眼看到这个公式,说实话我是一脸懵逼,心想这好像是微积分吧,当年的高数都是睡过来的。于是求助了在上研究生的同学。
于是得到了下面的图片:

贝塞尔曲线学习总结_第4张图片
Paste_Image.png

好吧,还是有点懵逼,主要是字太丑。

不管推导过程了,直接把结果拿来用吧。

一阶贝塞尔公式

给定p0、p1两个点。得到如下公式

Paste_Image.png
二阶贝塞尔公式

二阶贝塞尔曲线可以理解为两个一阶贝塞尔曲线的连线上,动态的划出一阶贝塞尔曲线。

贝塞尔曲线学习总结_第5张图片
Paste_Image.png
Paste_Image.png
三阶贝塞尔曲线

按照二阶的推导原理,以此类推,由四个控制点定义的三阶贝塞尔曲线P03可被定义为分别由(P0,P1,P2)和(P1,P2,P3)确定的两条二阶贝塞尔曲线的线性组合:P03 = (1-t)P02 + tP12

可递归代入得出三阶贝塞尔曲线公式:

Paste_Image.png

贝塞尔曲线学习总结_第6张图片
0.gif

贝塞尔曲线原理部分借鉴于:http://mp.weixin.qq.com/s/l0ABkX3IzBk_s8-nWkUJig
在此对大神表示感谢。

计算贝塞尔曲线的路径

通过上面的学习,我们可以很轻松的画出二阶、三阶贝塞尔曲线。那么,我们想画出高阶的贝塞尔曲线呢?我们只是需要贝塞尔曲线的路径而不只是画出细线呢?
那么我们就需要得到贝塞尔曲线的绘制过程,也就是说要得到贝塞尔曲线上的每一个点。

这里不多将直接上代码来,看效果好了。
核心代码,计算贝塞尔曲线上的点集:
借鉴于https://github.com/venshine/BezierMaker
这是真大神,给跪了。

    /**
     * 创建Bezier点集
     *
     * @return
     */
    private ArrayList buildBezierPoints() {
        ArrayList points = new ArrayList<>();
        int order = mControlPoints.size() - 1;
        float delta = 1.0f / FRAME;
        for (float t = 0; t <= 1; t += delta) {
            // Bezier点集
            points.add(new PointF(deCasteljauX(order, 0, t), deCasteljauY(order, 0, t)));
        }
        return points;
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private float deCasteljauX(int i, int j, float t) {
        if (i == 1) {
            return (1 - t) * mControlPoints.get(j).x + t * mControlPoints.get(j + 1).x;
        }
        return (1 - t) * deCasteljauX(i - 1, j, t) + t * deCasteljauX(i - 1, j + 1, t);
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private float deCasteljauY(int i, int j, float t) {
        if (i == 1) {
            return (1 - t) * mControlPoints.get(j).y + t * mControlPoints.get(j + 1).y;
        }
        return (1 - t) * deCasteljauY(i - 1, j, t) + t * deCasteljauY(i - 1, j + 1, t);
    }

初始化控制点:

void init() {
        mControlPoints = new ArrayList<>(MAX_COUNT + 1);
        int w = getResources().getDisplayMetrics().widthPixels;
        mControlPoints.add(new PointF(144, 144));
        mControlPoints.add(new PointF(266, 51));
        mControlPoints.add(new PointF(500, 83));
        mControlPoints.add(new PointF(650, 212));
        mControlPoints.add(new PointF(689, 491));
        mControlPoints.add(new PointF(485, 747));
        mControlPoints.add(new PointF(185, 577));
        mControlPoints.add(new PointF(45, 180));
        mControlPoints.add(new PointF(48, 747));
        mControlPoints.add(new PointF(689, 747));
//在这里初始化控制点的集合,可以任意添加控制点,从而得到N阶贝塞尔曲线。

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(getResources().getColor(R.color.colorAccent));
    }

在onDraw方法中调用:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Path path = new Path();
        ArrayList list = buildBezierPoints();
        for (int i = 1; i < list.size(); i++) {
            path.moveTo(list.get(i - 1).x, list.get(i - 1).y);
            path.lineTo(list.get(i).x, list.get(i).y);
            canvas.drawPath(path, paint);
        }
    }

好了看下效果,由于控制点是随便添加的,所以就是这样的鬼样子:

贝塞尔曲线学习总结_第7张图片
Paste_Image.png

看到这里大家觉得没什么了不起的。大家可以发挥想象力。
1.坐标规划的够好,就可以画出各种各样的图案,画个汽车不成问题。
2.得到贝塞尔曲线的点集后,我们不一次性画出来,而是延时的画,那么就可以让线条动起来,那么就是动画了
3.得到贝塞尔曲线后,我们不划线,而是在这个路径上画图案,那么就可以实现,图案在这路径上飘动。就想直播里面点赞出现的动画一样。

你可能感兴趣的:(贝塞尔曲线学习总结)