home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / WIN_NT / MASK2.ZIP / TRACK.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-22  |  17.0 KB  |  473 lines

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