android N阶贝瑟尔曲线绘制

android N阶贝瑟尔曲线绘制

本文主要讲解N阶贝瑟尔曲线绘制的原理,主要实现是通过描点的方式绘制在Canvas对象上。

贝瑟尔曲线又称为贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。

推导

二阶推导

给定点A,B,C,a=[0,1];则二阶贝瑟尔曲线的点集可以表示为 :
P = ( 1 − a ) 2 A + 2 a ( 1 − a ) B + a 2 C P=(1-a)^2A+2a(1-a)B+a^2C P=(1a)2A+2a(1a)B+a2C
计算过程:
AB线段上点E,BC线段上点F,EF线段上点P,P点的集合就是二阶贝瑟尔曲线的集合
E = A + a ( B − A ) = ( 1 − a ) A + a B 同理可得: E = ( 1 − a ) A + a B F = ( 1 − a ) B + a C P = ( 1 − a ) 2 A + 2 a ( 1 − a ) B + a 2 C E=A+a(B-A)\\\quad=(1-a)A+aB\\ 同理可得:\\ E=(1-a)A+aB\\ F=(1-a)B+aC\\ P=(1-a)^2A+2a(1-a)B+a^2C E=A+a(BA)=(1a)A+aB同理可得:E=(1a)A+aBF=(1a)B+aCP=(1a)2A+2a(1a)B+a2C

三阶推导

给定点A,B,C,D,a=[0,1]; 则三阶贝瑟尔曲线的点集可以表示为:
P = ( 1 − a ) 3 A + 3 a ( 1 − a ) 2 B + 3 a 2 ( 1 − a ) C + a 3 D P=(1-a)^3A+3a(1-a)^2B+3a^2(1-a)C+a^3D P=(1a)3A+3a(1a)2B+3a2(1a)C+a3D
计算过程:

AB线段上点E, BC 线段上点F,CD上线段点G,点E、F、G就类似二阶贝瑟尔曲线上的三个点。
E = ( 1 − a ) A + a B F = ( 1 − a ) B + a C G = ( 1 − a ) C + a D 则 E F 上点 M 、 F G 上点 N , M N 上点 P 的点分别表示为: M = ( 1 − a ) E + a F N = ( 1 − a ) F + a G P = ( 1 − a ) M + a N = ( 1 − a ) 2 E + 2 a ( 1 − a ) F + a 2 G = ( 1 − a ) 2 [ ( 1 − a ) A + a B ] + 2 a ( 1 − a ) [ ( 1 − a ) B + a C ] + a 2 [ ( 1 − a ) C + a D ] = ( 1 − a ) 3 A + a ( 1 − a ) 2 B + 2 a ( 1 − a ) 2 B + 2 a 2 ( 1 − a ) C + a 2 ( 1 − a ) C + a 3 D = ( 1 − a ) 3 A + 3 a ( 1 − a ) 2 B + 3 a 2 ( 1 − a ) C + a 3 D ) E=(1-a)A+aB\\ F=(1-a)B+aC\\ G=(1-a)C+aD\\ 则EF上点M、FG上点N,MN上点P的点分别表示为:\\ M=(1-a)E+aF\\ N=(1-a)F+aG\\ P=(1-a)M+aN\\ =(1-a)^2E+2a(1-a)F+a^2G\\ =(1-a)^2[(1-a)A+aB]+2a(1-a)[(1-a)B+aC]+a^2[(1-a)C+aD]\\ =(1-a)^3A+a(1-a)^2B+2a(1-a)^2B+2a^2(1-a)C+a^2(1-a)C+a^3D\\ =(1-a)^3A+3a(1-a)^2B+3a^2(1-a)C+a^3D) E=(1a)A+aBF=(1a)B+aCG=(1a)C+aDEF上点MFG上点NMN上点P的点分别表示为:M=(1a)E+aFN=(1a)F+aGP=(1a)M+aN=(1a)2E+2a(1a)F+a2G=(1a)2[(1a)A+aB]+2a(1a)[(1a)B+aC]+a2[(1a)C+aD]=(1a)3A+a(1a)2B+2a(1a)2B+2a2(1a)C+a2(1a)C+a3D=(1a)3A+3a(1a)2B+3a2(1a)C+a3D)

N阶推导

给定点x1,x2,x3,…xn,则N阶贝瑟尔曲线上的点集可以表示为:
P = ∑ i = 0 n C n i a i ( 1 − a ) n − i x i P=\sum_{i=0}^nC_n^ia^i(1-a)^{n-i}x_i P=i=0nCniai(1a)nixi
推理过程:

由二阶和三阶的点集表示形式可以看出:按照顺序每个点前面的乘数恰好是 ((1-a) + a) 的 2次、3次方展开式。由此可得,N阶贝瑟尔曲线每个点的系数恰好是N次方的展开式。

核心代码

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i("xxx", String.format("onDraw: pointSize=%d", pointSize));
        bezierTest(canvas, paint, new float[]{50,50,250,50, 250,250,50,250,50,150});

    }

 	/**
     * 测试
     * @param canvas
     * @param paint
     * @param points
     */
    public void bezierTest(Canvas canvas, Paint paint, float[] points){
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(5);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setColor(Color.YELLOW);
        float[] _points = nBezier(2000, points);
        canvas.drawPoints(_points, paint);
        paint.setColor(Color.RED);
        paint.setStrokeWidth(6);
        canvas.drawPoints(points, paint);
    }

    /**
     * 获取 N 阶贝瑟尔曲线的点
     * @param pointNum
     * @param points :[x1,y1,x2,y2,x3,y3....]
     * @return
     */
    public float[] nBezier(int pointNum, float ...points){
        if (points == null || points.length <6 || pointNum<=0){
            return new float[]{0,0};
        }
        float[] res = new float[pointNum*2];
        float ratio = (float) (1.0/((float)pointNum));
        float stepRatio = ratio;
        int n = points.length / 2;
        // 多项式系数
        int[] nIndex = getNIndex(n);
        // nn:(1-stepRatio) 的N次方; n1:(1-stepRatio)的k次*stepRatio的(1-k)次方
        float nn, n1=0;
        float dx, dy;
        for (int i = 1; i < pointNum; i++) {
            nn = (float) Math.pow((1-stepRatio), n-1);
            dx = 0;
            dy = 0;
            n1 = 1;
            for (int i1 = 0; i1 < nIndex.length; i1++) {
                dx += points[i1*2] * n1 * nn * nIndex[i1];
                dy += points[i1*2+1] * n1 * nn * nIndex[i1];
                n1 *= stepRatio;
                nn /=(1-stepRatio);
            }
            res[i*2] = dx;
            res[i*2+1] = dy;
            stepRatio += ratio;
        }
        res[0] = points[0];
        res[1] = points[1];
        res[n-2] = points[2*(n-1)];
        res[n-1] = points[2*(n-1)+1];
        return res;
    }

    /**
     * 获取N次方系数
     * @param n
     * @return
     */
    public int[] getNIndex(int n){
        if (n <=1){
            return new int[]{1};
        }
        int[] res = new int[n];
        long nn = getN(n);
        long k1=1, k2=nn;

        for (int i = 1; i < n; i++) {
            k1 *=i;
            k2 /=(n-i);
            res[i] = (int) (nn / k1 / k2);
        }
        res[0] = 1;
        res[n-1] = 1;
        return res;
    }

    /**
     * 获取N的阶乘
     * @param n
     * @return
     */
    private long getN(long n){
        if (n <= 1){
            return 1;
        }
        return getN(n-1)*n;
    }

这里的onDraw()方法是自定义View 中需要重写的方法。

nBezier()方法是获取贝瑟尔曲线点的方法。

二阶三阶可以不使用这个方法来进行绘制,Path中有quadTo() 方法来绘制二阶贝瑟尔曲线,cubicTo()方法来绘制三阶贝瑟尔曲线。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值