|
|
1.1.1.2 ! root 1: ! 2: /******************************************************************************\ ! 3: * This is a part of the Microsoft Source Code Samples. ! 4: * Copyright (C) 1993 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: 1.1 root 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); 1.1.1.2 ! root 76: SetGraphicsMode (pto->hdc, GM_ADVANCED); 1.1 root 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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.