贝塞尔曲线(Bezier)

作者:追风剑情 发布于:2019-2-17 14:31 分类:Algorithms

Bezier曲线的定义

给出型值点P0,P1,...,Pn,它们所确定的Bezier曲线是

1111.png

式中,基函数Bi,n(t)是Bernstein多项式:

2222.png

式中可能涉及0!及00,按约定均为1。0的任何次方都为0(0的0次方无意义)。任何数的0次方都为1(0的0次方无意)。

这里Bezier曲线可以看作是n+1个混合函数混合给定的n+1个顶点而产生的,混合函数用Bernstein多项式,所生成曲线是n次多项式。通常n+1个顶点也称为控制点,依次连接各控制点的多边形称为控制多边形。

例如,在n=1时,上式成为

33333.png

这表明一次Bezier曲线是连接起点P0和终点P1的直线段。

在n=2时,上式成为

4444.png

由此不难知道二次Bezier曲线是抛物线。

在n=3时,上式成为

555.png

这是一条三次参数多项式曲线。

Bernstein基函数具有如下性质,这些性质也决定了Bezier曲线的性质。

(1) 正性

1111.png

(2) 端点性质

111111.png

(3) 规范性

11111.png

(4) 对称性

2222.png

33333.png

(5) 权性

3333.png

由二项式定理可知:

4444.png

二项式定理

111111.png

二项式定理常用形式

2222.png

(6) 递推性

555.png

即高一次的Bernstein基函数可由两个低一次的Bernstein基函数线性组合而成。由于有组合恒等式:

666.png

所以

777.png

(7) 导函数

3333.png

因为

4444.png

11111.png

22222.png

(8) 最大值

Bi,n(t)在t=i/n处达到最大值。

因为当Bi,n(t)取最大值时,B'i,n(t)=0。推得Bi-1,n(t)=Bi-1,n(t),整理

222.png

333.png

推导过程

11111.png

几何作图法

几何作图法也称为de Casteljau算法,它利用了Bezier曲线的分割递推性实现Bezier曲线的绘制。

几何作图法的优点是直观性强,计算速度快。

9999.png(图1)

递推关系

888.png

上式含义是: 由点P0,P1,...,Pn所确定的n次Bezier曲线在点t的值,可以由点P0,P1,...,Pn-1所确定的n-1次Bezier曲线在点t的值,与由点P1,P2,...,Pn所确定的n-1次Bezier曲线在点t的值,通过递推关系的线性组合简单地求得。

n次Bezier曲线上控制点在t时的值P(t),可以归结为计算两个n-1次Bezier曲线在t时的值的线性组合,这一过程可以继续下去。图1中已知三次Bezier曲线的控制顶点P0,P1,P2,P3,递归计算先按t的比例在控制多边形各边上求得P0(1),P1(1),P2(1),再求得P0(2),P1(2),最后求得P0(3),即为P(t)对应的点。

示例:C#版——利用几何作图法算法实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BezierTest
{
    public class Bezier
    {
        /// <summary>
        /// 作图法算法实现
        /// </summary>
        /// <param name="P">控制点坐标</param>
        /// <param name="t">插值参数</param>
        /// <returns>返回曲线在参数t的坐标值</returns>
        public static Point Lerp(Point[] P, double t)
        {
            int m, i;//m 边数
            int n = P.Length; //n 控制点个数
            Point P0 = null;
            Point[] R, Q;
            R = new Point[n];
            Q = new Point[n];
            for (i = 0; i < n; i++)
            {
                R[i] = P[i];//将控制点坐标P保存于R中
                Q[i] = new Point();
            }
                
            //作n次外部循环,
            //每次循环都计算控制多边形上所有的m条边以参数t为分割比例的坐标值
            for(m=n-1; m > 0; m--)
            {
                //作m次内部循环,
                //每次循环计算控制多边形上一条边以参数t为分割比例的坐标值
                for(i=0; i <= m - 1; i++)
                {
                    //n次Bezier曲线在点t的值,可由两条n-1次bezier曲线
                    //在点t的值通过线性组合而求得
                    Q[i].x = R[i].x + t * (R[i+1].x - R[i].x);
                    Q[i].y = R[i].y + t * (R[i+1].y - R[i].y);
                }
                for (i = 0; i <= m - 1; i++)
                    R[i] = Q[i];
            }
            P0 = R[0];
            R = null;
            Q = null;
            return P0;
        }

        public class Point
        {
            public double x;
            public double y;
        }
    }
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BezierTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public void PaintControlPoint(PaintEventArgs e, Bezier.Point[] P)
        {
            Graphics g = e.Graphics;
            Pen myPen = new Pen(Color.Red, 2);
            for(int i=0; i<P.Length; i++)
            {
                g.DrawEllipse(myPen, (float)P[i].x, (float)P[i].y, 3, 3);
            }
        }

        public void PaintCurvePoint(PaintEventArgs e, Bezier.Point[] P, double t)
        {
            Bezier.Point tp = Bezier.Lerp(P, t);

            Graphics g = e.Graphics;
            Pen myPen = new Pen(Color.Blue, 2);
            g.DrawEllipse(myPen, (float)tp.x, (float)tp.y, 1, 1);
            
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Bezier.Point[] P = new Bezier.Point[]
            {
                new Bezier.Point{ x=0, y=100},
                new Bezier.Point{ x=50, y=0},
                new Bezier.Point{ x=80, y=200},
                new Bezier.Point{ x=130, y=50},
                new Bezier.Point{ x=200, y=100},
            };

            PaintControlPoint(e, P);

            for (double t = 0; t <= 1; t += 0.02)
            {
                PaintCurvePoint(e, P, t);
            }
        }
    }
}


运行测试

红色为控制点,蓝色为曲线的插值点

55555.png7777.png

标签: Algorithms

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号