贝塞尔曲线

2019-11-06  本文已影响0人  放肆滴微笑

贝塞尔曲线的来历

贝塞尔曲线的数学基础是早在 1912 年就广为人知的[伯恩斯坦多项式。直到1959年。当时就职于雪铁龙的法国数学家 Paul de Casteljau 开始对伯恩斯坦多项式进行了图形化的尝试,并且提供了一种数值稳定的德卡斯特里奥(de Casteljau) 算法。(多数理论公式是建立在大量且系统的数学建模基础之上研究的规律性成果)根据这个算法,就可以实现 通过很少的控制点,去生成复杂的平滑曲线,也就是贝塞尔曲线。

但贝塞尔曲线的声名大噪,不得不提到1962年就职于雷诺的法国工程师皮埃尔·贝塞尔(Pierre Bézier),他使用这种方法来辅助汽车的车体工业设计(最早计算机的诞生则是为了帮助美国海军绘制弹道图),并且广泛宣传(典型的理论联系实际并获得成功的示例),因此大家称为贝塞尔曲线

贝塞尔曲线的数学理论

贝塞尔曲线的本质是通过数学计算公式去绘制平滑的曲线,这些曲线是用一系列点来控制曲线状态的,可以将这些点分为2类,一类是数据点,一类是控制点。

N 阶贝塞尔

可以理解为有N+1个点 N+1减1 就是 N阶贝塞尔,比如3阶就是4个点链接的线,4-1=3,就叫做3阶贝塞尔

一阶贝塞尔

image

可以看到一阶贝塞尔是一条直线,可以通过几何知识得到t的坐标点


image.png

二阶贝塞尔

image

Path中贝塞尔曲线的实现

二阶贝塞尔

起点为之前的结束点,或者是下笔点
4个参数,x1,y1,为控制点,也就是B点,x2,y2为终点
public void quadTo(float x1, float y1, float x2, float y2)
Path path = new Path();
path.moveTo(100,100); //  起点
path.quadTo(400,200,50,500); //控制点和终点
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawPath(path,paint1);
image.png

三阶贝塞尔

起点为之前的结束点,或者是下笔点
x1,y1第一个控制点,x2,y2第二个控制点,x3,y3为终点
public void cubicTo(float x1, float y1, float x2, float y2,
                        float x3, float y3)
Path path  = new Path();
path.moveTo(100,100);
path.cubicTo(400,200,50,500,400,400);
canvas.drawCircle(100,100,10,paint);
canvas.drawCircle(400,200,10,paint);
canvas.drawCircle(50,500,10,paint);
canvas.drawCircle(400,400,10,paint);
canvas.drawPath(path,paint1);
image.png

N 阶

//生成贝塞尔Path
private ArrayList<PointF> buildBezierPoints() {
        mPath.reset();
        ArrayList<PointF> points = new ArrayList<>();
        int order = controlPoints.size() - 1;//阶数
        //份数
        float delta = 1.0f / 1000;
        for (float t = 0; t <= 1; t += delta) {
            //bezier点集
            PointF pointF = new PointF(deCastelJau(order, 0, t, true), deCastelJau(order, 0, t, false));//计算在曲线上点位置
            points.add(pointF);
            if (points.size() == 1) {
                mPath.moveTo(points.get(0).x, points.get(0).y);
            } else {
                mPath.lineTo(pointF.x, pointF.y);
            }
        }
        return points;
    }

    /**
     * p(i,j) =  (1-t) * p(i-1,j)  +  t * p(i-1,j+1);
     *
     * @param i          阶数
     * @param j          控制点
     * @param t          时间
     * @param calculateX 计算哪个坐标值 true=x
     * @return
     */
    private float deCastelJau(int i, int j, float t, boolean calculateX) {
        if (i == 1) {
            return calculateX ? (1 - t) * controlPoints.get(j).x + t * controlPoints.get(j + 1).x :
                    (1 - t) * controlPoints.get(j).y + t * controlPoints.get(j + 1).y;
        } else {
            return (1 - t) * deCastelJau(i - 1, j, t, calculateX) + t * deCastelJau(i - 1, j + 1, t, calculateX);
        }
    }


PointF pointF;
        for (int i = 0; i < controlPoints.size(); i++) {
            pointF = controlPoints.get(i);
            if (i > 0) {
                paint.setColor(Color.GRAY);
                canvas.drawLine(controlPoints.get(i - 1).x,
                        controlPoints.get(i - 1).y,
                        pointF.x, pointF.y, paint);
            }

            if (i == 0) {
                //起点为红色
                paint.setColor(Color.RED);
            } else if (i == controlPoints.size() - 1) {
                //终点为蓝色
                paint.setColor(Color.BLUE);
            }

            canvas.drawCircle(pointF.x
                    , pointF.y, 10, paint);  //画所有的控制点
        }

        buildBezierPoints();    //生成贝塞尔Path
        canvas.drawPath(mPath, paint1);  //画线
image.png

参考:https://www.jianshu.com/p/0c9b4b681724

上一篇下一篇

猜你喜欢

热点阅读