home *** CD-ROM | disk | FTP | other *** search
/ Windows Graphics Programming / Feng_Yuan_Win32_GDI_DirectX.iso / Samples / include / BezierCurve.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-16  |  7.8 KB  |  325 lines

  1. //-----------------------------------------------------------------------------------//
  2. //              Windows Graphics Programming: Win32 GDI and DirectDraw               //
  3. //                             ISBN  0-13-086985-6                                   //
  4. //                                                                                   //
  5. //  Written            by  Yuan, Feng                             www.fengyuan.com   //
  6. //  Copyright (c) 2000 by  Hewlett-Packard Company                www.hp.com         //
  7. //  Published          by  Prentice Hall PTR, Prentice-Hall, Inc. www.phptr.com      //
  8. //                                                                                   //
  9. //  FileName   : beziercurve.cpp                                                     //
  10. //  Description: Generic arc/bezier curves drawing                                   //
  11. //  Version    : 1.00.000, May 31, 2000                                              //
  12. //-----------------------------------------------------------------------------------//
  13.  
  14. #define STRICT
  15. #define _WIN32_WINNT 0x0500
  16. #define WIN32_LEAN_AND_MEAN
  17.  
  18. #include <windows.h>
  19. #include <assert.h>
  20. #include <tchar.h>
  21. #include <math.h>
  22.  
  23. #include "BezierCurve.h"
  24.  
  25. // Simple text label
  26. void Label(HDC hDC, int x, int y, const char * mess)
  27. {
  28.     TextOut(hDC, x, y, mess, _tcslen(mess));
  29. }
  30.  
  31.  
  32. // Label Bezier Curve control points
  33. void Label(HDC hDC, POINT p[], int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
  34. {
  35.     Label(hDC, p[0].x+x1, p[0].y+y1, "P1");
  36.     Label(hDC, p[1].x+x2, p[1].y+y2, "P2");
  37.     Label(hDC, p[2].x+x3, p[2].y+y3, "P3");
  38.     Label(hDC, p[3].x+x4, p[3].y+y4, "P4");
  39. }
  40.  
  41.  
  42. // Point inteporation
  43. void P(POINT & rslt, POINT & p1, POINT p2, double t)
  44. {
  45.     rslt.x = (int) ( (1-t) * p1.x + t * p2.x );
  46.     rslt.y = (int) ( (1-t) * p1.y + t * p2.y );
  47. }
  48.  
  49.  
  50. // Line
  51. void Line(HDC hDC, int x0, int y0, int x1, int y1)
  52. {
  53.     MoveToEx(hDC, x0, y0, NULL); LineTo(hDC, x1, y1);
  54. }
  55.  
  56.  
  57. // Line
  58. void Line(HDC hDC, int x0, int y0, int x1, int y1, HPEN hPen)
  59. {
  60.     SelectObject(hDC, hPen);
  61.  
  62.     MoveToEx(hDC, x0, y0, NULL); LineTo(hDC, x1, y1);
  63.  
  64.     SelectObject(hDC, GetStockObject(BLACK_PEN));
  65. }
  66.  
  67.  
  68. // Line
  69. void Line(HDC hDC, POINT & p1, POINT & p2)
  70. {
  71.     MoveToEx(hDC, p1.x, p1.y, NULL);
  72.     LineTo(hDC, p2.x, p2.y);
  73. }
  74.  
  75.  
  76. // 3x3 dot
  77. void Dot(HDC hDC, int x, int y)
  78. {
  79.     for (int i=-1; i<=1; i++)
  80.     for (int j=-1; j<=1; j++)
  81.         SetPixel(hDC, x+i, y+j, RGB(0, 0, 0xFF));
  82. }
  83.  
  84.  
  85. // simulate AngleArcTo using ArcTo
  86. BOOL AngleArcTo(HDC hDC, int X, int Y, DWORD dwRadius, FLOAT eStartAngle, FLOAT eSweepAngle)
  87. {
  88.     const FLOAT pi = (FLOAT) 3.141592653586;
  89.  
  90.     if ( eSweepAngle >=360 )    // more than one circle is one circle
  91.         eSweepAngle = 360;
  92.     else if ( eSweepAngle <= -360 )
  93.         eSweepAngle = - 360;
  94.  
  95.     FLOAT  eEndAngle   = (eStartAngle + eSweepAngle ) * pi / 180;
  96.            eStartAngle = eStartAngle * pi / 180;
  97.     
  98.     int dir;
  99.     
  100.     if ( eSweepAngle > 0 )
  101.         dir = SetArcDirection(hDC, AD_COUNTERCLOCKWISE);
  102.     else
  103.         dir = SetArcDirection(hDC, AD_CLOCKWISE);
  104.     
  105.     BOOL rslt = ArcTo(hDC, X - dwRadius, Y - dwRadius, X + dwRadius, Y + dwRadius,
  106.                       X + (int) (dwRadius * 10 * cos(eStartAngle)), 
  107.                       Y - (int) (dwRadius * 10 * sin(eStartAngle)),
  108.                       X + (int) (dwRadius * 10 * cos(eEndAngle)),   
  109.                       Y - (int) (dwRadius * 10 * sin(eEndAngle)));
  110.  
  111.     SetArcDirection(hDC, dir);
  112.     return rslt;
  113. }
  114.  
  115.  
  116. // Break Bezier curve into lines
  117.  
  118. void Bezier(HDC hDC, double x1, double y1, double x2, double y2, 
  119.                      double x3, double y3, double x4, double y4)
  120. {
  121.     double A = y4 - y1;
  122.     double B = x1 - x4;
  123.     double C = y1 * (x4-x1) - x1 * ( y4-y1);
  124.     // Ax + By + C = 0  is line (x1,y1) - (x4,y4)
  125.  
  126.     double AB  = A * A + B * B;
  127.  
  128.     // distance from (x2,y2) to the line is less than 1
  129.     // distance from (x3,y3) to the line is less than 1
  130.     if ( ( A * x2 + B * y2 + C ) * ( A * x2 + B * y2 + C ) < AB )
  131.     if ( ( A * x3 + B * y3 + C ) * ( A * x3 + B * y3 + C ) < AB )
  132.     {
  133.         MoveToEx(hDC, (int)x1, (int)y1, NULL);
  134.         LineTo(hDC, (int)x4, (int)y4);
  135.  
  136.         return;
  137.     }
  138.     
  139.     double x12   = x1+x2;
  140.     double y12   = y1+y2;
  141.     double x23   = x2+x3;
  142.     double y23   = y2+y3;
  143.     double x34   = x3+x4;
  144.     double y34   = y3+y4;
  145.  
  146.     double x1223 = x12+x23;
  147.     double y1223 = y12+y23;
  148.     double x2334 = x23+x34;
  149.     double y2334 = y23+y34;
  150.  
  151.     double x     = x1223 + x2334;
  152.     double y     = y1223 + y2334;
  153.  
  154.     Bezier(hDC, x1, y1, x12/2, y12/2, x1223/4, y1223/4, x/8, y/8);
  155.     Bezier(hDC, x/8, y/8, x2334/4, y2334/4, x34/2, y34/2, x4, y4);
  156. }
  157.  
  158.  
  159. // Calculate coordinate according to Bezier curve formula
  160. double P(double t, double p1, double p2, double p3, double p4)
  161. {
  162.     return     (1-t)*(1-t)*(1-t) * p1 +
  163.            3 * (1-t)*(1-t)*t     * p2 +
  164.            3 * (1-t)*t*t         * p3 +
  165.                t*t*t             * p4;
  166. }
  167.  
  168.  
  169. // calculate the largest error from a 90 degree Bezier curve relative to the unit circle
  170. double Error(double m, double & et)
  171. {
  172. //    double R = 4 * ( sqrt(2.0) - 1 ) / 3;
  173.  
  174.     double t     = 0;
  175.     double error = 0;
  176.  
  177.     while ( t<=0.5 )
  178.     {
  179.         double x = P(t, 0, m, 1, 1);
  180.         double y = P(t, 1, 1, m, 0);
  181.  
  182.         x = sqrt(x * x + y * y) - 1;
  183.  
  184.         if ( fabs(x)>fabs(error) )
  185.         {
  186.             et    = t;
  187.             error = x;
  188.         }
  189.  
  190.         t += 0.001;
  191.     }
  192.  
  193.     return error;
  194. }
  195.  
  196. /*
  197. Approximating sqrt(2)-1
  198.  
  199. n        m        n/m
  200. 1        2        0.5
  201. 2        5        0.4
  202. 5        12        0.416666667
  203. 12        29        0.413793103
  204. 29        70        0.414285714
  205. 70        169        0.414201183
  206. 169        408        0.414215686
  207. 408        985        0.414213198
  208. 985        2378    0.414213625
  209. 2378    5741    0.414213552
  210. 5741    13860    0.414213564
  211.  
  212. n:m -> m : 2m+n
  213. */
  214.  
  215. // Drawing a full ellipse using 4 Bezier curves
  216. BOOL EllipseToBezier(HDC hDC, int left, int top, int right, int bottom)
  217. {
  218.     const double M = 0.55228474983;
  219.     
  220.     POINT P[13];
  221.  
  222.     int dx = (int) ((right - left) * (1-M) / 2);
  223.     int dy = (int) ((bottom - top) * (1-M) / 2);
  224.  
  225.     P[ 0].x = right;            //     .   .   .   .   .   
  226.     P[ 0].y = (top+bottom)/2;   //       4   3   2
  227.     P[ 1].x = right;            //
  228.     P[ 1].y = top + dy;         //   5               1
  229.     P[ 2].x = right - dx;       //
  230.     P[ 2].y = top;                //   6              0,12
  231.     P[ 3].x = (left+right)/2;    //
  232.     P[ 3].y = top;                //   7               11    
  233.                                 //
  234.     P[ 4].x = left + dx;        //       8   9   10    
  235.     P[ 4].y = top;                
  236.     P[ 5].x = left;
  237.     P[ 5].y = top + dy;
  238.     P[ 6].x = left;
  239.     P[ 6].y = (top+bottom)/2;
  240.  
  241.     P[ 7].x = left;
  242.     P[ 7].y = bottom - dy;
  243.     P[ 8].x = left + dx;
  244.     P[ 8].y = bottom;
  245.     P[ 9].x = (left+right)/2;
  246.     P[ 9].y = bottom;
  247.  
  248.     P[10].x = right - dx;
  249.     P[10].y = bottom;
  250.     P[11].x = right;
  251.     P[11].y = bottom - dy;
  252.     P[12].x = right;
  253.     P[12].y = (top+bottom)/2;
  254.  
  255.     return PolyBezier(hDC, P, 13);
  256. }
  257.  
  258.  
  259. // Drawing an arc using a singple Bezier curve
  260.  
  261. BOOL AngleArcToBezier(HDC hDC, int x0, int y0, int rx, int ry, 
  262.                       double startangle, double sweepangle, double * err)
  263. {
  264.     double XY[8];
  265.     POINT P[4];
  266.  
  267.     // Compute bezier curve for arc centered along y axis
  268.     // Anticlockwise: (0,-B), (x,-y), (x,y), (0,B) 
  269.     double B = ry * sin(sweepangle/2);
  270.     double C = rx * cos(sweepangle/2);
  271.     double A = rx  - C;
  272.  
  273.     double X = A*4/3;
  274.     double Y = B - X * (rx-A)/B;
  275.  
  276.     XY[0] =   C;
  277.     XY[1] = - B;
  278.     XY[2] =   C+X;
  279.     XY[3] = - Y;
  280.     XY[4] =   C+X;
  281.     XY[5] =   Y;
  282.     XY[6] =   C;
  283.     XY[7] =   B;
  284.  
  285.     // rotate to the original angle
  286.     double s = sin(startangle + sweepangle/2);
  287.     double c = cos(startangle + sweepangle/2);
  288.  
  289.     double xy[8];
  290.     
  291.     for (int i=0; i<4; i++)
  292.     {
  293.         if ( err )
  294.         {
  295.             xy[i*2]   = XY[i*2] * c - XY[i*2+1] * s;
  296.             xy[i*2+1] = XY[i*2] * s + XY[i*2+1] * c;
  297.         }
  298.  
  299.         P[i].x = x0 + (int) (XY[i*2] * c - XY[i*2+1] * s);
  300.         P[i].y = y0 + (int) (XY[i*2] * s + XY[i*2+1] * c);
  301.     }
  302.  
  303.     if ( err )
  304.     {        
  305.         double t   = 0;
  306.  
  307.         * err = 0;
  308.  
  309.         while ( t<=1 )
  310.         {
  311.             double x = ::P(t, xy[0], xy[2], xy[4], xy[6]) / rx;
  312.             double y = ::P(t, xy[1], xy[3], xy[5], xy[7]) / ry;
  313.             double r = sqrt(x*x + y*y);
  314.  
  315.             if ( fabs(r-1) > fabs(*err) )
  316.                 *err = r-1;
  317.  
  318.             t += 0.01;
  319.         }
  320.     }
  321.  
  322.     return PolyBezier(hDC, P, 4);
  323. }
  324.  
  325.