// HipAnalysis.c #include "HipAnalysis.h" #include #include "utils.h" // // * // * // * // * // * // * CP2 // ********************************* // * * * // * * * // * * * // CP0 * * * CP1 // * * * // * * * // * * * // ********************************* // * CP3 // * // * // * // * // * // * // * // * // * CP4 // // defines #ifndef PI #define PI (3.14159265359) #endif #define MIN_DISTANCE_TO_LINE (2) #define MAX_HEIGHT (100) #define INITIAL_WIDTH (6) #define HANDLE_HEIGHT (2) #define TANGENT_15_DEGREES (0.26179938780) #define TANGENT_75_DEGREES (3.73205080763) //************************************************************************************** // // Functions for HipAnalysis // //************************************************************************************* typedef struct tag_RECTHANDLES { int nCount; ANNPOINT AnnPoints[4]; } RECTHANDLES, *pRECTHANDLES; L_INT L_EXPORT EXT_CALLBACK RectHandlesCallback(HANNOBJECT hObject, pANNHANDLEINFO pHandleInfo, L_VOID L_FAR * pUserData) { pRECTHANDLES pRectHandles = (pRECTHANDLES)pUserData; if (pHandleInfo->nType == ANNHANDLETYPE_DEFAULT_HANDLE) { pRectHandles->AnnPoints[pRectHandles->nCount] = pHandleInfo->ptLocationContainer; pRectHandles->nCount++; } return SUCCESS_NOCHANGE; } L_VOID HipAnalysis_AddRectangleHandle(HANNOBJECT hRect, ANNPOINT pt0, ANNPOINT pt1, pHANDLEDEF pHandleDef) { ANNHANDLE AnnHandle; L_UINT uFlags = ANNHANDLE_LOCATION | ANNHANDLE_CONTAINER_COORDINATES | ANNHANDLE_ID | ANNHANDLE_PEN_COLOR | ANNHANDLE_FILL_COLOR | ANNHANDLE_SHAPE; AnnHandle.uStructSize = sizeof(ANNHANDLE); AnnHandle.uFlags = ANNHANDLE_PEN_COLOR | ANNHANDLE_PEN_COLOR | ANNHANDLE_FILL_COLOR | ANNHANDLE_SHAPE; AnnHandle.nShape = pHandleDef->nShape; AnnHandle.crPen = RGB(0,0,0); AnnHandle.hCursor= NULL; AnnHandle.aptContainer.x = (pt0.x + pt1.x) / 2; AnnHandle.aptContainer.y = (pt0.y + pt1.y) / 2; AnnHandle.nID = pHandleDef->nID; AnnHandle.crFill = pHandleDef->crFill; AnnHandle.uFlags = uFlags; L_AnnAddUserHandle(hRect, &AnnHandle); } L_VOID HipAnalysis_GetNewRotateHandleLocation(HANNOBJECT hLine, pANNPOINT pptCenter, L_INT nType) { ANNPOINT aptLine[2]; HANNOBJECT hContainer; ANNRECT rcContainer; L_DOUBLE dSlope; if (!pptCenter) return; // Find where the rotate line intersects the bottom (or right side) of the annotation container // This is where to place the rotate user handle L_AnnGetPoints(hLine, aptLine); if (aptLine[0].y > aptLine[1].y) { // swap so that aptLine[0] is above aptLine[1] SwapAnnPoints(aptLine + 0, aptLine + 1); } L_AnnGetTopContainer(hLine, &hContainer); L_AnnGetRect(hContainer, &rcContainer, NULL); dSlope = (aptLine[1].y - aptLine[0].y) / (aptLine[1].x - aptLine[0].x); // Assume line intersects bottom of conainer pptCenter->y = rcContainer.bottom-HANDLE_HEIGHT; pptCenter->x = (pptCenter->y - aptLine[1].y)/dSlope + aptLine[1].x; if (nType == 1) { // if x coordinate is greater than the right side of the container, then the handle should lie on right side of container if (pptCenter->x > rcContainer.right) { pptCenter->x = rcContainer.right - HANDLE_HEIGHT; pptCenter->y = dSlope * (pptCenter->x - aptLine[1].x) + aptLine[1].y; } } else // nType == 2 { // if x coordinate is less than the left side of the container, then the handle should lie on left side of container if (pptCenter->x < 0) { pptCenter->x = HANDLE_HEIGHT; pptCenter->y = dSlope * (pptCenter->x - aptLine[1].x) + aptLine[1].y; } } } L_VOID HipAnalysis_AddRotateHandle(HANNOBJECT hLine, pHANDLEDEF pHandleDef, L_INT nType) { L_UINT uFlags = ANNHANDLE_LOCATION | ANNHANDLE_CONTAINER_COORDINATES | ANNHANDLE_ID | ANNHANDLE_PEN_COLOR | ANNHANDLE_FILL_COLOR | ANNHANDLE_SHAPE; ANNHANDLE AnnHandle; AnnHandle.uStructSize = sizeof(ANNHANDLE); AnnHandle.nShape = pHandleDef->nShape; AnnHandle.crPen = RGB(0,0,0); AnnHandle.hCursor= NULL; AnnHandle.nID = pHandleDef->nID; AnnHandle.crFill = pHandleDef->crFill; AnnHandle.uFlags = uFlags; HipAnalysis_GetNewRotateHandleLocation(hLine, &AnnHandle.aptContainer, nType); L_AnnAddUserHandle(hLine, &AnnHandle); } #define HIPANALYSIS_NUMPOINTS 5 HANDLEDEF HipAnalysis_HandleDef[HIPANALYSIS_NUMPOINTS] = { HANDLE_ID_CP0, ANNHANDLE_SHAPE_CIRCLE, RGB(0,0,0), HANDLE_ID_CP1, ANNHANDLE_SHAPE_CIRCLE, RGB(0,255,0), HANDLE_ID_CP2, ANNHANDLE_SHAPE_CIRCLE, RGB(255,0,0), HANDLE_ID_CP3, ANNHANDLE_SHAPE_CIRCLE, RGB(0,0,255), HANDLE_ID_CP4, ANNHANDLE_SHAPE_SQUARE, RGB(255,255,255), }; // 0----------CP2--------3 // | | // CP0 CP1 // | | // 1----------CP3--------2 // L_VOID HipAnalysis_AddUserHandles(HANNOBJECT hObject, L_INT nType) { L_INT nCount; HANNOBJECT AnnObjectNeighbors[2]; // this object is made of one line and one rectangle RECTHANDLES RectHandles; AnnGetNeighborObjects(hObject, AnnObjectNeighbors, &nCount); AnnSortNeighborObjects(hObject, AnnObjectNeighbors, nCount); // Get location of default handles RectHandles.nCount = 0; L_AnnEnumerateHandles(AnnObjectNeighbors[0], RectHandlesCallback, &RectHandles); // Add rectangle user handles at the midpoints of the default handles HipAnalysis_AddRectangleHandle(AnnObjectNeighbors[0], RectHandles.AnnPoints[0], RectHandles.AnnPoints[1], HipAnalysis_HandleDef + 0); HipAnalysis_AddRectangleHandle(AnnObjectNeighbors[0], RectHandles.AnnPoints[1], RectHandles.AnnPoints[2], HipAnalysis_HandleDef + 2); HipAnalysis_AddRectangleHandle(AnnObjectNeighbors[0], RectHandles.AnnPoints[2], RectHandles.AnnPoints[3], HipAnalysis_HandleDef + 1); HipAnalysis_AddRectangleHandle(AnnObjectNeighbors[0], RectHandles.AnnPoints[3], RectHandles.AnnPoints[0], HipAnalysis_HandleDef + 3); HipAnalysis_AddRotateHandle(AnnObjectNeighbors[1], HipAnalysis_HandleDef + 4, nType); } L_VOID HipAnalysis_UpdateRectangleHandle(HANNOBJECT hRect, ANNPOINT apt0, ANNPOINT apt1, L_INT nID) { L_INT nIndex; ANNHANDLE AnnHandle; GetHandleInfo(hRect, nID, NULL, NULL, &nIndex); L_AnnGetUserHandle(hRect, nIndex, &AnnHandle); AnnHandle.aptContainer.x = (apt0.x + apt1.x) / 2; AnnHandle.aptContainer.y = (apt0.y + apt1.y) / 2; AnnHandle.uFlags = ANNHANDLE_LOCATION | ANNHANDLE_CONTAINER_COORDINATES; L_AnnChangeUserHandle(hRect, nIndex, &AnnHandle); } // Arranges the points like so // 0----------CP2--------3 // | | // CP0 CP1 // | | // 1----------CP3--------2 // // nType == 1 // object is as above, but rotated counter clockwise // nType == 2 // object is as above, but rotated clockwise L_VOID ArrangePoints(ANNPOINT aptRect[], L_INT nType) { int nMinX=0; int nMinY=0; int nMaxX=0; int nMaxY=0; ANNPOINT aptMinX, aptMaxX, aptMinY, aptMaxY; int i; for (i=1; i<4; i++) { if (aptRect[i].x < aptRect[nMinX].x) nMinX = i; if (aptRect[i].y < aptRect[nMinY].y) nMinY = i; if (aptRect[i].x > aptRect[nMaxX].x) nMaxX = i; if (aptRect[i].y > aptRect[nMaxY].y) nMaxY = i; } aptMinX = aptRect[nMinX]; aptMaxX = aptRect[nMaxX]; aptMinY = aptRect[nMinY]; aptMaxY = aptRect[nMaxY]; if (nType == 1) { aptRect[0] = aptMinX; aptRect[1] = aptMaxY; aptRect[2] = aptMaxX; aptRect[3] = aptMinY; } else { aptRect[0] = aptMinY; aptRect[1] = aptMinX; aptRect[2] = aptMaxY; aptRect[3] = aptMaxX; } } L_VOID HipAnalysis_UpdateRectangleHandles(HANNOBJECT hRect, L_INT nType) { ANNPOINT aptRect[4]; L_AnnGetPoints(hRect, aptRect); ArrangePoints(aptRect, nType); HipAnalysis_UpdateRectangleHandle(hRect, aptRect[0], aptRect[1], HANDLE_ID_CP0); HipAnalysis_UpdateRectangleHandle(hRect, aptRect[1], aptRect[2], HANDLE_ID_CP2); HipAnalysis_UpdateRectangleHandle(hRect, aptRect[2], aptRect[3], HANDLE_ID_CP1); HipAnalysis_UpdateRectangleHandle(hRect, aptRect[3], aptRect[0], HANDLE_ID_CP3); } L_VOID HipAnalysis_UpdateRotateHandle(HANNOBJECT hLine, L_INT nType) { L_INT nIndex; //L_INT nCount; //HANNOBJECT AnnObjectNeighbors[2]; // this object is made of one line and one rectangle ANNHANDLE AnnHandle; //AnnGetNeighborObjects(hObject, AnnObjectNeighbors, &nCount); //AnnSortNeighborObjects(hObject, AnnObjectNeighbors, nCount); GetHandleInfo(hLine, HANDLE_ID_CP4, NULL, NULL, &nIndex); memset(&AnnHandle, 0, sizeof(ANNHANDLE)); AnnHandle.uStructSize = sizeof(ANNHANDLE); L_AnnGetUserHandle(hLine, nIndex, &AnnHandle); HipAnalysis_GetNewRotateHandleLocation(hLine, &AnnHandle.aptContainer, nType); AnnHandle.uFlags = ANNHANDLE_LOCATION | ANNHANDLE_CONTAINER_COORDINATES; L_AnnChangeUserHandle(hLine, nIndex, &AnnHandle); } //*************** HipAnalysis Create functions // Restricts drawing of HipAnalysis annotation // nType == 1 // * // * * // * * // * * // * * // * // // // nType == 2 // * // * * // * * // * * // * * // * // // Returns TRUE if the point was modified // Returns FALSE if point is unchanged L_BOOL HipAnalysis_AdjustPoint(LPPOINT ppt0, LPPOINT ppt1, L_INT nType) { L_BOOL bRet = FALSE; L_DOUBLE dMinX, dMaxX; L_INT nFactor = 1; int ndy, ndx; if (!ppt0 || !ppt1) return bRet; ndy = ppt1->y - ppt0->y; ndx = ppt1->x - ppt0->x; if (nType == 2) nFactor = -1; if (ndy > 0) { // pt0 is above pt1 dMinX = ndy * TANGENT_15_DEGREES; dMaxX = ndy * TANGENT_75_DEGREES; } else { // pt1 is above pt0 dMinX = ndy / TANGENT_15_DEGREES; dMaxX = ndy / TANGENT_75_DEGREES; } if ((nFactor * ndx) < dMinX) { ppt1->x = ppt0->x + (long)dMinX * nFactor; bRet = TRUE; } else if ( (nFactor * ndx) > dMaxX) { ppt1->x = ppt0->x + (long)dMaxX * nFactor; bRet = TRUE; } return bRet; } // nType is HIP_ANALYSIS_TYPE1 or HIP_ANALYSIS_TYPE2 L_VOID HipAnalysis_LButtonDown(HWND hWnd, LPCHILDDATA pData) { L_AnnAddUndoNode(pData->hAutomation); L_AnnCreateItem(pData->hContainer, ANNOBJECT_LINE, FALSE, &pData->ObjectHipAnalysis.hLineRotate); L_AnnDefine(pData->ObjectHipAnalysis.hLineRotate, &pData->ptStart, ANNDEFINE_BEGINSET); pData->fCapture = TRUE; L_AnnRestrictCursor(pData->hContainer, NULL, NULL, &pData->rcOldClip, TRUE); L_AnnCreateItem(pData->hContainer, ANNOBJECT_LINE, FALSE, &pData->ObjectHipAnalysis.hLine0); L_AnnDefine(pData->ObjectHipAnalysis.hLine0, &pData->ptStart, ANNDEFINE_BEGINSET); L_AnnCreateItem(pData->hContainer, ANNOBJECT_LINE, FALSE, &pData->ObjectHipAnalysis.hLine1); L_AnnDefine(pData->ObjectHipAnalysis.hLine1, &pData->ptStart, ANNDEFINE_BEGINSET); } L_VOID HipAnalysis_MouseMove(LPCHILDDATA pData) { POINT ptNew; HipAnalysis_AdjustPoint(&pData->ptStart, &pData->ptMove, pData->ObjectHipAnalysis.nType); L_AnnDefine(pData->ObjectHipAnalysis.hLineRotate, &pData->ptMove, ANNDEFINE_APPEND); ptNew.x = -(pData->ptMove.y - pData->ptStart.y)/2 + pData->ptStart.x; ptNew.y = (pData->ptMove.x - pData->ptStart.x)/2 + pData->ptStart.y; L_AnnDefine(pData->ObjectHipAnalysis.hLine0, &ptNew, ANNDEFINE_APPEND); ptNew.x = (pData->ptMove.y - pData->ptStart.y)/2 + pData->ptStart.x; ptNew.y = -(pData->ptMove.x - pData->ptStart.x)/2 + pData->ptStart.y; L_AnnDefine(pData->ObjectHipAnalysis.hLine1, &ptNew, ANNDEFINE_APPEND); } L_DOUBLE AnnGetSlope(ANNPOINT apt[]) { L_DOUBLE dSlope; // Swap (if necessary) so that apt[0] is above apt[1] if (apt[0].y > apt[1].y) { ANNPOINT aptTemp; aptTemp = apt[0]; apt[0] = apt[1]; apt[1] = aptTemp; } // This cannot happen--the angle is between 15 and 75 degrees! if (apt[1].x == apt[0].x) dSlope = 0; else dSlope = (apt[1].y - apt[0].y) / (apt[1].x - apt[0].x); return dSlope; } L_DOUBLE AnnGetAngle(ANNPOINT apt[]) { L_DOUBLE dAngle; // Swap (if necessary) so that apt[0] is above apt[1] if (apt[0].y > apt[1].y) { ANNPOINT aptTemp; aptTemp = apt[0]; apt[0] = apt[1]; apt[1] = aptTemp; } dAngle = atan2(apt[1].y - apt[0].y , apt[1].x - apt[0].x); return dAngle; } #define NUM_DIAGONALS (4) // Sets the height of hLine (the main line -- used to rotate the object) // The line height is eight times the height of the diagonal of hContainer // (four diagonals above the center, and four diagonals below the center) // If the length of the line above or below the rotate handle becomes less than two diagonals, // then resize the line L_VOID HipAnalysis_AnnSetLineHeight(HANNOBJECT hLine, HANNOBJECT hContainer, L_INT nType) { L_DOUBLE dAngle; ANNRECT rcContainer; ANNPOINT aptLine[2]; ANNPOINT aptNew[2]; L_DOUBLE dDiagonal; L_DOUBLE dx, dy; ANNPOINT aptTopLeft, aptBottomRight; ANNPOINT aptBottomContainer; L_DOUBLE dDistAbove, dDistBelow, dDistMin; L_AnnGetRect(hContainer, &rcContainer, NULL); aptTopLeft.x = rcContainer.left; aptTopLeft.y = rcContainer.top; aptBottomRight.x = rcContainer.right; aptBottomRight.y = rcContainer.bottom; dDiagonal = MY_DISTANCE(aptTopLeft, aptBottomRight); L_AnnGetPoints(hLine, aptLine); HipAnalysis_GetNewRotateHandleLocation(hLine, &aptBottomContainer, nType); dDistAbove = MY_DISTANCE(aptLine[0], aptBottomContainer); dDistBelow = MY_DISTANCE(aptLine[1], aptBottomContainer); dDistMin = min(dDistAbove, dDistBelow); if (dDistMin < NUM_DIAGONALS * dDiagonal) { //aptCenter = aptLine[0]; // Get angle of line (swapping points if necessary so that apt[0] is above apt[1] dAngle = AnnGetAngle(aptLine); dx = 2 * NUM_DIAGONALS * dDiagonal * cos(dAngle); dy = 2 * NUM_DIAGONALS * dDiagonal * sin(dAngle); aptNew[0].x = aptBottomContainer.x - dx; aptNew[0].y = aptBottomContainer.y - dy; aptNew[1].x = aptBottomContainer.x + dx; aptNew[1].y = aptBottomContainer.y + dy; L_AnnSetPoints(hLine, aptNew, 2); } } L_VOID HipAnalysis_LButtonUp(LPCHILDDATA pData, L_UINT uTool) { ANNRECT rc; HANNOBJECT hRectangle; L_DOUBLE dRectangleWidth; ANNPOINT aptLine[2]; L_DOUBLE dAngle; POINT ptEndLine; HipAnalysis_AdjustPoint(&pData->ptStart, &pData->ptEnd, pData->ObjectHipAnalysis.nType); L_AnnDefine(pData->ObjectHipAnalysis.hLineRotate, &pData->ptEnd, ANNDEFINE_END); ptEndLine.x = -(pData->ptEnd.y - pData->ptStart.y)/2 + pData->ptStart.x; ptEndLine.y = (pData->ptEnd.x - pData->ptStart.x)/2 + pData->ptStart.y; L_AnnDefine(pData->ObjectHipAnalysis.hLine0, &ptEndLine, ANNDEFINE_END); ptEndLine.x = (pData->ptEnd.y - pData->ptStart.y)/2 + pData->ptStart.x; ptEndLine.y = -(pData->ptEnd.x - pData->ptStart.x)/2 + pData->ptStart.y; L_AnnDefine(pData->ObjectHipAnalysis.hLine1, &ptEndLine, ANNDEFINE_END); AnnSetID(pData->ObjectHipAnalysis.hLineRotate, uTool, 1); //Set tag to identify line 0,1,2 HipAnalysis_AnnSetLineHeight(pData->ObjectHipAnalysis.hLineRotate, pData->hContainer, pData->ObjectHipAnalysis.nType); // Get width of line that defines rectangle L_AnnGetPoints(pData->ObjectHipAnalysis.hLine0, aptLine); dRectangleWidth = MY_DISTANCE(aptLine[0], aptLine[1]); // Create the rectangle L_AnnCreateItem(pData->hContainer, ANNOBJECT_RECT, TRUE, &hRectangle); rc.left = aptLine[0].x - dRectangleWidth; rc.right = aptLine[0].x + dRectangleWidth; rc.top = aptLine[0].y - INITIAL_WIDTH; rc.bottom = aptLine[0].y + INITIAL_WIDTH; L_AnnSetRect(hRectangle, &rc); L_AnnGetRotateAngle(pData->ObjectHipAnalysis.hLineRotate, &dAngle); dAngle -= PI/2; L_AnnRotate(hRectangle, dAngle * 180 / PI, NULL, 0); L_AnnSetRestrictToContainer(hRectangle, TRUE, 0); AnnSetID(hRectangle, uTool, 0); //Set tag to identify line 0,1,2 HideDefaultHandles(hRectangle); // Object must be visible to select it L_AnnSetVisible(pData->ObjectHipAnalysis.hLineRotate, TRUE, 0, NULL); L_AnnSetSelected(pData->ObjectHipAnalysis.hLineRotate, TRUE, 0); L_AnnSetSelected(hRectangle, TRUE, 0); HideDefaultHandles(pData->ObjectHipAnalysis.hLineRotate); // End definition of lines that define rectangle // and destroy the line objects L_AnnDestroy(pData->ObjectHipAnalysis.hLine0, 0); L_AnnDestroy(pData->ObjectHipAnalysis.hLine1, 0); // set the default settings from the automation object, and group the objects L_AnnSetAutoDefaults(pData->hAutomation, pData->hContainer, ANNFLAG_SELECTED|ANNFLAG_RECURSE); L_AnnGroup(pData->hContainer, ANNFLAG_SELECTED|ANNFLAG_RECURSE, NULL); L_AnnSetVisible(pData->ObjectHipAnalysis.hLineRotate, TRUE, 0, NULL); L_AnnDumpObject(pData->hContainer); HipAnalysis_AddUserHandles(pData->ObjectHipAnalysis.hLineRotate, pData->ObjectHipAnalysis.nType); DebugClipCursor(&pData->rcOldClip); } //*************** HipAnalysis Update functions L_VOID HipAnalysis_RestrictPoint(HANNOBJECT hObject, HANNOBJECT hObjectNeighbors[], LPCHILDDATA pData, pANNMOUSEPOS pMousePos) { } L_BOOL HipAnalysis_InsideContainerBounds(HANNOBJECT hRect) { L_BOOL bInside; ANNRECT rcRect; ANNRECT rcContainer; HANNOBJECT hContainer; L_AnnGetRect(hRect, &rcRect, NULL); L_AnnGetTopContainer(hRect, &hContainer); L_AnnGetRect(hContainer, &rcContainer, NULL); bInside = (rcRect.left >= rcContainer.left) && (rcRect.right <= rcContainer.right) && (rcRect.top >= rcContainer.top) && (rcRect.bottom <= rcContainer.bottom); return bInside; } // returns distance from ptA to hLine in container coordinates L_DOUBLE HipAnalysis_DistanceToCenterLine(HANNOBJECT hLine, POINT ptA) { HANNOBJECT hContainer; ANNPOINT aptLine[2]; ANNPOINT aptA; L_AnnGetPoints(hLine, aptLine); L_AnnGetTopContainer(hLine, &hContainer); L_AnnConvert(hContainer, &ptA, &aptA, 1, ANNCONVERT_TO_CONTAINER); return DistanceToLine(aptLine, aptA); } POINT HipAnalysis_PointAtDistanceToCenterLine(HANNOBJECT hLine, POINT ptC, L_DOUBLE dDistance) { HANNOBJECT hContainer; ANNPOINT aptLine[2]; //A = aptLine[0], B=aptLine[1] ANNPOINT aptA, aptB, aptC, aptQ; POINT ptQ; L_AnnGetPoints(hLine, aptLine); L_AnnGetTopContainer(hLine, &hContainer); aptA = aptLine[0]; aptB = aptLine[1]; L_AnnConvert(hContainer, &ptC, &aptC, 1, ANNCONVERT_TO_CONTAINER); aptQ = PointAtDistanceToLine(aptLine, aptC, dDistance); L_AnnConvert(hContainer, &ptQ, &aptQ, 1, ANNCONVERT_TO_CLIENT); return ptQ; } // Find the intersection of two lines: // Line0: line passing through the handles CP0, CP1 on hRect // Line1: the rotate line (hLine) L_VOID HipAnalysis_GetRotatePoint(HANNOBJECT hRect, HANNOBJECT hLine, LPPOINT pptRotate) { L_BOOL bRet; ANNPOINT aptHandles[2]; ANNPOINT aptLine[2]; ANNPOINT aptIntersect; HANNOBJECT hContainer; L_DOUBLE b0, b1; // b0 is the y-intercept of line going through handles CP0, CP1 // b1 is y-intercept of rotate line L_DOUBLE m0, m1; // m0 is slope of line going through handles CP0, CP1 // m1 is slope of rotate line if (!pptRotate) return; bRet = GetHandleInfo(hRect, HANDLE_ID_CP0, NULL, &aptHandles[0], NULL); if (!bRet) return; bRet = GetHandleInfo(hRect, HANDLE_ID_CP1, NULL, &aptHandles[1], NULL); if (!bRet) return; L_AnnGetPoints(hLine, aptLine); m0 = AnnGetSlope(aptHandles); m1 = AnnGetSlope(aptLine); b0 = aptHandles[1].y - m0 * aptHandles[1].x; b1 = aptLine[1].y - m1 * aptLine[1].x; aptIntersect.x = (b1 - b0) / (m0 - m1); aptIntersect.y = m0 * aptIntersect.x + b0; // Convert the point from container coordinates to screen coordinates L_AnnGetTopContainer(hLine, &hContainer); L_AnnConvert(hContainer, pptRotate, &aptIntersect, 1, ANNCONVERT_TO_CLIENT); } L_VOID HipAnalysis_Handle_LButtonDown(LPCHILDDATA pData, pANNMOUSEPOS pMousePos) { L_INT32 nCount; AnnGetNeighborObjects( pData->hObjectChange, pData->AnnObjectNeighbors, &nCount); AnnSortNeighborObjects(pData->hObjectChange, pData->AnnObjectNeighbors, nCount); pData->ptStart = pMousePos->pt; switch (pData->nUserHandleID) { case HANDLE_ID_CP0: case HANDLE_ID_CP1: case HANDLE_ID_CP2: case HANDLE_ID_CP3: L_AnnDefine(pData->AnnObjectNeighbors[0], &pMousePos->pt, ANNDEFINE_BEGINRESIZE); pData->ObjectHipAnalysis.nPrevState = STATE_VALID; break; case HANDLE_ID_CP4: { HipAnalysis_GetRotatePoint(pData->AnnObjectNeighbors[0], pData->AnnObjectNeighbors[1], &pData->ObjectHipAnalysis.ptRotate); L_AnnDefine(pData->AnnObjectNeighbors[0], &pData->ObjectHipAnalysis.ptRotate, ANNDEFINE_SETANCHORPOINT); L_AnnDefine(pData->AnnObjectNeighbors[1], &pData->ObjectHipAnalysis.ptRotate, ANNDEFINE_SETANCHORPOINT); L_AnnDefine(pData->AnnObjectNeighbors[0], &pMousePos->pt, ANNDEFINE_BEGINROTATE); L_AnnDefine(pData->AnnObjectNeighbors[1], &pMousePos->pt, ANNDEFINE_BEGINROTATE); } break; } } L_VOID HipAnalysis_Handle_MouseMove(LPCHILDDATA pData, pANNMOUSEPOS pMousePos) { L_DOUBLE dAngle; ANNPOINT aptAnchor, aptMove; POINT ptMove; L_DOUBLE dDistance; L_INT nState; L_AnnGetRotateAngle(pData->hObjectChange, &dAngle); aptAnchor.x = pData->pt0.x; aptAnchor.y = pData->pt0.y; aptMove.x = pMousePos->pt.x; aptMove.y = pMousePos->pt.y; switch (pData->nUserHandleID) { case HANDLE_ID_CP0: case HANDLE_ID_CP1: L_AnnAdjustPoint(&aptAnchor, &aptMove, dAngle, ANNADJUST_HORIZONTAL); ptMove.x = (LONG)aptMove.x; ptMove.y = (LONG)aptMove.y; dDistance = HipAnalysis_DistanceToCenterLine(pData->AnnObjectNeighbors[1],ptMove); // HANDLE_ID_CP0 is to the right of the center line // The distance is always negative--if it goes positive, then we have crossed the line // Therefore, only call L_AnnDefine with HANDLE_ID_CP0 if distance is negative, // or with HANDLE_ID_CP1 when distance is positive nState = ( ((pData->nUserHandleID == HANDLE_ID_CP0) && (dDistance < -MIN_DISTANCE_TO_LINE)) || ((pData->nUserHandleID == HANDLE_ID_CP1) && (dDistance > MIN_DISTANCE_TO_LINE)) ) ? STATE_VALID : STATE_TOO_CLOSE; // if not valid (crossed the center rotate line), set ptMove to be two pixels from the center line if ( (nState != STATE_VALID) && (pData->ObjectHipAnalysis.nPrevState == STATE_VALID) ) { ptMove = HipAnalysis_PointAtDistanceToCenterLine(pData->AnnObjectNeighbors[1], pData->ptStart, MIN_DISTANCE_TO_LINE); } if ( (pData->ObjectHipAnalysis.nPrevState == STATE_VALID) || (nState==STATE_VALID)) L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_APPEND); pData->ObjectHipAnalysis.nPrevState = nState; break; case HANDLE_ID_CP2: case HANDLE_ID_CP3: L_AnnAdjustPoint(&aptAnchor, &aptMove, dAngle, ANNADJUST_VERTICAL); ptMove.x = (LONG)aptMove.x; ptMove.y = (LONG)aptMove.y; dDistance = DistanceUserHandleToLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, ptMove); if (dDistance MAX_HEIGHT) nState = STATE_TOO_FAR; else nState = STATE_VALID; if ((nState==STATE_TOO_CLOSE) && (pData->ObjectHipAnalysis.nPrevState != STATE_TOO_CLOSE)) { ptMove = PointAtDistanceToOppositeLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, MIN_DISTANCE_TO_LINE, pData->ptStart); } if ((nState==STATE_TOO_FAR) && (pData->ObjectHipAnalysis.nPrevState != STATE_TOO_FAR)) { ptMove = PointAtDistanceToOppositeLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, MAX_HEIGHT, pData->ptStart); } if ((nState == STATE_VALID) || (pData->ObjectHipAnalysis.nPrevState != nState) ) L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_APPEND); pData->ObjectHipAnalysis.nPrevState = nState; break; case HANDLE_ID_CP4: ptMove = pMousePos->pt; if (pData->ObjectHipAnalysis.ptRotate.y < ptMove.y) { L_BOOL bAdjusted; L_BOOL bInside = TRUE; bAdjusted = HipAnalysis_AdjustPoint(&pData->ObjectHipAnalysis.ptRotate, &ptMove, pData->ObjectHipAnalysis.nType); if (pData->ObjectHipAnalysis.nPrevState != STATE_ADJUSTED) { L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_APPEND); L_AnnDefine(pData->AnnObjectNeighbors[1], &ptMove, ANNDEFINE_APPEND); pData->ObjectHipAnalysis.ptPrevMove = ptMove; } bInside = TRUE; pData->ObjectHipAnalysis.nPrevState = (bAdjusted || !bInside) ? STATE_ADJUSTED : STATE_VALID; } break; } } L_VOID HipAnalysis_Handle_LButtonUp(LPCHILDDATA pData, pANNMOUSEPOS pMousePos) { L_DOUBLE dAngle; ANNPOINT aptAnchor, aptMove; POINT ptMove; L_INT nType; L_DOUBLE dDistance; L_INT nState; L_AnnGetRotateAngle(pData->hObjectChange, &dAngle); aptAnchor.x = pData->pt0.x; aptAnchor.y = pData->pt0.y; aptMove.x = pMousePos->pt.x; aptMove.y = pMousePos->pt.y; switch (pData->nUserHandleID) { case HANDLE_ID_CP0: case HANDLE_ID_CP1: nType = ((pData->nUserHandleID == HANDLE_ID_CP0) || (pData->nUserHandleID == HANDLE_ID_CP1)) ? ANNADJUST_HORIZONTAL : ANNADJUST_VERTICAL; L_AnnAdjustPoint(&aptAnchor, &aptMove, dAngle, nType); ptMove.x = (LONG)aptMove.x; ptMove.y = (LONG)aptMove.y; dDistance = HipAnalysis_DistanceToCenterLine(pData->AnnObjectNeighbors[1], ptMove); // HANDLE_ID_CP0 is to the right of the center line // The distance is always negative--if it goes positive, then we have crossed the line // Therefore, only call L_AnnDefine with HANDLE_ID_CP0 if distance is negative, // or with HANDLE_ID_CP1 when distance is positive nState = ( ((pData->nUserHandleID == HANDLE_ID_CP0) && (dDistance < -MIN_DISTANCE_TO_LINE)) || ((pData->nUserHandleID == HANDLE_ID_CP1) && (dDistance > MIN_DISTANCE_TO_LINE)) ) ? STATE_VALID : STATE_TOO_CLOSE; // if not valid (crossed the center rotate line), set ptMove to be two pixels from the center line if (nState != STATE_VALID) { ptMove = HipAnalysis_PointAtDistanceToCenterLine(pData->AnnObjectNeighbors[1], pData->ptStart, MIN_DISTANCE_TO_LINE); } L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_END); HipAnalysis_UpdateRectangleHandles(pData->AnnObjectNeighbors[0], pData->ObjectHipAnalysis.nType); break; case HANDLE_ID_CP2: case HANDLE_ID_CP3: L_AnnAdjustPoint(&aptAnchor, &aptMove, dAngle, ANNADJUST_VERTICAL); ptMove.x = (LONG)aptMove.x; ptMove.y = (LONG)aptMove.y; dDistance = DistanceUserHandleToLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, ptMove); if (dDistance < MIN_DISTANCE_TO_LINE) { // STATE_TOO_CLOSE; ptMove = PointAtDistanceToOppositeLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, MIN_DISTANCE_TO_LINE, pData->ptStart); } else if (dDistance > MAX_HEIGHT) { // STATE_TOO_FAR; ptMove = PointAtDistanceToOppositeLine( pData->AnnObjectNeighbors[0], pData->nUserHandleID==HANDLE_ID_CP2?HANDLE_ID_CP3:HANDLE_ID_CP2, MAX_HEIGHT, pData->ptStart); } L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_END); HipAnalysis_UpdateRectangleHandles(pData->AnnObjectNeighbors[0], pData->ObjectHipAnalysis.nType); break; case HANDLE_ID_CP4: ptMove = pMousePos->pt; if (pData->ObjectHipAnalysis.ptRotate.y < ptMove.y) { HipAnalysis_AdjustPoint(&pData->ObjectHipAnalysis.ptRotate, &ptMove, pData->ObjectHipAnalysis.nType); } else ptMove = pData->ObjectHipAnalysis.ptPrevMove; L_AnnDefine(pData->AnnObjectNeighbors[0], &ptMove, ANNDEFINE_END); L_AnnDefine(pData->AnnObjectNeighbors[1], &ptMove, ANNDEFINE_END); HipAnalysis_UpdateRotateHandle(pData->AnnObjectNeighbors[1], pData->ObjectHipAnalysis.nType); break; } } // Updates the rotate handle when the object is moved in automation mode L_VOID HipAnalysis_Move(HANNOBJECT hObject, L_INT nType) { L_UINT uObjectType; L_AnnGetType(hObject, &uObjectType); if (uObjectType == ANNOBJECT_LINE) { HipAnalysis_UpdateRotateHandle(hObject, nType); } } L_VOID HipAnalysis_ClipCursor(LPCHILDDATA pData, HANNOBJECT hObject, POINT ptStart, L_BOOL bRestore) { L_INT32 nCount; ANNRECT arcBounds; RECT rcBounds; AnnGetNeighborObjects( hObject, pData->AnnObjectNeighbors, &nCount); AnnSortNeighborObjects(hObject, pData->AnnObjectNeighbors, nCount); // Restrict only the box--the line is unrestricted GetBoundingBox(pData->AnnObjectNeighbors, 1, &arcBounds); L_AnnConvert(pData->hContainer, (LPPOINT)&rcBounds, (pANNPOINT)&arcBounds, 2, ANNCONVERT_TO_CLIENT); L_AnnRestrictCursor(pData->hContainer, &rcBounds, &ptStart, &pData->rcOldClip, FALSE); }