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

unix.superglobalmegacorp.com

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