PAINT.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:19k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /******************************************************************************
  2. *
  3. *  MODULE:      PAINT.C
  4. *
  5. *  PURPOSE:     This is the module responsible for painting the SPINCUBE
  6. *               custom control. When Paint() is called we retrieve a
  7. *               pointer to a SPINCUBEINFO structure, and then use it's
  8. *               current rotation & translation values to transform the
  9. *               polyhedron described by gNormalizedVertices & gaiFacets.
  10. *               Once we've transformed the vertices, we draw the
  11. *               background, which consists of a grey rectangle and a few
  12. *               black lines (a crass attempt to render a perspective
  13. *               view into a "room"), on the offscreen bitmap associated
  14. *               with the control (i.e. pSCI->hbmCompat). Then we walk the
  15. *               facet list of the transformed polyhedron (gXformedVertices
  16. *               & gaiFacets), drawing only those facets whose outward
  17. *               normal faces us (again, drawing on pSCI->hbmCompat).
  18. *               Finally, we BitBlt the appropriate rectangle from our
  19. *               offscreen bitmap to the screen itself.
  20. *
  21. *               Drawing to the offscreen bitmap has two advantages over
  22. *               drawing straight to the screen:
  23. *
  24. *                 1. The actual drawing the user sees consists of only
  25. *                    a single BitBlt. Otherwise, the user would see us
  26. *                    both erase the polyhedron in it's old position and
  27. *                    draw it in it's new position (alot of flashing- not
  28. *                    very smooth animation).
  29. *
  30. *                 2. When a spincube control with the SS_ERASE style
  31. *                    is brought to the foreground, all it's contents
  32. *                    i.e. the cube trails) are saved & can be re-Blted
  33. *                    to the screen. Otherwise, all this info would be
  34. *                    lost & there'd be a big blank spot in the middle
  35. *                    of the control!
  36. *
  37. *               Interested persons should consult a text on 3 dimensional
  38. *               graphics for more information (i.e. "Computer Graphics:
  39. *               Principles and Practice", by Foley & van Dam).
  40. *
  41. *               Notes:
  42. *
  43. *               - A 3x2 tranformation matrix  is used instead of a  3x3
  44. *                 matrix, since the transformed z-values aren't needed.
  45. *                 (Normally these would be required for use in depth
  46. *                 sorting  [for hidden surface removal], but  since we
  47. *                 draw  only  a single convex polyhedron this  is not
  48. *                 necessary.)
  49. *
  50. *               - A simplified perspective viewing transformation
  51. *                 (which also  precludes the need for the transformed z
  52. *                 coordinates). In a nutshell, the perspective  scale
  53. *                 is as follows:
  54. *
  55. *                                    p' = S    x  p
  56. *                                          per
  57. *
  58. *                 where:
  59. *                        S    = WindowDepth /
  60. *                         per      (WindowDepth + fCurrentZTranslation)
  61. *
  62. *                 (WindowDepth is  the greater of the  control's window
  63. *                 height or window width.)
  64. *
  65. *
  66. *  FUNCTIONS:   Paint()                         - the paint routine
  67. *               TransformVertices()             - transforms vertices
  68. *               ComputeRotationTransformation() - computes xformation
  69. *                                                 based on current x, y
  70. *                                                 and z rotation angles
  71. *
  72. *
  73. *                           Microsoft Developer Support
  74. *                  Copyright (c) 1992-1997 Microsoft Corporation
  75. *
  76. ******************************************************************************/
  77. #include <windows.h>
  78. #include <math.h>
  79. #include <stdlib.h>
  80. #include "spincube.h"
  81. #include "paint.h"
  82. /******************************************************************************
  83. *
  84. *  FUNCTION:     Paint
  85. *
  86. *  INPUTS:       hwnd - Handle of the window to draw into.
  87. *
  88. *  COMMENTS:     Draws window background & a polyhedron in the window.
  89. *
  90. ******************************************************************************/
  91. void Paint (HWND hwnd)
  92. {
  93.   PSPINCUBEINFO  pSCI;
  94.   RECT           rect;
  95.   int            i;
  96.   LONG           lScaleFactor;
  97.   PAINTSTRUCT    ps;
  98.   HRGN           hrgnClip;
  99.   HBRUSH         hBrush, hBrushSave;
  100.   int            iX, iY, iCX, iCY;
  101.   int            facetIndex, numPoints;
  102.   POINT          polygn[MAX_POINTS_PER_FACET];
  103.   POINT          vector1, vector2;
  104.   COLORREF       acrColor[6] = { 0x0000ff, 0x00ff00, 0xff0000,
  105.                                  0x00ffff, 0xff00ff, 0xffff00 };
  106.   pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd, GWL_SPINCUBEDATA);
  107.   BeginPaint (hwnd, &ps);
  108.   if (memcmp((void *)&ps.rcPaint, (void *)&pSCI->rcCubeBoundary, sizeof(RECT))
  109.       & !REPAINT_BKGND(pSCI))
  110.   {
  111.     //
  112.     // We're not here because it's time to animate (i.e. this paint isn't
  113.     //   the result of a WM_TIMER), so just do the Blt & blow out of here...
  114.     //
  115.     BitBlt (ps.hdc,
  116.             ps.rcPaint.left,
  117.             ps.rcPaint.top,
  118.             ps.rcPaint.right - ps.rcPaint.left,
  119.             ps.rcPaint.bottom - ps.rcPaint.top,
  120.             pSCI->hdcCompat, ps.rcPaint.left,
  121.             ps.rcPaint.top, SRCCOPY);
  122.     EndPaint (hwnd, &ps);
  123.     return;
  124.   }
  125.   //
  126.   // The rectangle we get back is in Desktop coordinates, so we need to
  127.   //   modify it to reflect coordinates relative to this window.
  128.   //
  129.   GetWindowRect (hwnd, &rect);
  130.   rect.right  -= rect.left;
  131.   rect.bottom -= rect.top;
  132.   rect.left = rect.top = 0;
  133.   //
  134.   // Determine a "best fit" scale factor for our polyhedron
  135.   //
  136.   if (!(lScaleFactor = rect.right > rect.bottom ?
  137.                        rect.bottom/12 : rect.right/12))
  138.     lScaleFactor = 1;
  139.   TransformVertices (hwnd, &rect, pSCI, lScaleFactor);
  140.   //
  141.   // Draw the window frame & background
  142.   //
  143.   // Note: The chances are that we are coming through here because we
  144.   //   got a WM_TIMER message & it's time to redraw the cube to simulate
  145.   //   animation. In that case all we want to erase/redraw is that small
  146.   //   rectangle which bounded the polyhedron the last time. The less
  147.   //   drawing that actually gets done the better, since we wnat to
  148.   //   minimize the flicker on the screen. __BeginPaint__ is perfect for
  149.   //   this because it causes all drawing outside of the invalid region
  150.   //   to be "clipped" (no drawing is performed outside of the invalid
  151.   //   region), and it also validates the invalid region.
  152.   //
  153.   if (DO_ERASE(hwnd) || REPAINT_BKGND(pSCI))
  154.   {
  155.     hrgnClip = CreateRectRgnIndirect (&ps.rcPaint);
  156.     SelectClipRgn (pSCI->hdcCompat, hrgnClip);
  157.     DeleteObject (hrgnClip);
  158.     SelectObject (pSCI->hdcCompat, GetStockObject (GRAY_BRUSH));
  159.     Rectangle    (pSCI->hdcCompat, (int)rect.left, (int)rect.top,
  160.                   (int)rect.right, (int)rect.bottom);
  161.     iX = (rect.right  - rect.left) / 4;
  162.     iY = (rect.bottom - rect.top ) / 4;
  163.     MoveToEx (pSCI->hdcCompat, (int)rect.left, (int)rect.top, NULL);
  164.     LineTo   (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.top + iY);
  165.     LineTo   (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.bottom - iY);
  166.     LineTo   (pSCI->hdcCompat, (int)rect.left,      (int)rect.bottom);
  167.     MoveToEx (pSCI->hdcCompat, (int)rect.right, (int)rect.top, NULL);
  168.     LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);
  169.     LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom- iY);
  170.     LineTo   (pSCI->hdcCompat, (int)rect.right,      (int)rect.bottom);
  171.     MoveToEx (pSCI->hdcCompat, (int)rect.left + iX,  (int)rect.top + iY, NULL);
  172.     LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);
  173.     MoveToEx (pSCI->hdcCompat, (int)rect.left + iX,  (int)rect.bottom - iY, NULL);
  174.     LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom - iY);
  175.     SelectClipRgn (pSCI->hdcCompat, NULL);
  176.     pSCI->iOptions &= ~SPINCUBE_REPAINT_BKGND;
  177.   }
  178.   //
  179.   // Draw the polyhedron. We'll walk through the facets list and compute
  180.   //   the normal for each facet- if the normal has z > 0, then the facet
  181.   //   faces us and we'll draw it. Note that this algorithim is ONLY valid
  182.   //   for scenes with a single, convex polyhedron.
  183.   //
  184.   // Note: Use GetDC here because the above call to BeginPaint will
  185.   //   probably not give us a DC with access to as much real estate as
  186.   //   we'd like (we wouldn't be able to draw outside of the invalid
  187.   //   region). We can party on the entire control window with the DC
  188.   //   returned by GetDC.
  189.   //
  190.   for (i = 0, facetIndex = 0; i < NUMFACETS; i++)
  191.   {
  192.     vector1.x = gXformedVertices[gaiFacets[facetIndex + 1]].x -
  193.                 gXformedVertices[gaiFacets[facetIndex]].x;
  194.     vector1.y = gXformedVertices[gaiFacets[facetIndex + 1]].y -
  195.                 gXformedVertices[gaiFacets[facetIndex]].y;
  196.     vector2.x = gXformedVertices[gaiFacets[facetIndex + 2]].x -
  197.                 gXformedVertices[gaiFacets[facetIndex + 1]].x;
  198.     vector2.y = gXformedVertices[gaiFacets[facetIndex + 2]].y -
  199.                 gXformedVertices[gaiFacets[facetIndex + 1]].y;
  200.     for (numPoints = 0; gaiFacets[facetIndex] != -1; numPoints++, facetIndex++)
  201.     {
  202.       polygn[numPoints].x = gXformedVertices[gaiFacets[facetIndex]].x;
  203.       polygn[numPoints].y = gXformedVertices[gaiFacets[facetIndex]].y;
  204.     }
  205.     facetIndex++; /* skip over the -1's in the facets list */
  206.     if ((vector1.x*vector2.y - vector1.y*vector2.x) > 0)
  207.     {
  208.       hBrush     = CreateSolidBrush (acrColor[i]);
  209.       hBrushSave = (HBRUSH) SelectObject (pSCI->hdcCompat, hBrush);
  210.       Polygon (pSCI->hdcCompat, &polygn[0], numPoints);
  211.       SelectObject (pSCI->hdcCompat, hBrushSave);
  212.       DeleteObject (hBrush);
  213.     }
  214.   }
  215.   iX  = pSCI->rcCubeBoundary.left < ps.rcPaint.left ?
  216.         pSCI->rcCubeBoundary.left : ps.rcPaint.left;
  217.   iY  = pSCI->rcCubeBoundary.top  < ps.rcPaint.top  ?
  218.         pSCI->rcCubeBoundary.top  : ps.rcPaint.top;
  219.   iCX = (pSCI->rcCubeBoundary.right > ps.rcPaint.right ?
  220.          pSCI->rcCubeBoundary.right : ps.rcPaint.right) - iX;
  221.   iCY = (pSCI->rcCubeBoundary.bottom > ps.rcPaint.bottom ?
  222.          pSCI->rcCubeBoundary.bottom : ps.rcPaint.bottom) - iY;
  223.   EndPaint (hwnd, &ps);
  224.   ps.hdc = GetDC (hwnd);
  225.   BitBlt (ps.hdc, iX, iY, iCX, iCY, pSCI->hdcCompat, iX, iY, SRCCOPY);
  226.   ReleaseDC (hwnd, ps.hdc);
  227. }
  228. /******************************************************************************
  229. *
  230. *  FUNCTION:     TransformVertices
  231. *
  232. *  INPUTS:       hwnd         - control window handle
  233. *                pWindowRect  - pointer to RECT describing control's dimensions
  234. *                pSCI         - pointer to control's SPINCUBEINFO structure
  235. *                fScaleFactor - scale factor for use in this window
  236. *
  237. ******************************************************************************/
  238. void TransformVertices (HWND hwnd,          RECT  *pWindowRect,
  239.                         PSPINCUBEINFO pSCI, LONG  lScaleFactor)
  240. {
  241.   int    i;
  242.   int    iWindowDepth = pWindowRect->right > pWindowRect->bottom ?
  243.                         pWindowRect->right : pWindowRect->bottom;
  244.   RECT   WindowRect;
  245.   float  fDepthScale;
  246.   int    iNewTranslationInc = (rand() % 10) + 2;
  247.   float  fNewRotationInc    = (float) 0.01 * ((rand() % 30) + 2);
  248.   WindowRect.left = - (WindowRect.right  = pWindowRect->right  >> 1);
  249.   WindowRect.top  = - (WindowRect.bottom = pWindowRect->bottom >> 1);
  250.   //
  251.   // Initiailize the bounding rectangle with max/min vals
  252.   //
  253.   pSCI->rcCubeBoundary.left   =
  254.   pSCI->rcCubeBoundary.top    = 100000; // big positive value
  255.   pSCI->rcCubeBoundary.right  =
  256.   pSCI->rcCubeBoundary.bottom = -100000; // small negative value
  257.   //
  258.   // First scale, then rotate, then translate each vertex.
  259.   //   Keep track of the maximum & minimum values bounding the
  260.   //   vertices in the x,y plane for use later in bounds checking.
  261.   //
  262.   // Note: we don't bother computing z values after the scale,
  263.   //   as they are only really necessary for the rotation. If we
  264.   //   were doing real bounds checking we'd need it, but this code
  265.   //   simply uses the pSCI->iCurrentZTranslation to determine
  266.   //   the z-boundaries.
  267.   //
  268.   for (i = 0; i < NUMVERTICES; i++)
  269.   {
  270.     LONG tempX;
  271.     //
  272.     // Copy the static vertices into a temp array
  273.     //
  274.     gXformedVertices[i] = gNormalizedVertices[i];
  275.     //
  276.     // The scale...
  277.     //
  278.     gXformedVertices[i].x *= lScaleFactor;
  279.     gXformedVertices[i].y *= lScaleFactor;
  280.     gXformedVertices[i].z *= lScaleFactor;
  281.     //
  282.     // The rotation...
  283.     //
  284.     ComputeRotationTransformation (pSCI->fCurrentXRotation,
  285.                                    pSCI->fCurrentYRotation,
  286.                                    pSCI->fCurrentZRotation);
  287.     tempX   =               (LONG) (gM[0][0] * gXformedVertices[i].x +
  288.                                     gM[0][1] * gXformedVertices[i].y +
  289.                                     gM[0][2] * gXformedVertices[i].z);
  290.     gXformedVertices[i].y = (LONG) (gM[1][0] * gXformedVertices[i].x +
  291.                                     gM[1][1] * gXformedVertices[i].y +
  292.                                     gM[1][2] * gXformedVertices[i].z);
  293.     gXformedVertices[i].x = tempX;
  294.     //
  295.     // The translation...
  296.     //
  297.     gXformedVertices[i].x += pSCI->iCurrentXTranslation;
  298.     gXformedVertices[i].y += pSCI->iCurrentYTranslation;
  299.     //
  300.     // Check if we have new max or min vals
  301.     //
  302.     if (pSCI->rcCubeBoundary.left > gXformedVertices[i].x)
  303.       pSCI->rcCubeBoundary.left = gXformedVertices[i].x;
  304.     if (pSCI->rcCubeBoundary.right < gXformedVertices[i].x)
  305.       pSCI->rcCubeBoundary.right = gXformedVertices[i].x;
  306.     if (pSCI->rcCubeBoundary.top > gXformedVertices[i].y)
  307.       pSCI->rcCubeBoundary.top = gXformedVertices[i].y;
  308.     if (pSCI->rcCubeBoundary.bottom < gXformedVertices[i].y)
  309.       pSCI->rcCubeBoundary.bottom = gXformedVertices[i].y;
  310.   }
  311.   //
  312.   // Now for some bounds checking, change translation & rotation increments
  313.   //   if we hit a "wall". Also so the gbHitBoundary flag so we remember
  314.   //   to flash the cube when we draw it.
  315.   //
  316.   if (pSCI->rcCubeBoundary.left < WindowRect.left)
  317.   {
  318.     pSCI->iCurrentXTranslationInc = iNewTranslationInc;
  319.     pSCI->fCurrentZRotationInc    = fNewRotationInc;
  320.   }
  321.   else if (pSCI->rcCubeBoundary.right > WindowRect.right)
  322.   {
  323.     pSCI->iCurrentXTranslationInc = -iNewTranslationInc;
  324.     pSCI->fCurrentZRotationInc    = -fNewRotationInc;
  325.   }
  326.   if (pSCI->rcCubeBoundary.top < WindowRect.top)
  327.    {
  328.     pSCI->iCurrentYTranslationInc = iNewTranslationInc;
  329.     pSCI->fCurrentXRotationInc    = fNewRotationInc;
  330.   }
  331.   else if (pSCI->rcCubeBoundary.bottom > WindowRect.bottom)
  332.   {
  333.     pSCI->iCurrentYTranslationInc = -iNewTranslationInc;
  334.     pSCI->fCurrentXRotationInc    = -fNewRotationInc;
  335.   }
  336.   if (pSCI->iCurrentZTranslation < (int) lScaleFactor<<1)
  337.   {
  338.     pSCI->iCurrentZTranslationInc = iNewTranslationInc;
  339.     pSCI->fCurrentYRotationInc    = fNewRotationInc;
  340.   }
  341.   else if (pSCI->iCurrentZTranslation > (iWindowDepth - (int) lScaleFactor))
  342.   {
  343.     pSCI->iCurrentZTranslationInc = -iNewTranslationInc;
  344.     pSCI->fCurrentYRotationInc    = -fNewRotationInc;
  345.   }
  346.   //
  347.   // Now a kludgy scale based on depth (iCurrentZTranslation) of the center
  348.   //   point of the polyhedron
  349.   //
  350.   fDepthScale =  ((float) iWindowDepth) /
  351.                  ((float) (iWindowDepth + pSCI->iCurrentZTranslation));
  352.   pSCI->rcCubeBoundary.left  = (LONG)(fDepthScale* pSCI->rcCubeBoundary.left  );
  353.   pSCI->rcCubeBoundary.right = (LONG)(fDepthScale* pSCI->rcCubeBoundary.right );
  354.   pSCI->rcCubeBoundary.top   = (LONG)(fDepthScale* pSCI->rcCubeBoundary.top   );
  355.   pSCI->rcCubeBoundary.bottom= (LONG)(fDepthScale* pSCI->rcCubeBoundary.bottom);
  356.   for (i = 0; i < NUMVERTICES; i++)
  357.   {
  358.     gXformedVertices[i].x = (LONG) (fDepthScale * gXformedVertices[i].x);
  359.     gXformedVertices[i].y = (LONG) (fDepthScale * gXformedVertices[i].y);
  360.   }
  361.   //
  362.   // If currently in motion then increment the current rotation & tranlation
  363.   //
  364.   if (IN_MOTION(hwnd))
  365.   {
  366.     pSCI->fCurrentXRotation += pSCI->fCurrentXRotationInc;
  367.     pSCI->fCurrentYRotation += pSCI->fCurrentYRotationInc;
  368.     pSCI->fCurrentZRotation += pSCI->fCurrentZRotationInc;
  369.     pSCI->iCurrentXTranslation += pSCI->iCurrentXTranslationInc;
  370.     pSCI->iCurrentYTranslation += pSCI->iCurrentYTranslationInc;
  371.     pSCI->iCurrentZTranslation += pSCI->iCurrentZTranslationInc;
  372.   }
  373.   //
  374.   // Up to this point all coordinates are relative to a window whose
  375.   //   center is at (0,0). Now we'll translate appropriately...
  376.   //
  377.   pSCI->rcCubeBoundary.left   += pWindowRect->right  >> 1;
  378.   pSCI->rcCubeBoundary.right  += pWindowRect->right  >> 1;
  379.   pSCI->rcCubeBoundary.top    += pWindowRect->bottom >> 1;
  380.   pSCI->rcCubeBoundary.bottom += pWindowRect->bottom >> 1;
  381.   for (i = 0; i < NUMVERTICES; i++)
  382.   {
  383.     gXformedVertices[i].x += pWindowRect->right  >> 1;
  384.     gXformedVertices[i].y += pWindowRect->bottom >> 1;
  385.   }
  386.   //
  387.   // Since FillRect's are inclusive-exclusive (there'll be leftovers
  388.   //   from the last cube we drew otherwise)...
  389.   //
  390.   pSCI->rcCubeBoundary.right++;
  391.   pSCI->rcCubeBoundary.bottom++;
  392.   //
  393.   // Finally, adjust the rcCubeBoundary such that it fits entirely within
  394.   //   the acutal control window. The reason for this is that when calling
  395.   //   InvalidateRect from SpincubeWndProccase_WM_TIMER we may get
  396.   //   a different PAINSTRUCT.rcPaint (since InvalidateRect clips the passed
  397.   //   in rect to the window bounds) and our abouve test to memcmp() will
  398.   //   fail.
  399.   //
  400.   if (pSCI->rcCubeBoundary.left < 0)
  401.     pSCI->rcCubeBoundary.left = 0;
  402.   if (pSCI->rcCubeBoundary.top < 0)
  403.     pSCI->rcCubeBoundary.top = 0;
  404.   if (pSCI->rcCubeBoundary.right > pWindowRect->right)
  405.     pSCI->rcCubeBoundary.right = pWindowRect->right;
  406.   if (pSCI->rcCubeBoundary.bottom > pWindowRect->bottom)
  407.     pSCI->rcCubeBoundary.bottom = pWindowRect->bottom;
  408. }
  409. /******************************************************************************
  410. *
  411. *  FUNCTION:    ComputeRotationTransformation
  412. *
  413. *  INPUTS:      fRotationX - Angle to rotate about X axis.
  414. *               fRotationY - Angle to rotate about Y axis.
  415. *               fRotationZ - Angle to rotate about Z axis.
  416. *
  417. *  COMMENTS:    Computes a 3x2 tranformation matrix which rotates about
  418. *               the Z axis, the Y axis, and the X axis, respectively.
  419. *
  420. ******************************************************************************/
  421. void ComputeRotationTransformation (float fRotationX,
  422.                                     float fRotationY,
  423.                                     float fRotationZ)
  424. {
  425.   float sinX, cosX, sinY, cosY, sinZ, cosZ;
  426.   sinX = (float) sin ((double) fRotationX);
  427.   cosX = (float) cos ((double) fRotationX);
  428.   sinY = (float) sin ((double) fRotationY);
  429.   cosY = (float) cos ((double) fRotationY);
  430.   sinZ = (float) sin ((double) fRotationZ);
  431.   cosZ = (float) cos ((double) fRotationZ);
  432.   gM[0][0] =  cosY*cosZ;
  433.   gM[0][1] = -cosY*sinZ;
  434.   gM[0][2] =  sinY;
  435.   gM[1][0] =  sinX*sinY*cosZ + cosX*sinZ;
  436.   gM[1][1] = -sinX*sinY*sinZ + cosX*cosZ;
  437.   gM[1][2] = -sinX*cosY;
  438. }