home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / gdi / winnt / plgblt / track.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  478 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1992-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /**************************************************************************\
  13. *  track.c -- support for direct manipulation of parallelogram object.
  14. \**************************************************************************/
  15.  
  16. #include <windows.h>
  17. #include <math.h>
  18. #include "track.h"
  19.  
  20. #define EPSILON  (float) 0.0001
  21. #define RECTSIZE  60
  22.  
  23.  
  24.  
  25. /**************************************************************************\
  26. *
  27. *  function:  doTrackObject()
  28. *
  29. *  input parameters:
  30. *   pto -  pointer to a track object.
  31. *   msg -  message selecting what action to take.  Values may include WM_*'s
  32. *           (see case statements below for more information.)
  33. *   hwnd - Window handle for the window the track object exists within.
  34. *   lParam - Usually fourth param to window proc. varies based on msg.
  35. *
  36. *  global variables:  none.
  37. *
  38. *  coordinate spaces:  There are three coordinate spaces of interest here,
  39. *   and this routine is frequently switching between them...
  40. *
  41. *           WORLD                   DEVICE                  SCREEN
  42. *
  43. *      object coordinates       input mouse pos       used w/ SetCursorPos()
  44. *         (pto->rect)          (lParam for WM_*)
  45. *
  46. *             ----->  LPtoDP() ---->    ----> ClientToScreen() -->
  47. *             <-----  DPtoLP() <----    <---- ScreenToClient() <--
  48. *
  49. *   in addition, the HDC has an offset origin.  Device coordinates for the
  50. *   mouse (lParam) never take this into account, but it is necessary to
  51. *   translate them in order to get direct manipulation right.
  52. *
  53. \**************************************************************************/
  54. PTrackObject doTrackObject(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
  55. {
  56.   if ((pto == NULL) && (msg != TROB_NEW))  return NULL;
  57.  
  58.   switch (msg) {
  59.  
  60.  
  61.     /**********************************************************************\
  62.     *  TROB_NEW
  63.     *
  64.     * Allocate new PTrackObject structure.  Fill in default values
  65.     *  for the fields of the structure.  Set up the HDC correctly.
  66.     * return - pointer to the new object.
  67.     \**********************************************************************/
  68.     case  TROB_NEW: {
  69.         PTrackObject  pto;
  70.  
  71.         /* with LPTR returned value is a pointer. */
  72.         pto = (PTrackObject) LocalAlloc (LPTR, sizeof (TrackObject));
  73.  
  74.         /* initialize the HDC and other fields. */
  75.         pto->hdc = GetDC (hwnd);
  76.         SetGraphicsMode (pto->hdc, GM_ADVANCED);
  77.         SetROP2(pto->hdc, R2_NOT);
  78.         SelectObject (pto->hdc, GetStockObject (NULL_BRUSH));
  79.         pto->Mode = TMNONE;
  80.         pto->allowedModes = TMMOVE | TMSIZEXY;
  81.  
  82.         GetWorldTransform (pto->hdc, &(pto->xfmChange));
  83.  
  84.         /* initialize the size. */
  85.         pto->rect.top = pto->rect.left = 0;
  86.         pto->rect.bottom = pto->rect.right = RECTSIZE;
  87.  
  88.         return (pto);
  89.     }
  90.  
  91.  
  92.  
  93.     /**********************************************************************\
  94.     *  TROB_DELETE
  95.     *
  96.     * Complement of TROB_NEW.  Free up the memory allocated for the object.
  97.     \**********************************************************************/
  98.     case  TROB_DELETE:
  99.         doTrackObject (pto, TROB_PAINT, hwnd, lParam);
  100.         ReleaseDC (hwnd, pto->hdc);
  101.         LocalFree (LocalHandle ((LPSTR)pto));
  102.     return NULL;
  103.  
  104.  
  105.  
  106.     /**********************************************************************\
  107.     *  TROB_PAINT
  108.     *
  109.     * Paint the object into its hdc.  Called half the time to erase
  110.     *  the object, and half the time to redraw it.
  111.     \**********************************************************************/
  112.     case TROB_PAINT: {
  113.         MoveToEx (pto->hdc, pto->rect.right, pto->rect.top, NULL);
  114.         LineTo (pto->hdc,  pto->rect.left, pto->rect.top);
  115.         LineTo (pto->hdc,  pto->rect.left, pto->rect.bottom);
  116.  
  117.         if (pto->allowedModes & TMSIZEXY) {
  118.           LineTo (pto->hdc,  pto->rect.right, pto->rect.bottom);
  119.           LineTo (pto->hdc,  pto->rect.right, pto->rect.top);
  120.         }
  121.  
  122.         if (pto->allowedModes & TMROTATE) {
  123.           MoveToEx (pto->hdc, pto->rect.left, pto->rect.bottom/ 4, NULL);
  124.           AngleArc (pto->hdc, pto->rect.left, pto->rect.top,
  125.                  (DWORD) pto->rect.bottom/ 4, (float) 270.0, (float) 90.0);
  126.         }
  127.  
  128.     } return NULL;
  129.  
  130.  
  131.  
  132.     /**********************************************************************\
  133.     *  TROB_HITTEST
  134.     *
  135.     * Check the point sent in in the lParam to see if it lays within
  136.     *  the bounds of the objects defining rectangle.
  137.     * return - pointer to the object iff the point is in rectangle,
  138.     *  otherwise return NULL.
  139.     \**********************************************************************/
  140.     case TROB_HITTEST:{
  141.         POINT  mouWorld;
  142.         mouWorld.x = LOWORD(lParam);
  143.         mouWorld.y = HIWORD(lParam);
  144.  
  145.         DPtoLP (pto->hdc, &mouWorld, 1);
  146.  
  147.         if (PtInRect (&pto->rect, mouWorld))  return pto;
  148.         else  return NULL;
  149.     }
  150.  
  151.  
  152.  
  153.     /**********************************************************************\
  154.     *  WM_LBUTTONDOWN &  WM_RBUTTONDOWN
  155.     *
  156.     * Capture the mouse, set the tracking mode depending on the mouse
  157.     *  location in world coordinates, reset the mouse position.
  158.     *
  159.     \**********************************************************************/
  160.     case WM_LBUTTONDOWN:
  161.     case WM_RBUTTONDOWN: {
  162.       POINT  newmouScreen;
  163.       POINT  mouWorld;
  164.  
  165.       mouWorld.x = LOWORD(lParam);
  166.       mouWorld.y = HIWORD(lParam);
  167.       DPtoLP (pto->hdc, &mouWorld, 1);
  168.  
  169.       /* upper left hand corner. right button is no-op. */
  170.       if ((mouWorld.x <= (pto->rect.right  / 2)) &&
  171.           (mouWorld.y <= (pto->rect.bottom / 2))) {
  172.           if (msg == WM_RBUTTONDOWN) return NULL;
  173.           pto->Mode = TMMOVE;
  174.           newmouScreen.x = pto->rect.left;
  175.           newmouScreen.y = pto->rect.top;
  176.  
  177.       /* lower left hand corner */
  178.       } else if ((mouWorld.x <= (pto->rect.right  / 2)) &&
  179.           (mouWorld.y > (pto->rect.bottom / 2))) {
  180.  
  181.           pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARY : TMSIZEY;
  182.           newmouScreen.x = pto->rect.left;
  183.           newmouScreen.y = pto->rect.bottom;
  184.  
  185.       /* upper right hand corner */
  186.       } else if ((mouWorld.x > (pto->rect.right  / 2)) &&
  187.           (mouWorld.y <= (pto->rect.bottom / 2))) {
  188.  
  189.           pto->Mode = (msg == WM_RBUTTONDOWN) ? TMSHEARX : TMSIZEX;
  190.           newmouScreen.x = pto->rect.right;
  191.           newmouScreen.y = pto->rect.top;
  192.  
  193.       /* lower right hand corner */
  194.       } else if ((mouWorld.x > (pto->rect.right  / 2)) &&
  195.           (mouWorld.y > (pto->rect.bottom / 2))) {
  196.  
  197.           pto->Mode = (msg == WM_RBUTTONDOWN) ? TMROTATE : TMSIZEXY;
  198.           newmouScreen.x = pto->rect.right;
  199.           newmouScreen.y = pto->rect.bottom;
  200.       }
  201.  
  202.       if (! (pto->Mode & pto->allowedModes)) {
  203.         pto->Mode = TMNONE;
  204.         return NULL;
  205.       }
  206.  
  207.       SetCapture(hwnd);
  208.       LPtoDP (pto->hdc, &newmouScreen, 1);
  209.       ClientToScreen (hwnd, &newmouScreen);
  210.       SetCursorPos (newmouScreen.x,newmouScreen.y);
  211.  
  212.       GetWorldTransform (pto->hdc, &pto->xfmDown);
  213.     } return NULL;
  214.  
  215.  
  216.  
  217.     /**********************************************************************\
  218.     *  WM_MOUSEMOVE
  219.     *
  220.     * this is where almost all of the interesting calculation is done.
  221.     *  First clip the mouse location to be in rectClip, then
  222.     *  call MouseMove() to handle the different tracking modes.
  223.     \**********************************************************************/
  224.     case WM_MOUSEMOVE: {
  225.       if ((short) LOWORD(lParam) < (short)pto->rectClip.left)
  226.         lParam = MAKELONG ((WORD)pto->rectClip.left, HIWORD(lParam));
  227.  
  228.       if (LOWORD(lParam) > (WORD)pto->rectClip.right)
  229.         lParam = MAKELONG ((WORD)pto->rectClip.right, HIWORD(lParam));
  230.  
  231.       if ((short) HIWORD(lParam) < (short)pto->rectClip.top)
  232.         lParam = MAKELONG (LOWORD(lParam), (WORD)pto->rectClip.top);
  233.  
  234.       if (HIWORD(lParam) > (WORD)pto->rectClip.bottom)
  235.         lParam = MAKELONG (LOWORD(lParam),(WORD)pto->rectClip.bottom);
  236.  
  237.       MouseMove (pto, msg, hwnd, lParam);
  238.  
  239.     } return NULL;
  240.  
  241.  
  242.  
  243.     /**********************************************************************\
  244.     *  WM_RBUTTONUP & WM_LBUTTONUP
  245.     *
  246.     * simply release the mouse capture, and set the mode to TMNONE.
  247.     \**********************************************************************/
  248.     case WM_RBUTTONUP:
  249.     case WM_LBUTTONUP: {
  250.       if (pto->Mode) {
  251.          ReleaseCapture();
  252.          pto->Mode = TMNONE;
  253.       }
  254.     } return NULL;
  255.  
  256.   }  /* end switch(msg) */
  257. }
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264. /**************************************************************************\
  265. *  function:  MouseMove()
  266. *
  267. *  input parameters:
  268. *   pto -  pointer to a track object.
  269. *   msg -  not used.
  270. *   hwnd - Window handle for the window the track object exists within.
  271. *   lParam - Usually fourth param to window proc. varies based on msg.
  272. *
  273. *  The tracking behavior which the user observers when moving the mouse
  274. *   is based on the current tracking mode of the object.  This is usually
  275. *   determined on the mouse down event (c.f. TM*).  First erase the old
  276. *   object, then figure out the change to the transform matrix, finally
  277. *   change the world transform matrix and redraw the object.
  278. *
  279. *  Tranform:
  280. *    (    eM11        eM12        0   )
  281. *    (    eM21        eM22        0   )
  282. *    (    eDx         eDy         1   )
  283. *
  284. *   xDevice = (xWorld * eM11) + (yWorld * eM21) + eDx
  285. *   yDevice = (xWorld * eM12) + (yWorld * eM22) + eDy
  286. *
  287. *   In this routine the Device (mouse location) and World (rectangle corner)
  288. *   points are known.  Therefore, the two equations above are solved for
  289. *   the desired matrix entry value (e.g. eM11, 1M12, ... eDy).  The tracking
  290. *   mode determines which one of these entries may be changed.  E.g. scaling
  291. *   in X modifies eM11 while shearing in X modifies eM12.  So rather than
  292. *   using the world transform to map from world to device points, we are
  293. *   back-computing the proper contents of the world transform.
  294. *
  295. \**************************************************************************/
  296. VOID MouseMove(PTrackObject pto, int msg, HWND hwnd, LONG lParam)
  297. {
  298. POINT  mouWorld, mouDevice, orgDevice;
  299.  
  300.     UNREFERENCED_PARAMETER(msg);
  301.  
  302.     doTrackObject(pto, TROB_PAINT, hwnd, lParam);
  303.     mouDevice.x = mouWorld.x = LOWORD(lParam);
  304.     mouDevice.y = mouWorld.y = HIWORD(lParam);
  305.  
  306.     SetWorldTransform(pto->hdc, &pto->xfmDown);
  307.     DPtoLP (pto->hdc, &mouWorld, 1);
  308.  
  309.     /* offset the mouse device point for the viewport's origin. */
  310.     GetViewportOrgEx (pto->hdc, &orgDevice);
  311.     mouDevice.x -= orgDevice.x;
  312.     mouDevice.y -= orgDevice.y;
  313.  
  314.     GetWorldTransform(pto->hdc, &pto->xfmChange);
  315.  
  316.     switch (pto->Mode) {
  317.       /*******************************************************\
  318.       *    (     1         xShear       0   )
  319.       *    (     0           1          0   )
  320.       *    (     0           0          1   )
  321.       *
  322.       * xWorld = rect.left == 0;
  323.       \*******************************************************/
  324.       case TMSHEARX: {
  325.         pto->xfmChange.eM12 = (float) mouDevice.y;
  326.         pto->xfmChange.eM12 -=pto->xfmChange.eDy;
  327.         pto->xfmChange.eM12 /=(float) pto->rect.right ;
  328.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  329.       } break;
  330.  
  331.  
  332.       /*******************************************************\
  333.       *    (     1           0          0   )
  334.       *    (   yShear        1          0   )
  335.       *    (     0           0          1   )
  336.       *
  337.       * yWorld = rect.top == 0;
  338.       \*******************************************************/
  339.       case TMSHEARY: {
  340.         pto->xfmChange.eM21 = (float) mouDevice.x;
  341.         pto->xfmChange.eM21 -=pto->xfmChange.eDx;
  342.         pto->xfmChange.eM21 /=(float) pto->rect.bottom ;
  343.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  344.  
  345.       } break;
  346.  
  347.  
  348.       /*******************************************************\
  349.       *    (   cos(a)      -sin(a)      0   )
  350.       *    (   sin(a)       cos(a)      0   )
  351.       *    (     0           0          1   )
  352.       *
  353.       * a == rotation angle.  Since mouse in in lower right,
  354.       *  we need to shift this back 45 degrees (assuming that
  355.       *  straight down is 0 degrees).  Thus we actually compute
  356.       *  cos(a) = cos(b - 45) = cos(b)sin(45) + cos(45)sin(45)
  357.       *  where b is angle from the origin to the mouse (x,y)
  358.       *  cos(45) = sin(45) ~= 0.707107
  359.       *  cos(b) = y/r    sin(b) = x/r
  360.       *
  361.       \*******************************************************/
  362.       case TMROTATE: {
  363.         float r;
  364.  
  365.         /* translate back to the origin. */
  366.         pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
  367.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  368.  
  369.         /* rotate about the origin. */
  370.         r = (float) sqrt( (double)(mouWorld.x * mouWorld.x) +
  371.                           (double)(mouWorld.y * mouWorld.y));
  372.  
  373.         pto->xfmChange.eM11 = (float) mouWorld.y / r;
  374.         pto->xfmChange.eM11 += (float) mouWorld.x / r;
  375.         pto->xfmChange.eM11 *= (float) 0.707107;
  376.         pto->xfmChange.eM22 = pto->xfmChange.eM11;
  377.  
  378.         pto->xfmChange.eM12 = (float) mouWorld.y / r;
  379.         pto->xfmChange.eM12 -= (float) mouWorld.x / r;
  380.         pto->xfmChange.eM12 *= (float) 0.707107;
  381.         pto->xfmChange.eM21 = -pto->xfmChange.eM12;
  382.  
  383.         pto->xfmChange.eDx = pto->xfmChange.eDy = (float)0.0;
  384.  
  385.         ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
  386.  
  387.         /* translate back to the original offset. */
  388.         pto->xfmChange.eM11 =
  389.         pto->xfmChange.eM22 = (float) 1.0;
  390.         pto->xfmChange.eM12 =
  391.         pto->xfmChange.eM21 = (float) 0.0;
  392.  
  393.         pto->xfmChange.eDx = pto->xfmDown.eDx;
  394.         pto->xfmChange.eDy = pto->xfmDown.eDy;
  395.         ModifyWorldTransform (pto->hdc, &pto->xfmChange, MWT_RIGHTMULTIPLY);
  396.         GetWorldTransform (pto->hdc, &pto->xfmChange);
  397.       } break;
  398.  
  399.  
  400.       /*******************************************************\
  401.       *    (  Size X         0          0   )
  402.       *    (     0        Size Y        0   )
  403.       *    (     0           0          1   )
  404.       *
  405.       \*******************************************************/
  406.       case TMSIZEXY: {
  407.         pto->xfmChange.eM11 = (float) mouDevice.x;
  408.         pto->xfmChange.eM11 -=pto->xfmChange.eDx;
  409.         pto->xfmChange.eM11 -=((float) pto->rect.bottom*pto->xfmChange.eM21);
  410.         pto->xfmChange.eM11 /=(float) pto->rect.right ;
  411.         if (fabs(pto->xfmChange.eM11) < EPSILON)  // HACK.  system bug ?
  412.            pto->xfmChange.eM11 = EPSILON;
  413.  
  414.         pto->xfmChange.eM22 = (float) mouDevice.y;
  415.         pto->xfmChange.eM22 -=pto->xfmChange.eDy;
  416.         pto->xfmChange.eM22 -=((float) pto->rect.right*pto->xfmChange.eM12);
  417.         pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
  418.         if (fabs(pto->xfmChange.eM22) < EPSILON)  // HACK.  system bug ?
  419.            pto->xfmChange.eM22 = EPSILON;
  420.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  421.       } break;
  422.  
  423.  
  424.       /*******************************************************\
  425.       *    (  Size X         0          0   )
  426.       *    (     0           1          0   )
  427.       *    (     0           0          1   )
  428.       *
  429.       * yWorld = rect.top == 0;
  430.       \*******************************************************/
  431.       case TMSIZEX: {
  432.         pto->xfmChange.eM11 = (float) mouDevice.x;
  433.         pto->xfmChange.eM11 -=pto->xfmChange.eDx;
  434.         pto->xfmChange.eM11 /=(float) pto->rect.right ;
  435.         if (fabs(pto->xfmChange.eM11) < EPSILON)  // HACK.  system bug ?
  436.            pto->xfmChange.eM11 = EPSILON;
  437.  
  438.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  439.       } break;
  440.  
  441.  
  442.       /*******************************************************\
  443.       *    (     1           0          0   )
  444.       *    (     0        Size Y        0   )
  445.       *    (     0           0          1   )
  446.       *
  447.       * xWorld = rect.left == 0;
  448.       \*******************************************************/
  449.       case TMSIZEY: {
  450.         pto->xfmChange.eM22 = (float) mouDevice.y;
  451.         pto->xfmChange.eM22 -=pto->xfmChange.eDy;
  452.         pto->xfmChange.eM22 /=(float) pto->rect.bottom ;
  453.         if (fabs(pto->xfmChange.eM22) < EPSILON)  // HACK.  system bug ?
  454.            pto->xfmChange.eM22 = EPSILON;
  455.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  456.       } break;
  457.  
  458.  
  459.       /*******************************************************\
  460.       *    (     1           0          0   )
  461.       *    (     0           1          0   )
  462.       *    (   Move x      Move y       1   )
  463.       *
  464.       * xWorld = rect.left == 0;
  465.       * yWorld = rect.top == 0;
  466.       \*******************************************************/
  467.       case TMMOVE: {
  468.         pto->xfmChange.eDx = (float) mouDevice.x ;
  469.         pto->xfmChange.eDy = (float) mouDevice.y ;
  470.         SetWorldTransform (pto->hdc, &pto->xfmChange);
  471.       } break;
  472.     } /* end switch */
  473.  
  474.     doTrackObject(pto, TROB_PAINT, hwnd, lParam);
  475.  
  476.     return;
  477.  }
  478.