Android自定义控件系列——贝塞尔曲线全解析

贝塞尔曲线

贝塞尔曲线展示

一阶贝塞尔曲线

在这里插入图片描述

二阶贝塞尔曲线定义:DF:DE = AD:AB = BE:BC

在这里插入图片描述

在这里插入图片描述

三阶贝塞尔曲线

在这里插入图片描述

在这里插入图片描述

多阶贝塞尔曲线

在这里插入图片描述

三维贝塞尔曲线

在这里插入图片描述

贝塞尔曲线公式

在这里插入图片描述
P ( t ) = ( 1 − t ) 3 P 1 + 3 t ( 1 − t ) 2 P 2 + 3 t 2 ( 1 − t ) P 3 + t 3 P 4 P(t) = (1−t)^3P_1 + 3t(1-t)^2P_2 + 3t^2(1-t)P_3 + t^3P_4 P(t)=(1t)3P1+3t(1t)2P2+3t2(1t)P3+t3P4
通过公式:n代表最高次数,i对应控制点顺序
B i n ( t ) = n ! i ! ( n − i ) ! t i ( 1 − t ) n − i B^n_i(t)= \frac{n!}{i!(n-i)!}t^i(1-t)^{n-i} Bin(t)=i!(ni)!n!ti(1t)ni

特性

​ 1、一个贝塞尔曲线可分割成多个,且不影响整个曲线,不提升计算难度。反之亦然

在线工具

缓动函数速查表http://www.xuanfengge.com/easeing/easeing/
Ceaserhttp://xuanfengge.com/easeing/ceaser/
cubic-bezierhttp://cubic-bezier.com/在线调试

案例

​ 计算控制点坐标分别为:P0(3,8),P1(2,3),P2(2,7),想要返回 10 个在贝塞尔曲线上的点

float[] p0 = {3, 8};
float[] p1 = {4, 3};
float[] p2 = {2, 7};
float[][] result = new float[10][2];
for (int i = 0; i < 10; i++) {
	float t = i / 10;
	result[i][0] = (float) (1 * Math.pow(1 - t, 2) * Math.pow(t, 0) * p0[0] + 2 * Math.pow(1 - t, 1) * Math.pow(t, 1) * p1[0] + 1 * Math.pow(1 - t, 0) * Math.pow(t, 2) * p2[0]);
	result[i][1] = (float) (1 * Math.pow(1 - t, 2) * Math.pow(t, 0) * p0[1] + 2 * Math.pow(1 - t, 1) * Math.pow(t, 1) * p1[1] + 1 * Math.pow(1 - t, 0) * Math.pow(t, 2) * p2[1]);

​ 计算贝塞尔曲线上所有点坐标

/**
* @param poss		贝塞尔曲线控制点坐标
* @param precision 	 需要计算的该条贝塞尔曲线上的点的数目
* @return 			该条贝塞尔曲线上的点(二维坐标)
*/
public float[][] calculate(float[][] poss, int precision) {

    //维度,坐标轴数(二维坐标,三维坐标...)
    int dimersion = poss[0].length;

    //贝塞尔曲线控制点数(阶数)
    int number = poss.length;

    //控制点数不小于 2 ,至少为二维坐标系
    if (number < 2 || dimersion < 2)
        return null;

    float[][] result = new float[precision][dimersion];

    //计算杨辉三角
    int[] mi = new int[number];
    mi[0] = mi[1] = 1;
    for (int i = 3; i <= number; i++) {

        int[] t = new int[i - 1];
        for (int j = 0; j < t.length; j++) {
            t[j] = mi[j];
        }

        mi[0] = mi[i - 1] = 1;
        for (int j = 0; j < i - 2; j++) {
            mi[j + 1] = t[j] + t[j + 1];
        }
    }

    //计算坐标点
    for (int i = 0; i < precision; i++) {
        float t = (float) i / precision;
        for (int j = 0; j < dimersion; j++) {
            float temp = 0.0f;
            for (int k = 0; k < number; k++) {
                temp += Math.pow(1 - t, number - k - 1) * poss[k][j] * Math.pow(t, k) * mi[k];
            }
            result[i][j] = temp;
        }
    }

    return result;
}

​ 通过计算出来的点依次连接形成贝塞尔曲线

// calculate 方法在 BezierImpl 中实现
    private BezierImpl bezier = new BezierImpl();
    private Paint paint = new Paint();
    float[][] poss = {
            {353.0f, 383.0f},
            {670.0f, 266.0f},
            {403.0f, 128.0f},
            {148.0f, 369.0f},
            {400.0f, 513.0f},
            {564.0f, 503.0f},
            {582.0f, 378.0f},
            {682.0f, 878.0f},
            {182.0f, 878.0f}
    };

    @Override
    protected void onDraw(Canvas canvas) {
        float x0, y0, x, y;
        paint.setColor(Color.DKGRAY);
        paint.setStrokeWidth(3.0f);
        x0 = poss[0][0];
        y0 = poss[0][1];
        for (int i = 1; i < poss.length; i++) {
            x = poss[i][0];
            y = poss[i][1];
            //画控制点连线
            canvas.drawLine(x0, y0, x, y, paint);
            x0 = x;
            y0 = y;
        }

        paint.setColor(Color.RED);
        paint.setStrokeWidth(5.0f);
        float[][] po = bezier.calculate(poss, 500);
        x0 = po[0][0];
        y0 = po[0][1];
        for (int i = 1; i < 500; i++) {
            x = po[i][0];
            y = po[i][1];
            //画贝塞尔曲线连线
            canvas.drawLine(x0, y0, x, y, paint);
            x0 = x;
            y0 = y;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值