zoukankan      html  css  js  c++  java
  • Bezier曲线原理及实现代码(c++)

    Bezier曲线原理及实现代码(c++)  

    2009-06-30 18:50:09|  分类: 数据结构与算法|字号 订阅

     
     

           一、原理:

           贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由 Paul de Casteljau 于1959年运用 de Casteljau 算法开发,以稳定数值的方法求出贝塞尔曲线。

    线性贝塞尔曲线

    给定点 P0P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

    mathbf{B}(t)=mathbf{P}_0 + (mathbf{P}_1-mathbf{P}_0)t=(1-t)mathbf{P}_0 + tmathbf{P}_1 mbox{ , } t in [0,1]

    且其等同于线性插值

    二次方贝塞尔曲线的路径由给定点 P0P1P2 的函数 B(t) 追踪:

    mathbf{B}(t) = (1 - t)^{2}mathbf{P}_0 + 2t(1 - t)mathbf{P}_1 + t^{2}mathbf{P}_2 mbox{ , } t in [0,1]

    TrueType 字型就运用了以贝塞尔样条组成的二次贝塞尔曲线。

    P0P1P2P3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P0 走向 P1,并从 P2 的方向来到 P3。一般不会经过 P1 或 P2;这两个点只是在那里提供方向资讯。 P0 和 P1 之间的间距,决定了曲线在转而趋进 P3 之前,走向 P2 方向的“长度有多长”。

    曲线的参数形式为:

    mathbf{B}(t)=mathbf{P}_0(1-t)^3+3mathbf{P}_1t(1-t)^2+3mathbf{P}_2t^2(1-t)+mathbf{P}_3t^3 mbox{ , } t in [0,1]

    现代的成象系统,如 PostScriptAsymptote 和 Metafont,运用了以贝塞尔样条组成的三次贝塞尔曲线,用来描绘曲线轮廓。

    一般化

    P0P1、…、Pn,其贝塞尔曲线即

    mathbf{B}(t)=sum_{i=0}^n {nchoose i}mathbf{P}_i(1-t)^{n-i}t^i =mathbf{P}_0(1-t)^n+{nchoose 1}mathbf{P}_1(1-t)^{n-1}t+cdots+mathbf{P}_nt^n mbox{ , } t in [0,1]

    例如 

    mathbf{B}(t)=mathbf{P}_0(1-t)^5+5mathbf{P}_1t(1-t)^4+10mathbf{P}_2t^2(1-t)^3+10mathbf{P}_3t^3(1-t)^2+5mathbf{P}_4t^4(1-t)+mathbf{P}_5t^5 mbox{ , } t in [0,1]

    如上公式可如下递归表达: 用 mathbf{B}_{mathbf{P}_0mathbf{P}_1ldotsmathbf{P}_n} 表示由点 P0P1、…、Pn 所决定的贝塞尔曲线。则

    mathbf{B}(t) = mathbf{B}_{mathbf{P}_0mathbf{P}_1ldotsmathbf{P}_n}(t) = (1-t)mathbf{B}_{mathbf{P}_0mathbf{P}_1ldotsmathbf{P}_{n-1}}(t) + tmathbf{B}_{mathbf{P}_1mathbf{P}_2ldotsmathbf{P}_n}(t)

    用平常话来说, 阶贝塞尔曲线之间的插值。

    一些关于参数曲线的术语,有

    mathbf{B}(t) = sum_{i=0}^n mathbf{P}_imathbf{b}_{i,n}(t),quad tin[0,1]

    即多项式

    mathbf{b}_{i,n}(t) = {nchoose i} t^i (1-t)^{n-i},quad i=0,ldots n

    又称作 n 阶的伯恩斯坦基底多项式,定义 00 = 1。

    点 Pi 称作贝塞尔曲线的控制点多边形以带有线的贝塞尔点连接而成,起始于 P0并以 Pn 终止,称作贝塞尔多边形(或控制多边形)。贝塞尔多边形的凸包(convex hull)包含有贝塞尔曲线。

     
     

    线性贝塞尔曲线函数中的 t 会经过由 P0 至P1 的 B(t) 所描述的曲线。例如当 t=0.25时,B(t) 即一条由点 P0 至 P1 路径的四分之一处。就像由 0 至 1 的连续 tB(t) 描述一条由 P0 至 P1 的直线。

    线性贝塞尔曲线演示动画,t in [0,1]

    为建构二次贝塞尔曲线,可以中介点 Q0 和 Q1 作为由 0 至 1 的 t

    • 由 P0 至 P1 的连续点 Q0,描述一条线性贝塞尔曲线。
    • 由 P1 至 P2 的连续点 Q1,描述一条线性贝塞尔曲线。
    • 由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
    •  
    二次贝塞尔曲线的结构   二次贝塞尔曲线演示动画,t in [0,1]
         

    为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点 Q0Q1Q2,和由二次曲线描述的点 R0R1 所建构:

    三次贝塞尔曲线的结构   三次贝塞尔曲线演示动画,t in [0,1]
         

    对于四次曲线,可由线性贝塞尔曲线描述的中介点 Q0Q1Q2Q3,由二次贝塞尔曲线描述的点 R0R1R2,和由三次贝塞尔曲线描述的点 S0S1 所建构:

    四次贝塞尔曲线的结构   四次贝塞尔曲线演示动画,t in [0,1]
         

    P(t)=(1-t)P0+tP1 , Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    矩阵表示为:
      Bezier曲线原理及实现代码(c++) - mappy - mappy 天下Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 。
    P(t)=(1-t)2P0+2t(1-t)P1+t2P2Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    矩阵表示为:
      Bezier曲线原理及实现代码(c++) - mappy - mappy 天下Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 。

      P(t)=(1-t)3P0+3t(1-t)2P1+3t2(1-t)P2+t3P
    矩阵表示为:
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下, Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    (6-3-2) 
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 。 
    在(6-3-2)式中,Mn+1是一个n+1阶矩阵,称为n次Bezier矩阵。
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 (6-3-3)
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    其中,
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    利用(6-3-3)式,我们可以得到任意次Bezier矩阵的显式表示,例如4次和5次Bezier矩阵为:
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 
    可以证明,n次Bezier矩阵还可以表示为递推的形式:
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 (6-3-4)
    Bezier曲线原理及实现代码(c++) - mappy - mappy 天下 

    二、算法(c++)

    工程目录是:Win32App 
    vc6.0

    #include<windows.h>
    #include<stdlib.h>
    #include<time.h>
    #define NUM 10

    LRESULT CALLBACK Winproc(HWND,UINT,WPARAM,LPARAM);
    int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstanc,LPSTR lpCmdLine,int nShowCmd)
    {
        MSG msg;
        static TCHAR szClassName[] = TEXT("::Bezier样条计算公式由法国雷诺汽车公司的工程师Pierm Bezier于六十年代提出");
        HWND hwnd;
        WNDCLASS wc;
        wc.cbClsExtra =0;
        wc.cbWndExtra =0;
        wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wc.hCursor = LoadCursor(NULL,IDC_ARROW);
        wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
        wc.hInstance = hInstance;
        wc.lpfnWndProc = Winproc;
        wc.lpszClassName = szClassName;
        wc.lpszMenuName = NULL;
        wc.style = CS_HREDRAW|CS_VREDRAW;

        if(!RegisterClass(&wc))
        {
            MessageBox(NULL,TEXT("注册失败"),TEXT("警告框"),MB_ICONERROR);
            return 0;
        }
        hwnd = CreateWindow(szClassName,szClassName,
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT,CW_USEDEFAULT,
                            CW_USEDEFAULT,CW_USEDEFAULT,
                            NULL,NULL,hInstance,NULL);

        ShowWindow(hwnd,SW_SHOWMAXIMIZED);
        UpdateWindow(hwnd);

        while(GetMessage(&msg,NULL,0,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }

    LRESULT CALLBACK Winproc(HWND hwnd,UINT message, WPARAM wparam,LPARAM lparam)
    {
      PAINTSTRUCT ps;
      HDC hdc;
      static POINT pt[NUM];
      TEXTMETRIC tm;
      static int cxClient,cyClient;
      HPEN hpen;
      int i,j,k,n,t;

      switch(message)
      {
      case WM_CREATE:
          static int cxchar;
          hdc = GetDC(hwnd);
          GetTextMetrics(hdc,&tm);
          cxchar = tm.tmAveCharWidth;
          ReleaseDC(hwnd,hdc);

      case WM_SIZE:
           cxClient = LOWORD(lparam);
          cyClient = HIWORD(lparam);
          return 0;
      case WM_PAINT:
           hdc = GetDC(hwnd);
           srand(time(0));

           Rectangle(hdc,0,0,cxClient,cyClient);
          for(i=0; i<500; i++)
              {
                SelectObject(hdc,GetStockObject(WHITE_PEN));
                PolyBezier(hdc,pt,NUM);
                for(j=0; j<NUM; j++)
                {
                    pt[j].x = rand()%cxClient;
                    pt[j].y = rand()%cyClient;
                }
                hpen = CreatePen(PS_INSIDEFRAME,3,RGB(rand()%256,rand()%256,rand()%256));
                 DeleteObject(SelectObject(hdc,hpen));
                PolyBezier(hdc,pt,NUM);
                for(k=0; k<50000000;k++);
              }
          for(i=0; i<100;i++)
          {
            Ellipse(hdc,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient);

            Pie(hdc,j=rand()%cxClient,k=rand()%cyClient,n=rand()%cxClient,t=rand()%cyClient,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient) ; 

          }
           if((n=(n+j)/2)>cxchar*20) n=cxchar*20;  
            SetTextColor(hdc,RGB(rand()%256,rand()%256,rand()%256));
            TextOut(hdc,n/2,(t+k)/2,TEXT("瑾以此向Pierm Bezier致敬!"),lstrlen(TEXT("瑾以此向Pierm Bezier致敬!")));
            ReleaseDC(hwnd,hdc);
              DeleteObject(hpen);
              ValidateRect(hwnd,NULL);
       return 0;

      case WM_DESTROY:
          PostQuitMessage(0);
          return 0;
      }
      return DefWindowProc(hwnd,message,wparam,lparam);
    }

  • 相关阅读:
    QQ分享 QQ空间分享 API链接:
    一起谈.NET技术,C# 中奇妙的函数联接序列的五种简单方法 狼人:
    一起谈.NET技术,.NET简谈面向接口编程 狼人:
    一起谈.NET技术,用C#实现HTTP协议下的多线程文件传输 狼人:
    一起谈.NET技术,改善代码设计 —— 简化函数调用(Making Method Calls Simpler) 狼人:
    一起谈.NET技术,改善代码设计 —— 处理概括关系(Dealing with Generalization) 狼人:
    一起谈.NET技术,页面片段缓存(二) 狼人:
    一起谈.NET技术,改善代码设计 —— 简化条件表达式(Simplifying Conditional Expressions) 狼人:
    一起谈.NET技术,回顾.NET Remoting分布式开发 狼人:
    一起谈.NET技术,改善代码设计 —— 优化物件之间的特性(Moving Features Between Objects) 狼人:
  • 原文地址:https://www.cnblogs.com/mfryf/p/2366162.html
Copyright © 2011-2022 走看看