android高级UI之贝塞尔曲线<上>---基本概念、德卡斯特里奥算法

2021年09月15日 阅读数:3
这篇文章主要向大家介绍android高级UI之贝塞尔曲线<上>---基本概念、德卡斯特里奥算法,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

在上一次https://www.cnblogs.com/webor2006/p/12712076.html对于Android UI绘制中核心的Canvas进行了相关的学习,这块的学习也中断一年多了,既然主业是Android开发,没有任何理由能中止对它的继续学习探索,因此接下来对于Android UI的学习继续。html

贝塞尔曲线基本概念:

目标:

对于贝塞尔曲线的绘制其实在以前https://www.cnblogs.com/webor2006/p/7726174.html作QQ汽泡效果时已经用到过了,日常不自定义一些特殊的效果可能不必定能用到,可是!!!若是你不了解它的绘制,可能在须要它的时候你会很抓狂,由于它的使用也不是那么容易,我还记得早些年在一家公司叫我作一个电池充电过程当中的水波纹效果就死活没有搞定,最后是让组里的一位大神帮忙解决了,当时的动画需求自己就比较复杂,网上也没有相似能知足要求的开源效果,我也就是对于贝塞尔曲线的绘制不了解致使作不出来,当时真的就是“书到用时方恨少”感觉,因此接下来再来对它进行一个详细的了解,看成一个复习+巩固吧,最后仍是以QQ汽泡的效果作为一个实战练习,效果以下:java

整个效果用过手机版的QQ应该都比较熟悉了,不过这是没有集成到列表当中,对于列表当中如何集成也请移步到https://www.cnblogs.com/webor2006/p/7787511.html进行观赏。android

那我们此次主要学习的是怎么画“贝塞尔曲线”对吧,那对应这个QQ汽泡来讲,哪里用到了呢?这里:git

看到中间的上下两个曲线了吧,它们用到了,固然这是一个二阶贝塞尔【啥叫二阶贝塞尔呢?下面会学习到】,也是最最基础的,了解了基础的画法,对于它更加复杂的画法你也比较容易上手了,好,这是一个总体的目标。github

整个学习会参考到这位大佬的简书:https://www.jianshu.com/p/95513310ff4d,不过我这边会“去其糟粕,取其精华”,最终目的就是吸取成本身的。web

概念了解:

首先要明白,对于贝塞尔曲线,它是分阶的,不一样的阶数,其绘制的曲线效果也不同,因此这里先来了解不一样阶数的贝塞尔曲线的绘制状况。算法

一阶贝塞尔曲线:

先看一下效果:canvas

一条直线。。不是贝塞尔是用来绘制曲线的么?是的,由于对于一阶贝塞尔实际没啥用,可是对于你了解它的概念是一个基础,等于它的核心目的其实就只是由两点控制的一条直线,绘制就是从一点绘制到另外一点的整个轨迹,记住这个绘制过程,由于在理解二阶贝塞尔曲线的过程当中,须要使用到它。api

二阶贝塞尔曲线【重点】:

绘制过程:

对于二阶贝塞尔曲线来讲,应该是应用最最普遍的,也是核心中的核心,对于贝塞尔曲线不是还有更加高阶复杂的曲线么?而要理解更加复杂的贝塞尔曲线,理解二阶就成为一个必需要掌握的了,只有掌握了二阶的画法,你才能更加从容的面对更加复杂的贝塞尔曲线,因此这块务必要理解透。dom

先来直观的感觉一下它的绘制过程:

嗯,此图来自于csdn,看完此效果以后是否是以为挺酷炫的?而在上面QQ汽泡效果中的那个曲线,是否是跟这个二阶曲线能联系起来?这也就是为啥在以后的贝塞尔曲线的案例应用时QQ汽泡效果使用的是二阶贝塞尔的缘由了,可是你能看懂它的绘制规则么?反正我光看这动图仍是一脸懵逼的,因此接下来会了解其原理,这样才能真正理解它。

绘制原理:

先明显对于二阶贝塞尔曲线是由三个点来进行构成的,以下:

其中AC这两点原本是应该绘制一条直线的对吧:

可是该直线须要受B点往下进行拖动,这个B其实就是一个控制点,正由于有这个控制点才会让本应该绘直线的最终变成了绘制曲线了,其实在咱们电脑的绘制软件中也能直观的感觉一下这个B点的控制点的魅力,好比我mac上用Paintbrush这个软件有一个曲线的绘制:

 

我要绘制一条曲线,也是先绘制一条直线,而后再经过对直线的拖拽才完成曲线的绘制的,以下:

因此,此时应该对于B点它的出现的意义有了直观的了解了吧,接下来重点是就来看一下这个曲线的绘制规则了,这里先把两点到控制点链接两条直线,以下:

而后曲线的总体绘制是由AB这条一阶贝塞尔曲线来控制:

那AC的这根曲线很显然也是由一大堆的点的连续构成的对吧,让曲线上的绘制点又是如何来肯定的呢?如今整个曲线的控制是由AB这个贝塞尔曲线来控制,它是一个运动的轨迹【这一点必需要理解到位,它是不断在走的,不明白的能够看一下一阶贝塞尔曲线的那个动图】,也就是AB上走了多少个点,就会对应的生成曲线上的各个点,这里以AB上的某一个静止点进行分析,把它如何肯定最终曲线上的绘制点的搞清楚了,你也就明白了整个曲线的绘制原理了,好比AB上的贝塞尔曲线走到D这个点了:

其中这条直线上的AD跟DB就有一个比例关系了对吧,这时在BC上也取一个点E,其比例跟D点比例同样【注意关键词,比例要同样】,以下:

好,此时将DE进行一个连线:

接下来的绘制点就是在DE上了,那。DE上对应的绘制点是如何肯定的呢?也是一样的套路,在这条直线上取一点F,要保证比例跟AD的同样,以下:

此时F点就是最终要绘制在曲线上的一个点【注意它只是曲线上的一个点哟】,以下:

曲线上的某个点咱们已经知道怎么算出来了,咱们由A-C开始启动绘制,则会算出曲线上的N个点,用PATH记录这个点进行绘制,从而y就获得了一条曲线,这条曲线就是所谓的贝塞尔曲线。这里再来回忆一下整个二阶贝塞尔曲线完整的绘制过程:

其中t表示整个曲线的点的次数,盯着那根绿色的那根线看,是否是能感觉到比例正好跟P0到P1的一阶贝塞尔走的位置比例同样,截一个静态图说明一下:

计算公式:

其实对于二阶贝塞尔曲线在百度上能搜到相关的公式:

可是我看不太懂,其实有一个比较容易理解的计算公式,仍是以这个图为例:

想要找到绘制点的话只须要遵照DF:DE= AD:AB= BE:BC,那么此时F点就是绘制点,由于比例是同样的。

三阶贝塞尔曲线:

绘制过程:

绘制原理:

其原理跟二阶的同样,看一下图:

从上图咱们能够看到三阶比二阶多了一条线段,实际上表示的是最终开始点在A结束在B
中途会往C的方法有必定的移动,而后最终到D结束,计算方式有必定的区别,先由AB BC计算出一条线,在由BC CD 计算出第二条线构建一个二阶的贝塞尔,而后进行绘制,最终绘制的点是J ,一样知足这样的公式:AE:AB= BF:BC= CG:CD= EH:EF= FI:FG= HJ:HI。

更多阶贝塞尔曲线:

了解了1、2、三阶贝塞尔曲线以后,接着再来感觉一下更多阶的效果,让你瞬间晕眩:

一、四阶贝塞尔曲线:

二、五阶贝塞尔曲线:

生成工具:

若是想看更多阶的效果,这里推荐一个针对贝塞尔曲线学习开源的一个代码:https://github.com/qingguoguo/BezierMaker,最多支持七阶,我们把它下下来,看一下七阶的效果:

这对于学习贝塞尔曲线来讲是一个不错的辅助工具,有兴趣的能够下下来跑一跑。

贝塞尔曲线的基本画法:

接下来我们本身来体验画一画贝塞尔曲线吧,为以后的QQ消息气泡的实现打打基础。

二阶贝塞尔曲线:

对于Android的Path类中自己就已经提供有2、三阶曲线的API了,具体能够参考https://www.jianshu.com/p/12fcc3fedbbc:

而我在Android7.0的源码中貌似对于三创的API是这个方法名:

应该是有版本差别,这里就不过多纠结了,反正系统支持最可能是3阶。 

这里以绘制二阶为例,来体验一下画法。

一、新建工程:

这里仍是基于原来学习UI时搭建的工程新建一个module:

二、新建View:

接下来新建一个自定义View,为曲线的绘制搭好环境:

三、实现绘制逻辑:

一、定义控制点:

也就是我手指移动的位置就是一个控制点。

二、定义开始点和结束点:

对于三阶的贝塞尔曲线,除了控制点以外,还须要有一个开始点和结束点,这里定死一下:

三、定义画笔:

四、开始绘制:

这里直接使用path.quadTo()的api既能够绘制一个二阶贝塞尔曲线,以下:

整个代码以下:

package com.cexo.beziermaker.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 绘制贝塞尔曲线
 */
public class BezierView1 extends View {

    //曲线开始点
    private float startX, startY;
    //结束点
    private float endX, endY;
    //控制点
    private float contorlX = 200, contorlY = 60;//默认值
    private Paint paint;
    private Path path;

    public BezierView1(Context context) {
        this(context, null);

        startX = 160;
        startY = 350;
        endX = 550;
        endY = 350;

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(4);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.BLACK);

        path = new Path();
    }

    public BezierView1(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public BezierView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, -1);
    }

    public BezierView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();
        paint.setColor(Color.BLACK);
        path.moveTo(startX, startY);
        //二介曲线绘制方法
        path.quadTo(contorlX, contorlY, endX, endY);
        canvas.drawPath(path, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            contorlX = event.getX();
            contorlY = event.getY();
            invalidate();
        }
        return true;
    }
} 

五、运行:

这里运行看一下:

 

而若是想绘制三阶,也相似,使用Path的API既可,可是!!!若是三阶以上你也想绘制,怎么办?系统没有直接提供相关的API呀,因此接下来处理高阶的绘制问题。

四阶及更高阶贝塞尔曲线:

一、先在屏幕随机绘制5个点:

既然是四阶,确定是须要五个点的,一个开始点,一个结束点,还有三个控制点,因此在正式绘制曲线以前,先将准备工做作到位,这块比较简单,直接给出代码了:

 

代码以下:

package com.cexo.beziermaker.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.Random;

/**
 * 绘制四阶或更高阶贝塞尔曲线,使用德卡斯特里奥算法(贝塞尔公式)实现
 */
public class BezierView2 extends View {

    //多控制点【第一个点和最后一个点则为开始与结束点】
    private ArrayList<PointF> controlPoints = null;
    private Paint paint, linePointPaint;

    public BezierView2(Context context) {
        this(context, null);

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(4);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.BLACK);

        linePointPaint = new Paint();
        linePointPaint.setAntiAlias(true);
        linePointPaint.setStrokeWidth(4);
        linePointPaint.setStyle(Paint.Style.STROKE);
        linePointPaint.setColor(Color.RED);

        controlPoints = new ArrayList<>();
        init();
    }

    private void init() {
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            int x = random.nextInt(600) + 100;
            int y = random.nextInt(600) + 100;
            PointF pointF = new PointF(x, y);
            controlPoints.add(pointF);
        }
    }

    public BezierView2(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public BezierView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, -1);
    }

    public BezierView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 控制点和控制点连线
        int size = controlPoints.size();
        PointF point;
        for (int i = 0; i < size; i++) {
            point = controlPoints.get(i);
            if (i > 0) {
                // 控制点连线
                canvas.drawLine(controlPoints.get(i - 1).x, controlPoints.get(i - 1).y, point.x, point.y,
                        linePointPaint);
            }
            // 控制点
            canvas.drawCircle(point.x, point.y, 12, linePointPaint);
        }
    }
}

运行看一下效果:

固然这个点是随机的,每次运行是不同的。

二、德卡斯特里奥算法(贝塞尔公式)了解:

好,接下来的核心就是怎么来绘制贝塞尔曲线的问题了,这里则须要使用到一个比较“高深”的算法了,如小标题所示:德卡斯特里奥算法,百度一下它:

算法的目标:

在正式了解该算法以前,咱们先从贝塞尔曲线绘制特色来分析一下,好比这个三阶贝塞尔曲线:

是否是最终其实就是降阶来进行处理的,以下:

 

而对于“德卡斯特里奥算法”而言,它的目的就是能够达到降阶的效果,其实也就是经过这个算法,最终可以算出降为一阶的绘制点:

有了绘制点,那么将全部绘制点链接起来,不就成了一个曲线了么,这就是该算法的目的之所在,另外该算法能实现N阶贝塞尔曲线,是一个通用的算法,因此首先先明白该算法的一个目的很重要

算法的逻辑:

关于它的算法逻辑,说实话是有一点难理解的,反正我是在网上找了一圈,貌似说得都有点生涩,若是不理解透,你在编写代码时确定会懵,不信,我先贴出来以后利用此算法实现贝塞尔曲线的一个代码:

是否是在你不了解此算法的公式以前,这代码是彻底理解不了的,仅仅只能把这个方法当一个工具方法来用,可是别人问你怎么实现时,能够丢一句:“代码如此,本身看吧”。

一、先理解如何在一个线段中得到具体比例的点:【这块有点小绕,可是必须理解透】

关于这个算法的逻辑分析能够参考这位大佬的:https://blog.csdn.net/venshine/article/details/51750906,基本上网上说的都是这篇的内容,我也是基于这篇来理解的,不过我也是反复看以后才有所理解,这里理解算法的核心得要理解透这句话才行:

由于,有一个式子,跟我提早贴的代码中的式子很像呀:

理解透了这个公式,那对于整个算法的核心就已经掌握了,这篇博主我看完以后其实仍是有点懵懵的,最后理解到位是经过它:https://wenku.baidu.com/view/7ae91e260722192e4436f605.html

其实两篇思想同样,只是这篇语言组织上详细通俗一点,我们就根据两位大佬的博客,带着本身的理解来挼一挼:

其中提到了“向量”这个词, 不知各位都对它还记得么,对我来讲印象比较深入,由于以前学了“线性代数”:

如彻底没印象的,能够移步到这https://www.cnblogs.com/webor2006/p/14245895.html瞅一瞅,图中所示的应该仍是比较简单的,道出了这个算法的核心思想就是来找到C这个点的位置:

其中为啥是“使得C分向量AB为u:1-u(即∣AC∣:∣AB∣= u)”,实际上是将整个AB当作是1(why?),而后u是∣AC∣:∣AB∣,其中还知道“∣AC∣”是啥意思不?小学数学仍是初中数学的知识了,表示AC两点的距离,复习下:

那:

那是否是:

因此很显然C就把向量AB分红了u:1-u了。接下来则须要来解释上面标红的一句话了:“整个AB当作是1”,这里回忆一下最开始对于一阶贝塞尔曲线的执行过程:

看到其中的t了么?最大就是1,由于曲线的绘制就是根据算出来的绘制点连线而成,而多少个绘制点则你能够本身来根据这个1进行拆分,可能这边说得有点绕,这里先把以后我们实现时须要用到的一个代码先提早亮出来吧,这样就比较好理解了:

固然如今贴代码有点超前,不过细节先不用关注,这里只是为了理解这个“1”,接下来的核心就是理解如何算C这个绘制点了,也就是博主的这句话:

理解一下,“A到B的向量是B - A”,这个应该容易理解吧,就是指AB之间的距离嘛,而后C点占整个1的百分比是u,那么C点的位置很明显就是u * (B -A)嘛,这样就把C给算出来了呀,可是“考虑到A点的位置”,因为A点的位置不必定是原点,好比发生偏移之类的,那么整个C点的位置还得基于A的位置来算,因此整个式子就变成了“A + u(B - A)”,再根据结合率之类的,最终就能够变化成这个式子了:“(1 - u)*A + u*B”,其中是否是能够发现,只要我将u这个比例值知道了,整个C点的绘制位置就知道了?

二、理解递归的逻辑:

对于一个N阶贝塞尔曲线而言,你最终要绘制的只是最里面1阶贝塞尔曲线,因此光知道了,好比这样的一个五阶贝塞尔曲线的演变过程:

也就是在计算过程当中会涉及到N多个点对吧,可是绘制而言其实只须要它就能够了:

很明显这里须要一个递归的过程最终再算出指定t下的贝塞尔曲线所在的那个绘制点对吧, 那递归的思路是啥呢?下面基于图中的这个场景来简单挼一下:

其中的u为0.4,也就是该点是整个贝塞尔曲线40%的位置,说实话,这个递归过程不是那么好描述,其实就是降级的过程,最终降到一阶,其绘制点就给算出来了,这块具体的过程,我打算先把代码实现贴出来以后,再结合程序来进行理解,这样可能更加容易理解一点,因此这里的递归过程先忽略。

三、实现四阶贝塞尔曲线:

接下来直接来实现多阶贝塞尔曲线了,代码其实仍是比较亲切的,以下:

a、先将整个绘制分红1000等份,而后一等份一等份的算出绘制点:

而具体算法,目前还未实现:

b、完成绘制点的计算:

/**
     * deCasteljau算法
     * p(i,j) =  (1-t) * p(i-1,j)  +  u * p(i-1,j-1);
     *
     * @param i 阶数   4
     * @param j 控制点 3
     * @param t 时间
     * @return
     */
    private float deCasteljauX(int i, int j, float t) {
        if (i == 1) {
            return (1 - t) * controlPoints.get(j).x + t * controlPoints.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) * controlPoints.get(j).y + t * controlPoints.get(j + 1).y;
        }
        return (1 - t) * deCasteljauY(i - 1, j, t) + t * deCasteljauY(i - 1, j + 1, t);
    }

总体的思路就是若是当前是1阶了,则直接根据德卡斯特里奥算法来算出绘制点,若是大于1阶的,则递归降级处理,具体的细节这里先不解释,由于下面会根据运行结果再来分析整个程序的执行过程的,这样你就明白了其递归的一个逻辑了。

四、运行:

接下来运行看一下,目前看的是4阶的效果,因为点是随机产生的,多运行几回,看曲线画得完不完美:

嗯,挺完美的~~ 

五、实现N阶贝塞尔曲线:

为了验证这种方式的通用性,用两个极端的阶数来测试一下,一个是2阶,一个是7阶,先来看一个2阶的吧,我们把程序写一个数字既可,其它彻底不须要动:

运行:

 

接下来再看一下7阶的,一样改个数字既可:

运行:

完美,可见经过这种算法来实现的贝塞尔曲线能够知足全部阶次。 

六、分析递归逻辑:

好!!最后这里还遗留一个问题,那就是整个递归的思路是咋样的呢?对应的代码是:

这里以三阶贝塞尔曲线绘制为例,来debug一下我们的程序,为了方便分析,这里将随机的点改成定死的点,以下:

先看一下运行的样子:

注意,起始点和结束点的位置,由于关系到待会的逻辑分析涉及到的控制点的控制:

而为了方便分析流程,这里将t定死一个,只分析一个绘制点既可,以下:

目前长这样了:

而为了更加精简,只分析一下坐标点的x的值计算过程既可,由于y值的计算过程是如出一辙的,也就是它:

另外,为了让分析代码变得清晰,这里将deCasteljauX调整一下:

是否是这样跟我们理论所分析的公式就如出一辙了:

这样分析递归也好分析一些,否则一行代码有两个递归看着有点晕,一切就绪,接下来进入代码分析阶段。

开始分析:

一、i=3,j=0:

其中i表明是阶数,j表明的是控制点,也就是首先是来计算三阶的这

因为阶数不是1,因此此时会进入递归环节。

二、递归计算A点:float A = deCasteljauX(i - 1, j, t);

在分析前,要明白目前分析的点是哪一个,这里利用开源的BezierMaker来手动制造跟我们分析的三阶贝塞尔曲线,由于它有降阶的辅助线供参考,因此对于想把贝塞尔曲线学好的强烈推荐这个工具,真的很方便,其实目前这句代码的目的就是为了算出它:

【注意】:这里我制造效果时有点顺序问题,应该p0在p3的位置的,因为在写逻辑时已经基于这样的图进行了,这里就说明一下,p3是第一个点,p0是最后一个点【正常p0是第一个点,p3是最后一个点嘛,犯了个细节错误,将错就错了,不过不影响总体的逻辑理解】,这个注意一下!!!

而这个A点的算出,很明显是须要根据降阶操做以后的这俩A,B点来算出,以下:

因此,为了降阶,这里将阶数减一再次递归了:

而后此时进入递归环节:

因为阶数目前仍是不等于1,又会执行到这:

算二阶的A:

而此时因为阶数已经降为2了,再递归降一级,就变为1阶了,此时结果就直接能够算出返回了:

 

此时的A为619.23193,也就是这个点坐标的x值:

算二阶的B:

此时则开始算降阶的B点了:

一样因为阶数已经仍是2,再递归降一级,就变为1阶了,此时结果就直接能够算出返回了:

此时的B为361.1466,也就是这个点坐标的x值:

结果得出:

此时再根据德卡斯特里奥算法,此时这个二阶上的点结果x值就出来了:

也就是这个点算出来为482.70563:

这里好好理会一下整个A值的计算过程,很明显的一个降级,也就是无论多少阶,最终都会递归降到1阶,而后再算出其中的A,B点的值,再算出最终的曲线绘制点,固然这个递归不是很好理解的,须要本身边画边挼一下才行。

三、计算B点:float B = deCasteljauX(i - 1, j + 1, t);

理清了A点的计算递归逻辑,这个B点的计算逻辑是如出一辙的,一样在分析前,须要明确目标,这句代码的意思是算出它:

而这个B点的算出,很明显是须要根据降阶操做以后的这俩A,B点来算出,以下:

因此,为了降阶,这里将阶数减一再次递归了:

而后注意,此时的j控制点+1了:

这是由于此时B点的算出得根据第二个控制点来:

是否是这两个点得基于p2起步来算出?因此这里的控制点数+1了,这个细节须要好好体会。

而后此时进入递归环节:

因为阶数目前仍是不等于1,又会执行到这:

 

算二阶的A:

而此时因为阶数已经降为2了,再递归降一级,就变为1阶了,此时结果就直接能够算出返回了:

此时的A为361.1466,也就是这个点坐标的x值:

 

算二阶的B:

此时则开始算降阶的B点了:

一样因为阶数已经仍是2,再递归降一级,就变为1阶了,此时结果就直接能够算出返回了:

此时的B为213.16849,也就是这个点坐标的x值:

结果得出:

此时再根据德卡斯特里奥算法,此时这个二阶上的点结果x值就出来了:

也就是这个点算出来为282.86667:

四、根据德卡斯特里奥算法来算出结果:

目前A,B两点已经算出来了,你说要绘制的这个点怎么得出应该就很简单了吧?

 

这样,绘制点就成功算出:

总结递归逻辑:

通过这么完整的一个debug分析,是否是比直接分析代码要容易理解一些?对于递归的程序,若是当你光看代码很差理解的话,建议用debug的方式完整的梳理一遍,这样你就会比较容易理解,不过!!!总体仍是比较晕的,这里最后再整理一下,其实递归的目的就是降级,降到1阶,递归的目的就是为了算出1阶的开始点和结束点:

有了这俩点,最终绘制点就能够根据德卡斯特里奥算法来算出了。

 七、理解等份的含义:

最后,我们将debug时的一些代码给还原,最后再来看个东东,就是关于它的做用:

以前不是说这个数值越大其绘制出来的曲线就越细腻么,为了体现,我们改成很小,反着看一下,是否是越小,越不细腻,这里先把1000的图贴出来:

而后我们将其改成10,再对比看一下:

 

 

是否是对比很是明显,等份小了,很明显点与点之间的距离大了,而后连线固然就不圆润了啦,经过这个对比应该就明白这个等份的意义了吧?

总结:

至此,咱们已经学会了N阶贝塞尔曲线的画法了,可是!!!在以后我们实现的QQ汽泡的效果时只会使用到Android Path API来绘制二阶贝塞尔曲线的,那这么复杂的算法有何意义呢?可让你对贝塞尔曲线的概念理解得更加深入呀,若是你只学Android API和二三阶贝塞尔曲线的用法,人家都把实现细节给你封装好了,能知道贝塞尔曲线的精髓么?这篇耗了我业余大概一周多时间梳理吧,收获反正是挺大的,有种茅塞顿开的感受,若是你有心,建议本身写一篇博客从头到尾的按本身的思路来梳理分享出来,哪怕是网上参考的【可是必定得要带着本身的理解去解读,而不是直接copy】,这样你就能体会到虽然说时间成本比较大,可是你的收获也很大,这也是为啥我一直坚持的缘由,这样可让本身摒弃浮躁,脚踏实地的一步步朝前走。

原本计划还要实现一个qq汽泡的效果滴,不过在这个算法上花得篇幅较长,放下次吧。

下一篇: sql 补零方法