Annotation of mstools/samples/plgblt/track.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.