贝塞尔曲线学习总结

前言

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

贝塞尔曲线是什么

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

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

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);
    }

效果:


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);
    }

效果:


Paste_Image.png

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

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

贝塞尔曲线公式。

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

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

Paste_Image.png

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

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

一阶贝塞尔公式

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

Paste_Image.png
二阶贝塞尔公式

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

Paste_Image.png
Paste_Image.png
三阶贝塞尔曲线

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

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

Paste_Image.png

0.gif

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

计算贝塞尔曲线的路径

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

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

    /**
     * 创建Bezier点集
     *
     * @return
     */
    private ArrayList<PointF> buildBezierPoints() {
        ArrayList<PointF> 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<PointF> 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);
        }
    }

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

Paste_Image.png

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容