|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.