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