[转载] Hermite 与 Bezier 曲线的绘制

发信人:   william@cis_nctu   (何陋居主),   信区:   programming  
  标     题:   [转载]   Hermite   与   Bezier   曲线的绘制  
  发信站:   交大资科_BBS   (May   25   00:32:41   1995)  
  转信站:   cis_nctu  
   
   
      From:   Chi'u   I-Nan                                           *   Area:   90   C   语言  
          To:   All                                                           Date:   03   May   95     22:37:26  
      Subj:   Hermite   与   Bezier   曲线的绘制  
   
  **********************************************************************  
  *   Hermite与Bezier曲线绘制方法研究                                                                         *  
  *   作者:邱奕南   (Chi'u   I-Nan)                                                                                   *  
  *   版权声明:以下文章内容本人仅同意供BBS   站上流传学习,但必须完整流传   *  
  *                       (含版权声明及程序),其余权利一概保留。任何未经本人同意   *  
  *                       ,将本文贩卖、刊登、节录、或其它一切侵害本人著作权之行为   *  
  *                       者,皆需负担刑事责任及民事赔偿责任。                                           *  
  **********************************************************************  
   
            Hermite及Bezier曲线为三度空间曲线的常用表示法,以下我们先说明一  
  下这两个曲线的定义:  
   
  1.Hermite曲线  
   
            Hermite曲线为给定两端点及两端点向量所得的三次曲线。令三次曲线:  
   
                                      3                   2  
            x(t)   =   Ax   *   t     +   Bx   *   t     +   Cx   *   t   +   d  
   
                                      3                   2  
            y(t)   =   Ay   *   t     +   By   *   t     +   Cy   *   t   +   d  
   
  且令给定的两端点为(x1,y1)、(x2,y2),以及两端点向量(xr1,yr1)、(xr2,yr2),  
  则:  
   
            x(0)     =   x1,       y(0)     =   y1  
            x(1)     =   x2,       y(1)     =   y2  
            x'(0)   =   xr1,     y'(0)   =   yr1  
            x'(1)   =   xr2,     y'(1)   =   yr2  
   
  绘出(x(t),y(t)),0<=t<=1,即为Hermite曲线。  
   
  2.Bezier曲线  
   
          Bezier曲线为给定两端点及另两参考点所得的三次曲线,它可说是Hermite  
  曲线的另一种表示方式。假设两端点为P1、P2,以及两参考点Pr1、Pr2,则  
  Bezier曲线换算成Hermite曲线的方式为:  
   
          R1   =   3*(Pr1-P1)  
          R2   =   3*(P2-Pr2)  
   
  R1、R2即为Hermite曲线的两端点向量。  
   
          由于Bezier曲线和Hermite曲线可以说是相同的,因此以下之说明我们便以  
  Hermite曲线为主。由Hermite曲线的定义,首先我们必须求出Ax,Bx...等值。  
  由定义中的条件:  
   
          x(0)     =   x1,     x(1)     =   x2,     x'(0)   =   xr1,     x'(1)   =   xr2  
   
  以及  
   
                                              2  
          x'(t)   =   3   *   Ax   *   t     +   2   *   Bx   *   t   +   Cx  
   
  可得:  
   
          ┌   0   0   0   1   ┐┌   Ax   ┐     ┌   x1     ┐  
          │   1   1   1   1   ││   Bx   │=│   x2     │  
          │   0   0   1   0   ││   Cx   │     │   xr1   │  
          └   3   2   1   0   ┘└   Dx   ┘     └   xr2   ┘  
   
  解之得:  
   
          ┌   Ax   ┐     ┌     2   -2     1     1   ┐┌   x1     ┐  
          │   Bx   │=│   -3     3   -2     1   ││   x2     │  
          │   Cx   │     │     0     0     1     0   ││   xr1   │  
          └   Dx   ┘     └     1     0     0     0   ┘└   xr2   ┘  
   
  因此  
   
          x(t)   =   (2*t^3   -   3*t^2   +   1)   *   x1   +   (-2*t^3   +   3*t^2)   *   x2   +  
                        (t^3   -   2*t^2   +   t)   *   xr1   +   (t^3   -   t^2)   *   xr2  
   
  同理  
   
          y(t)   =   (2*t^3   -   3*t^2   +   1)   *   y1   +   (-2*t^3   +   3*t^2)   *   y2   +  
                        (t^3   -   2*t^2   +   t)   *   yr1   +   (t^3   -   t^2)   *   yr2  
   
          上述的公式中,由于t的值是介于0与1之间,这对于我们在实际绘图上较不  
  方便,而且运算速度也比较慢。因此假设我们以n条直线来仿真Hermite曲线,  
  则我们必须将t值化成整数,使它成为0<=t<=n(注意n条连续直线需有n+1个点)。  
  令i=n*t代入上述公式,化简后可得:  
   
          x(i)   =   (m1*x1   +   m2*x2   +   m3*xr1   +   m4*xr2)   /   n^3  
          y(i)   =   (m1*y1   +   m2*y2   +   m3*yr1   +   m4*yr2)   /   n^3  
   
  其中  
   
          m1   =   2*i^3   -   3*n*i^2   +   n^3                 ,   0   <=   i   <=   n  
          m2   =   -2*i^3   +   3*n*i^2  
          m3   =   i^3   -   2*n*i^2   +   n^2*i  
          m4   =   i^3   -   n*i^2  
   
  如此我们便能很方便地求出仿真直线的各端点。然而上述公式中的乘法仍相当  
  多,对于m1~m4的计算上至少需5次乘法、2次移位乘法和6次加法(或4次乘法、  
  3次移位乘法和7次加法),在计算上仍然会浪费相当多时间(PC上的乘法约为  
  加法的近20倍,移位乘法则和加法相当),因此有必要再予以化简。如何简化  
  呢?由上述公式可发现,主要的乘法来自于i^3的计算,因此欲将之简化的最简  
  单的方式便是由前一个m1~m4值来求得后一个m1~m4值,藉此消减掉这个三次  
  方值的运算:  
   
          m1(i+1)   =   m1(i)   +   6*i^2   +   6*i   +   2   -   3*n*(2*i+1)  
          m2(i+1)   =   m2(i)   -   6*i^2   -   6*i   -   2   +   3*n*(2*i+1)  
          m3(i+1)   =   m3(i)   +   3*i^2   +   3*i   +   1   -   2*n*(2i+1)   +   n^2  
          m4(i+1)   =   m4(i)   +   3*i^2   +   3*i   +   1   -   n*(2*i+1)  
   
  现在便是要化简各式中后面的计算项次。由于这些项次有相当多的重复,故可  
  将之写成:  
   
          m1(i+1)   =   m1(i)   +   a  
          m2(i+1)   =   m2(i)   -   a  
          m3(i+1)   =   m3(i)   +   d   -   c   +   n^2  
          m4(i+1)   =   m4(i)   +   d  
          a   =   2*b   -   3*c  
          b   =   3*i^2   +   3*i   +   1  
          c   =   n*(2*i+1)  
          d   =   b-c  
   
  再进一步简化为:  
   
          e   =   2*i   +   1  
          c   =   n*e  
          b   =   3*i^2   +   3*i   +   1  
              =   (3*i+1)*i   +   (2*i+1)  
              =   (e+i)*i   +   e  
   
  整理一下成为(依计算顺序):  
   
          e     =   2*i   +   1  
          b     =   (e+i)*i   +   e  
          c     =   n*e  
          d     =   b-c  
          m4   =   m4   +   d  
          f     =   d-c  
          m3   =   m3   +   f   +   n^2  
          a     =   d   +   f  
          m2   =   m2   -   a  
          m1   =   m1   +   a  
   
  共计2次乘法、1次移位乘法和11次加法,等于是以两次加法代替了两次乘法,  
  其速度必然较快。记得m1~m4的初值为:  
   
          m1(0)   =   n^3  
          m2(0)   =   m3(0)   =   m4(0)   =   0  
   
          接下来我们要探讨一下模拟线数n的问题。倒底取多少模拟线数较为恰当?  
  采用的仿真线数太少,曲线将不够平滑,但若模拟线数太多,曲线绘制速度又  
  会太慢。据作者实际的测试,一般取n=20以上曲线便已相当平滑,但对于弯度  
  太大的曲线,其转折处仍约略可见,不过并不严重。由于在绘制曲线时,大都  
  以整数来运算(为求速度),因此仿真线数最好不要超出32,以避免运算结果  
  超出整数运算的值域。以下便是Hermite曲线和Bezier曲线的绘制程式:  
   
   
  #define   Iterative     24                               /*   曲线仿真的线数(必须小于32)   */  
  #define   Iterative2   (Iterative*Iterative)  
  #define   Iterative3   (Iterative2*Iterative)  
   
  void   DrawHermiteCurve(int   x1,int   y1,int   x2,int   y2,int   xr1,int   yr1,  
        int   xr2,int   yr2)  
      {  
        /*   ------------------------------------------------------------  
              作用:画出Hermite曲线  
              输入:x1,y1,x2,y2   =   曲线端点  
                          xr1,yr1,xr2,yr2   =   曲线两参考向量  
              作者:邱奕南   Chi'u   I-Nan  
              ------------------------------------------------------------   */  
        int   i,   oldx,   oldy,   m1,   m2,   m3,   m4,   x,   y,   k1,   k2;  
   
        oldx   =   x1;  
        oldy   =   y1;  
        m1   =   Iterative3;  
        m2   =   m3   =   m4   =   0;  
        for   (i=0;   i<Iterative;   i++)     /*   用Iterative条直线仿真   */  
            {  
              k1   =   (i   <<   1)   +   1;  
              k2   =   (k1+i)*i   +   k1;  
              m4   +=   (k2   -=   (k1   *=   Iterative));  
              m3   +=   (k1   =   k2   -   k1)   +   Iterative2;  
              m2   -=   (k2   +=   k1);  
              m1   +=   k2;  
              x   =   (int)   (((long)   x1*m1   +   (long)   x2*m2   +   (long)   xr1*m3   +  
                                      (long)   xr2*m4)   /   Iterative3);  
              y   =   (int)   (((long)   y1*m1   +   (long)   y2*m2   +   (long)   yr1*m3   +  
                                      (long)   yr2*m4)   /   Iterative3);  
              DrawLine(oldx,oldy,x,y);  
              oldx   =   x;  
              oldy   =   y;  
            }  
      }  
   
  void   DrawBezierCurve(int   x1,int   y1,int   x2,int   y2,int   xr1,int   yr1,  
        int   xr2,int   yr2)  
      {  
        /*   ------------------------------------------------------------  
              作用:画出Bezier曲线  
              输入:x1,y1,x2,y2   =   曲线端点  
                          xr1,yr1,xr2,yr2   =   曲线两参考点  
              作者:邱奕南   Chi'u   I-Nan  
              ------------------------------------------------------------   */  
        DrawHermiteCurve(x1,y1,x2,y2,3*(xr1-x1),3*(yr1-y1),3*(x2-xr2),3*(y2-yr2));  
      }  
   
                                  *   青衫诗客   --   小邱   *  
   
  --   Via   中文银版快信   V2.28C  
    !   Origin:   档案货柜,   欢迎您来挖宝,   28800   BPS,   04-230-2080;   (90:2010/622)  
  ________________________________________  
  --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --  
  ≡   何陋居   ≡   中学生以伏案读书为主,   大学生则应起而高瞻远瞩。     Whitehead.  
  --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --   --

你可能感兴趣的:(c,Date,测试,读书,语言,bbs)