Index: doc/CrtItemType.3 ================================================================== --- doc/CrtItemType.3 +++ doc/CrtItemType.3 @@ -81,10 +81,13 @@ Tk_ItemCursorProc *\fIicursorProc\fR; Tk_ItemSelectionProc *\fIselectionProc\fR; Tk_ItemInsertProc *\fIinsertProc\fR; Tk_ItemDCharsProc *\fIdCharsProc\fR; Tk_ItemType *\fInextPtr\fR; +.VS "8.7, TIP164" + Tk_ItemRotateProc *\fIrotateProc\fR; +.VE "8.7, TIP164" } \fBTk_ItemType\fR; .CE .PP The fields of a Tk_ItemType structure are described in more detail later in this manual entry. @@ -547,10 +550,50 @@ The \fIcanvas\fR and \fIitemPtr\fR arguments have the usual meaning, and \fIdeltaX\fR and \fIdeltaY\fR give the amounts that should be added to each x and y coordinate within the item. The type manager should adjust the item's coordinates and update the bounding box in the item's header. +.SS ROTATEPROC +.VS "8.7, TIP164" +.PP +\fItypePtr\->rotateProc\fR is invoked by Tk to rotate a canvas item +during the \fBrotate\fR widget command. +The procedure must match the following prototype: +.PP +.CS +typedef void \fBTk_ItemRotateProc\fR( + Tk_Canvas \fIcanvas\fR, + Tk_Item *\fIitemPtr\fR, + double \fIoriginX\fR, + double \fIoriginY\fR, + double \fIangleRad\fR); +.CE +.PP +The \fIcanvas\fR and \fIitemPtr\fR arguments have the usual meaning. +\fIoriginX\fR and \fIoriginY\fR specify an origin relative to which +the item is to be rotated, and \fIangleRad\fR gives the anticlockwise +rotation to be applied in radians. +The item should adjust the coordinates of its control points so that where +they used to have coordinates \fIx\fR and \fIy\fR, they will have new +coordinates \fIx\(fm\fR and \fIy\(fm\fR, where +.PP +.CS +\fIrelX\fR = \fIx\fR - \fIoriginX\fR +\fIrelY\fR = \fIy\fR - \fIoriginY\fR +\fIx\(fm\fR = \fIoriginX\fR + \fIrelX\fR \(mu cos(\fIangleRad\fR) + \fIrelY\fR \(mu sin(\fIangleRad\fR) +\fIy\(fm\fR = \fIoriginY\fR \(mi \fIrelX\fR \(mu sin(\fIangleRad\fR) + \fIrelY\fR \(mu cos(\fIangleRad\fR) +.CE +.PP +The control points for an item are not necessarily the coordinates provided to +the item when it is created (or via the \fItypePtr\->coordProc\fR), but could +instead be derived from them. +\fIrotateProc\fR must also update the bounding box in the item's header. +.PP +Item types do not need to provide a \fItypePtr\->rotateProc\fR. If the +\fItypePtr\->rotateProc\fR is NULL, the \fItypePtr\->coordProc\fR will be +used instead to retrieve and update the list of coordinates. +.VE "8.7, TIP164" .SS INDEXPROC .PP \fItypePtr\->indexProc\fR is invoked by Tk to translate a string index specification into a numerical index, for example during the \fBindex\fR widget command. Index: doc/canvas.n ================================================================== --- doc/canvas.n +++ doc/canvas.n @@ -218,12 +218,16 @@ \fBxview\fR and \fByview\fR widget commands; this is typically used for scrolling. Canvases do not support scaling or rotation of the canvas coordinate system relative to the window coordinate system. .PP -Individual items may be moved or scaled using widget commands -described below, but they may not be rotated. +Individual items may be moved, scaled +.VS "8.7, TIP164" +or rotated +.VE "8.7, TIP164" +using widget commands +described below. .PP Note that the default origin of the canvas's visible area is coincident with the origin for the whole window as that makes bindings using the mouse position easier to work with; you only need to use the \fBcanvasx\fR and \fBcanvasy\fR widget commands if you adjust the @@ -672,17 +676,16 @@ be mapped for this widget command to work, but at least one of it's ancestors must be mapped. This command returns an empty string. .TP \fIpathName \fBimove \fItagOrId index x y\fR -.VS 8.6 +. This command causes the \fIindex\fR'th coordinate of each of the items indicated by \fItagOrId\fR to be relocated to the location (\fIx\fR,\fIy\fR). Each item interprets \fIindex\fR independently according to the rules described in \fBINDICES\fR above. Out of the standard set of items, only line and polygon items may have their coordinates relocated this way. -.VE 8.6 .TP \fIpathName \fBindex \fItagOrId index\fR . This command returns a decimal string giving the numerical index within \fItagOrId\fR corresponding to \fIindex\fR. @@ -764,20 +767,19 @@ associated with the item and \fIyAmount\fR to the y-coordinate of each point associated with the item. This command returns an empty string. .TP \fIpathName \fBmoveto \fItagOrId xPos yPos\fR -.VS 8.6 +. Move the items given by \fItagOrId\fR in the canvas coordinate space so that the first coordinate pair of the bottommost item with tag \fItagOrId\fR is located at position (\fIxPos\fR,\fIyPos\fR). \fIxPos\fR and \fIyPos\fR may be the empty string, in which case the corresponding coordinate will be unchanged. All items matching \fItagOrId\fR remain in the same positions relative to each other. This command returns an empty string. -.VE 8.6 .TP \fIpathName \fBpostscript \fR?\fIoption value option value ...\fR? . Generate a Postscript representation for part or all of the canvas. If the \fB\-file\fR option is specified then the Postscript is written @@ -959,20 +961,40 @@ determined by the \fBraise\fR command and \fBlower\fR command, not the \fBraise\fR widget command and \fBlower\fR widget command for canvases. .RE .TP \fIpathName \fBrchars \fItagOrId first last string\fR -.VS 8.6 +. This command causes the text or coordinates between \fIfirst\fR and \fIlast\fR for each of the items indicated by \fItagOrId\fR to be replaced by \fIstring\fR. Each item interprets \fIfirst\fR and \fIlast\fR independently according to the rules described in \fBINDICES\fR above. Out of the standard set of items, text items support this operation by altering their text as directed, and line and polygon items support this operation by altering their coordinate list (in which case \fIstring\fR should be a list of coordinates to use as a replacement). The other items ignore this operation. -.VE 8.6 +.TP +\fIpathName \fBrotate \fItagOrId xOrigin yOrigin angle\fR +.VS "8.7, TIP164" +Rotate the coordinates of all of the items given by \fItagOrId\fR in canvas +coordinate space. +\fIXOrigin\fR and \fIyOrigin\fR identify the origin for the rotation +operation and \fIangle\fR identifies the amount to rotate the coordinates +anticlockwise, in degrees. (Negative values rotate clockwise.) +This command returns an empty string. +.RS +.PP +Implementation note: not all item types work the same with rotations. In +particular,\fB bitmap\fR,\fB image\fR,\fB text\fR and\fB window\fR items only +rotate their anchor points and do not rotate the items themselves about those +points, and the \fBarc\fR, \fBoval\fR and \fBrectangle\fR types rotate about a +computed center point instead of moving the bounding box coordinates directly. +.PP +Some items (currently \fBarc\R and\fB text\fR) have angles in their options; +this command \fIdoes not\fR affect those options. +.RE +.VE "8.7, TIP164" .TP \fIpathName \fBscale \fItagOrId xOrigin yOrigin xScale yScale\fR . Rescale the coordinates of all of the items given by \fItagOrId\fR in canvas coordinate space. @@ -1832,17 +1854,16 @@ \fB\-tags\fR .DE The following extra options are supported for text items: .TP \fB\-angle \fIrotationDegrees\fR -.VS 8.6 +. \fIRotationDegrees\fR tells how many degrees to rotate the text anticlockwise about the positioning point for the text; it may have any floating-point value from 0.0 to 360.0. For example, if \fIrotationDegrees\fR is \fB90\fR, then the text will be drawn vertically from bottom to top. This option defaults to \fB0.0\fR. -.VE 8.6 .TP \fB\-font \fIfontName\fR Specifies the font to use for the text item. \fIFontName\fR may be any string acceptable to \fBTk_GetFont\fR. If this option is not specified, it defaults to a system-dependent Index: generic/tk.h ================================================================== --- generic/tk.h +++ generic/tk.h @@ -1032,10 +1032,12 @@ double *pointPtr); typedef int (Tk_ItemAreaProc)(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr); typedef int (Tk_ItemPostscriptProc)(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass); +typedef void (Tk_ItemRotateProc)(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRadians); typedef void (Tk_ItemScaleProc)(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); typedef void (Tk_ItemTranslateProc)(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -1115,11 +1117,13 @@ * item. */ Tk_ItemDCharsProc *dCharsProc; /* Procedure to delete characters from an * item. */ struct Tk_ItemType *nextPtr;/* Used to link types together into a list. */ - char *reserved1; /* Reserved for future extension. */ + Tk_ItemRotateProc *rotateProc; + /* Procedure to rotate an item's coordinates + * about a point. */ int reserved2; /* Carefully compatible with */ char *reserved3; /* Jan Nijtmans dash patch */ char *reserved4; } Tk_ItemType; Index: generic/tkCanvArc.c ================================================================== --- generic/tkCanvArc.c +++ generic/tkCanvArc.c @@ -212,10 +212,12 @@ double y, double rx, double ry, double start, double extent); static int VertLineToArc(double x, double y1, double y2, double rx, double ry, double start, double extent); +static void RotateArc(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); /* * The structures below defines the arc item types by means of functions that * can be invoked by generic item code. */ @@ -239,11 +241,12 @@ NULL, /* icursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateArc, /* rotateProc */ + 0, NULL, NULL }; /* *-------------------------------------------------------------- * @@ -1485,10 +1488,64 @@ arcPtr->bbox[0] = originX + scaleX*(arcPtr->bbox[0] - originX); arcPtr->bbox[1] = originY + scaleY*(arcPtr->bbox[1] - originY); arcPtr->bbox[2] = originX + scaleX*(arcPtr->bbox[2] - originX); arcPtr->bbox[3] = originY + scaleY*(arcPtr->bbox[3] - originY); + ComputeArcBbox(canvas, arcPtr); +} + +/* + *-------------------------------------------------------------- + * + * RotateArc -- + * + * This function is called to rotate an arc by a given amount. + * + * Results: + * None. + * + * Side effects: + * The position of the arc is rotated by angleRad radians about (originX, + * originY), and the bounding box is updated in the generic part of the + * item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateArc( + Tk_Canvas canvas, + Tk_Item *itemPtr, + double originX, + double originY, + double angleRad) +{ + ArcItem *arcPtr = (ArcItem *) itemPtr; + double newX, newY, oldX, oldY; + + /* + * Compute the centre of the box, then rotate that about the origin. + */ + + newX = oldX = (arcPtr->bbox[0] + arcPtr->bbox[2]) / 2.0; + newY = oldY = (arcPtr->bbox[1] + arcPtr->bbox[3]) / 2.0; + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &newX, &newY); + + /* + * Apply the translation to the box. + */ + + arcPtr->bbox[0] += newX - oldX; + arcPtr->bbox[1] += newY - oldY; + arcPtr->bbox[2] += newX - oldX; + arcPtr->bbox[3] += newY - oldY; + + /* + * TODO: update the arc endpoints? + */ + ComputeArcBbox(canvas, arcPtr); } /* *-------------------------------------------------------------- Index: generic/tkCanvBmap.c ================================================================== --- generic/tkCanvBmap.c +++ generic/tkCanvBmap.c @@ -103,10 +103,12 @@ static void DeleteBitmap(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display); static void DisplayBitmap(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display, Drawable dst, int x, int y, int width, int height); +static void RotateBitmap(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScaleBitmap(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslateBitmap(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -135,11 +137,12 @@ NULL, /* icursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateBitmap, /* rotateProc */ + 0, NULL, NULL }; /* *-------------------------------------------------------------- * @@ -784,10 +787,43 @@ bmapPtr->x = originX + scaleX*(bmapPtr->x - originX); bmapPtr->y = originY + scaleY*(bmapPtr->y - originY); ComputeBitmapBbox(canvas, bmapPtr); } + +/* + *-------------------------------------------------------------- + * + * RotateBitmap -- + * + * This function is called to rotate a bitmap's origin by a given amount. + * + * Results: + * None. + * + * Side effects: + * The position of the bitmap is rotated by angleRad radians about + * (originX, originY), and the bounding box is updated in the generic + * part of the item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateBitmap( + Tk_Canvas canvas, + Tk_Item *itemPtr, + double originX, + double originY, + double angleRad) +{ + BitmapItem *bmapPtr = (BitmapItem *) itemPtr; + + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &bmapPtr->x, &bmapPtr->y); + ComputeBitmapBbox(canvas, bmapPtr); +} /* *-------------------------------------------------------------- * * TranslateBitmap -- Index: generic/tkCanvImg.c ================================================================== --- generic/tkCanvImg.c +++ generic/tkCanvImg.c @@ -92,10 +92,12 @@ static void DeleteImage(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display); static void DisplayImage(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display, Drawable dst, int x, int y, int width, int height); +static void RotateImage(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScaleImage(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslateImage(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -124,11 +126,12 @@ NULL, /* icursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateImage, /* rotateProc */ + 0, NULL, NULL }; /* *-------------------------------------------------------------- * @@ -755,10 +758,44 @@ } return Tk_PostscriptImage(image, interp, canvasWin, ((TkCanvas *) canvas)->psInfo, 0, 0, width, height, prepass); } + +/* + *-------------------------------------------------------------- + * + * RotateImage -- + * + * This function is called to rotate an image's origin by a given amount. + * This does *not* rotate the contents of the image. + * + * Results: + * None. + * + * Side effects: + * The position of the image anchor is rotated by angleRad radians about + * (originX, originY), and the bounding box is updated in the generic + * part of the item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateImage( + Tk_Canvas canvas, + Tk_Item *itemPtr, + double originX, + double originY, + double angleRad) +{ + ImageItem *imgPtr = (ImageItem *) itemPtr; + + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &imgPtr->x, &imgPtr->y); + ComputeImageBbox(canvas, imgPtr); +} /* *-------------------------------------------------------------- * * ScaleImage -- Index: generic/tkCanvLine.c ================================================================== --- generic/tkCanvLine.c +++ generic/tkCanvLine.c @@ -115,10 +115,12 @@ Tcl_Interp *interp, Tk_Window tkwin, const char *value, char *recordPtr, int offset); static const char * PrintArrowShape(ClientData clientData, Tk_Window tkwin, char *recordPtr, int offset, Tcl_FreeProc **freeProcPtr); +static void RotateLine(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScaleLine(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslateLine(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -237,11 +239,12 @@ NULL, /* icursorProc */ NULL, /* selectionProc */ LineInsert, /* insertProc */ LineDeleteCoords, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateLine, /* rotateProc */ + 0, NULL, NULL }; /* * The definition below determines how large are static arrays used to hold * spline points (splines larger than this have to have their arrays @@ -1847,10 +1850,60 @@ for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; i++, coordPtr += 2) { coordPtr[0] += deltaX; coordPtr[1] += deltaY; } + } + ComputeLineBbox(canvas, linePtr); +} + +/* + *-------------------------------------------------------------- + * + * RotateLine -- + * + * This function is called to rotate a line by a given amount about a + * point. + * + * Results: + * None. + * + * Side effects: + * The position of the line is rotated by angleRad about (originX, + * originY), and the bounding box is updated in the generic part of the + * item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateLine( + Tk_Canvas canvas, /* Canvas containing item. */ + Tk_Item *itemPtr, /* Item that is being moved. */ + double originX, double originY, + double angleRad) /* Amount by which item is to be rotated. */ +{ + LineItem *linePtr = (LineItem *) itemPtr; + double *coordPtr; + int i; + double s = sin(angleRad), c = cos(angleRad); + + for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; + i++, coordPtr += 2) { + TkRotatePoint(originX, originY, s, c, &coordPtr[0], &coordPtr[1]); + } + if (linePtr->firstArrowPtr != NULL) { + for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; + i++, coordPtr += 2) { + TkRotatePoint(originX, originY, s, c, &coordPtr[0], &coordPtr[1]); + } + } + if (linePtr->lastArrowPtr != NULL) { + for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; + i++, coordPtr += 2) { + TkRotatePoint(originX, originY, s, c, &coordPtr[0], &coordPtr[1]); + } } ComputeLineBbox(canvas, linePtr); } /* Index: generic/tkCanvPoly.c ================================================================== --- generic/tkCanvPoly.c +++ generic/tkCanvPoly.c @@ -174,10 +174,12 @@ Tk_Item *itemPtr, double *rectPtr); static double PolygonToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *pointPtr); static int PolygonToPostscript(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass); +static void RotatePolygon(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScalePolygon(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslatePolygon(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -200,17 +202,18 @@ PolygonToPoint, /* pointProc */ PolygonToArea, /* areaProc */ PolygonToPostscript, /* postscriptProc */ ScalePolygon, /* scaleProc */ TranslatePolygon, /* translateProc */ - GetPolygonIndex, /* indexProc */ + GetPolygonIndex, /* indexProc */ NULL, /* icursorProc */ NULL, /* selectionProc */ - PolygonInsert, /* insertProc */ + PolygonInsert, /* insertProc */ PolygonDeleteCoords, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotatePolygon, /* rotateProc */ + 0, NULL, NULL }; /* * The definition below determines how large are static arrays used to hold * spline points (splines larger than this have to have their arrays @@ -1732,10 +1735,48 @@ badIndex: Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad index \"%s\"", string)); Tcl_SetErrorCode(interp, "TK", "CANVAS", "ITEM_INDEX", "POLY", NULL); return TCL_ERROR; } + +/* + *-------------------------------------------------------------- + * + * RotatePolygon -- + * + * This function is called to rotate a polygon by a given amount about a + * point. + * + * Results: + * None. + * + * Side effects: + * The position of the polygon is rotated by angleRad about (originX, + * originY), and the bounding box is updated in the generic part of the + * item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotatePolygon( + Tk_Canvas canvas, /* Canvas containing item. */ + Tk_Item *itemPtr, /* Item that is being moved. */ + double originX, double originY, + double angleRad) /* Amount by which item is to be rotated. */ +{ + PolygonItem *polyPtr = (PolygonItem *) itemPtr; + double *coordPtr; + int i; + double s = sin(angleRad), c = cos(angleRad); + + for (i = 0, coordPtr = polyPtr->coordPtr; i < polyPtr->numPoints; + i++, coordPtr += 2) { + TkRotatePoint(originX, originY, s, c, &coordPtr[0], &coordPtr[1]); + } + ComputePolygonBbox(canvas, polyPtr); +} /* *-------------------------------------------------------------- * * TranslatePolygon -- Index: generic/tkCanvText.c ================================================================== --- generic/tkCanvText.c +++ generic/tkCanvText.c @@ -168,10 +168,12 @@ Tk_Item *itemPtr, double *rectPtr); static double TextToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *pointPtr); static int TextToPostscript(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass); +static void RotateText(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void TranslateText(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); /* * The structures below defines the rectangle and oval item types by means of @@ -197,11 +199,12 @@ SetTextCursor, /* icursorProc */ GetSelText, /* selectionProc */ TextInsert, /* insertProc */ TextDeleteChars, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateText, /* rotateProc */ + 0, NULL, NULL }; #define ROUND(d) ((int) floor((d) + 0.5)) /* @@ -1244,10 +1247,43 @@ (int) ((rectPtr[1] + 0.5) - textPtr->drawOrigin[1]), (int) (rectPtr[2] - rectPtr[0] + 0.5), (int) (rectPtr[3] - rectPtr[1] + 0.5), textPtr->angle); } + +/* + *-------------------------------------------------------------- + * + * RotateText -- + * + * This function is called to rotate a text item by a given amount about a + * point. Note that this does *not* rotate the text of the item. + * + * Results: + * None. + * + * Side effects: + * The position of the text anchor is rotated by angleRad about (originX, + * originY), and the bounding box is updated in the generic part of the + * item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateText( + Tk_Canvas canvas, /* Canvas containing item. */ + Tk_Item *itemPtr, /* Item that is being rotated. */ + double originX, double originY, + double angleRad) /* Amount by which item is to be rotated. */ +{ + TextItem *textPtr = (TextItem *) itemPtr; + + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &textPtr->x, &textPtr->y); + ComputeTextBbox(canvas, textPtr); +} /* *-------------------------------------------------------------- * * ScaleText -- Index: generic/tkCanvUtil.c ================================================================== --- generic/tkCanvUtil.c +++ generic/tkCanvUtil.c @@ -1263,11 +1263,10 @@ tsoffset->yoffset += h; return 1; } return 0; } - /* *-------------------------------------------------------------- * * Tk_ResetOutlineGC @@ -1861,13 +1860,50 @@ if (tempArr != staticSpace) { ckfree(tempArr); } return numOutput; } + +/* + *-------------------------------------------------------------- + * + * TkRotatePoint -- + * + * Rotate a point about another point. The angle should be converted into + * its sine and cosine before calling this function. + * + * Results: + * None + * + * Side effects: + * The point in (*xPtr,*yPtr) is updated to be rotated about + * (originX,originY) by the amount given by the sine and cosine of the + * angle to rotate. + * + *-------------------------------------------------------------- + */ + +void +TkRotatePoint( + double originX, double originY, /* The point about which to rotate. */ + double sine, double cosine, /* How much to rotate? */ + double *xPtr, double *yPtr) /* The point to be rotated. (INOUT) */ +{ + double x = *xPtr - originX; + double y = *yPtr - originY; + + /* + * Beware! The canvas coordinate space is flipped vertically, so rotations + * go the "wrong" way with respect to mathematics. + */ + + *xPtr = originX + x * cosine + y * sine; + *yPtr = originY - x * sine + y * cosine; +} /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ Index: generic/tkCanvWind.c ================================================================== --- generic/tkCanvWind.c +++ generic/tkCanvWind.c @@ -75,10 +75,12 @@ static void DeleteWinItem(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display); static void DisplayWinItem(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display, Drawable dst, int x, int y, int width, int height); +static void RotateWinItem(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScaleWinItem(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslateWinItem(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -128,11 +130,12 @@ NULL, /* cursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateWinItem, /* rotateProc */ + 0, NULL, NULL }; /* * The structure below defines the official type record for the canvas (as * geometry manager): @@ -909,10 +912,44 @@ Tcl_DiscardInterpState(interpState); } Tcl_DecrRefCount(psObj); return result; } + +/* + *-------------------------------------------------------------- + * + * RotateWinItem -- + * + * This function is called to rotate a window item by a given amount + * about a point. Note that this does *not* rotate the window of the + * item. + * + * Results: + * None. + * + * Side effects: + * The position of the window anchor is rotated by angleRad about (originX, + * originY), and the bounding box is updated in the generic part of the + * item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateWinItem( + Tk_Canvas canvas, /* Canvas containing item. */ + Tk_Item *itemPtr, /* Item that is being rotated. */ + double originX, double originY, + double angleRad) /* Amount by which item is to be rotated. */ +{ + WindowItem *winItemPtr = (WindowItem *) itemPtr; + + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &winItemPtr->x, &winItemPtr->y); + ComputeWindowBbox(canvas, winItemPtr); +} /* *-------------------------------------------------------------- * * ScaleWinItem -- Index: generic/tkCanvas.c ================================================================== --- generic/tkCanvas.c +++ generic/tkCanvas.c @@ -239,10 +239,13 @@ Tcl_Obj *const *argv); static void CanvasWorldChanged(ClientData instanceData); static int ConfigureCanvas(Tcl_Interp *interp, TkCanvas *canvasPtr, int argc, Tcl_Obj *const *argv, int flags); +static void DefaultRotateImplementation(TkCanvas *canvasPtr, + Tk_Item *itemPtr, double x, double y, + double angleRadians); static void DestroyCanvas(void *memPtr); static int DrawCanvas(Tcl_Interp *interp, ClientData clientData, Tk_PhotoHandle photohandle, int subsample, int zoom); static void DisplayCanvas(ClientData clientData); static void DoItem(Tcl_Obj *accumObj, Tk_Item *itemPtr, Tk_Uid tag); @@ -558,10 +561,106 @@ double yDelta) { itemPtr->typePtr->translateProc((Tk_Canvas) canvasPtr, itemPtr, xDelta, yDelta); } + +static inline void +ItemRotate( + TkCanvas *canvasPtr, + Tk_Item *itemPtr, + double x, + double y, + double angleRadians) +{ + if (itemPtr->typePtr->rotateProc != NULL) { + itemPtr->typePtr->rotateProc((Tk_Canvas) canvasPtr, + itemPtr, x, y, angleRadians); + } else { + DefaultRotateImplementation(canvasPtr, itemPtr, x, y, angleRadians); + } +} + +/* + *-------------------------------------------------------------- + * + * DefaultRotateImplementation -- + * + * The default implementation of the rotation operation, used when items + * do not provide their own version. + * + *-------------------------------------------------------------- + */ + +static void +DefaultRotateImplementation( + TkCanvas *canvasPtr, + Tk_Item *itemPtr, + double x, + double y, + double angleRadians) +{ + int objc, i, ok = 1; + Tcl_Obj **objv, **newObjv; + double *coordv; + double s = sin(angleRadians); + double c = cos(angleRadians); + Tcl_Interp *interp = canvasPtr->interp; + + /* + * Get the coordinates out of the item. + */ + + if (ItemCoords(canvasPtr, itemPtr, 0, NULL) == TCL_OK && + Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), + &objc, &objv) == TCL_OK) { + coordv = (double *) Tcl_Alloc(sizeof(double) * objc); + for (i=0 ; iredraw_flags &= ~TK_ITEM_DONT_REDRAW; } + break; + } + case CANV_ROTATE: { + double x, y, angle; + Tk_Canvas canvas = (Tk_Canvas) canvasPtr; + + if (objc != 6) { + Tcl_WrongNumArgs(interp, 2, objv, "tagOrId x y angle"); + result = TCL_ERROR; + goto done; + } + if (Tk_CanvasGetCoordFromObj(interp, canvas, objv[3], &x) != TCL_OK || + Tk_CanvasGetCoordFromObj(interp, canvas, objv[4], &y) != TCL_OK || + Tcl_GetDoubleFromObj(interp, objv[5], &angle) != TCL_OK) { + result = TCL_ERROR; + goto done; + } + angle = angle * 3.1415927 / 180.0; + FOR_EVERY_CANVAS_ITEM_MATCHING(objv[2], &searchPtr, goto done) { + EventuallyRedrawItem(canvasPtr, itemPtr); + ItemRotate(canvasPtr, itemPtr, x, y, angle); + EventuallyRedrawItem(canvasPtr, itemPtr); + canvasPtr->flags |= REPICK_NEEDED; + } break; } case CANV_SCALE: { double xOrigin, yOrigin, xScale, yScale; Index: generic/tkInt.h ================================================================== --- generic/tkInt.h +++ generic/tkInt.h @@ -1257,10 +1257,13 @@ MODULE_SCOPE void TkpWarpPointer(TkDisplay *dispPtr); MODULE_SCOPE void TkpCancelWarp(TkDisplay *dispPtr); MODULE_SCOPE int TkListCreateFrame(ClientData clientData, Tcl_Interp *interp, Tcl_Obj *listObj, int toplevel, Tcl_Obj *nameObj); +MODULE_SCOPE void TkRotatePoint(double originX, double originY, + double sine, double cosine, double *xPtr, + double *yPtr); #ifdef _WIN32 #define TkParseColor XParseColor #else MODULE_SCOPE Status TkParseColor (Display * display, Index: generic/tkRectOval.c ================================================================== --- generic/tkRectOval.c +++ generic/tkRectOval.c @@ -146,10 +146,12 @@ Tk_Canvas canvas, Tk_Item *itemPtr, int prepass); static int RectToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *areaPtr); static double RectToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *pointPtr); +static void RotateRectOval(Tk_Canvas canvas, Tk_Item *itemPtr, + double originX, double originY, double angleRad); static void ScaleRectOval(Tk_Canvas canvas, Tk_Item *itemPtr, double originX, double originY, double scaleX, double scaleY); static void TranslateRectOval(Tk_Canvas canvas, Tk_Item *itemPtr, double deltaX, double deltaY); @@ -178,11 +180,12 @@ NULL, /* icursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateRectOval, /* rotateProc */ + 0, NULL, NULL }; Tk_ItemType tkOvalType = { "oval", /* name */ sizeof(RectOvalItem), /* itemSize */ @@ -202,11 +205,12 @@ NULL, /* cursorProc */ NULL, /* selectionProc */ NULL, /* insertProc */ NULL, /* dTextProc */ NULL, /* nextPtr */ - NULL, 0, NULL, NULL + RotateRectOval, /* rotateProc */ + 0, NULL, NULL }; /* *-------------------------------------------------------------- * @@ -1279,10 +1283,61 @@ return -1; } } return result; } + +/* + *-------------------------------------------------------------- + * + * RotateRectOval -- + * + * This function is invoked to rotate a rectangle or oval item's + * coordinates. It works by rotating a computed point in the centre of + * the bounding box, NOT by rotating the corners of the bounding box. + * + * Results: + * None. + * + * Side effects: + * The position of the rectangle or oval is rotated by angleRad about + * (originX, originY), and the bounding box is updated in the generic + * part of the item structure. + * + *-------------------------------------------------------------- + */ + +static void +RotateRectOval( + Tk_Canvas canvas, /* Canvas containing rectangle. */ + Tk_Item *itemPtr, /* Rectangle to be scaled. */ + double originX, double originY, + /* Origin about which to rotate rect. */ + double angleRad) /* Amount to scale in X direction. */ +{ + RectOvalItem *rectOvalPtr = (RectOvalItem *) itemPtr; + double newX, newY, oldX, oldY; + + /* + * Compute the centre of the box, then rotate that about the origin. + */ + + newX = oldX = (rectOvalPtr->bbox[0] + rectOvalPtr->bbox[2]) / 2.0; + newY = oldY = (rectOvalPtr->bbox[1] + rectOvalPtr->bbox[3]) / 2.0; + TkRotatePoint(originX, originY, sin(angleRad), cos(angleRad), + &newX, &newY); + + /* + * Apply the translation to the box. + */ + + rectOvalPtr->bbox[0] += newX - oldX; + rectOvalPtr->bbox[1] += newY - oldY; + rectOvalPtr->bbox[2] += newX - oldX; + rectOvalPtr->bbox[3] += newY - oldY; + ComputeRectOvalBbox(canvas, rectOvalPtr); +} /* *-------------------------------------------------------------- * * ScaleRectOval -- Index: tests/canvas.test ================================================================== --- tests/canvas.test +++ tests/canvas.test @@ -1038,10 +1038,193 @@ } -cleanup { destroy .c image delete testimage } -result 1 +destroy .c +test canvas-21.1 {canvas rotate} -setup { + pack [canvas .c] +} -body { + .c create line 50 50 50 100 100 100 + .c rotate all 75 75 90 + lmap c [.c coords all] {format %.2f $c} +} -cleanup { + destroy .c +} -result {50.00 100.00 100.00 100.00 100.00 50.00} +test canvas-21.2 {canvas rotate} -setup { + pack [canvas .c] +} -body { + .c create line 50 50 50 100 100 100 + .c rotate all 75 75 -10 + lmap c [.c coords all] {format %.2f $c} +} -cleanup { + destroy .c +} -result {54.72 46.04 46.04 95.28 95.28 103.96} +test canvas-21.3 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate all 75 75 +} -returnCodes error -cleanup { + destroy .c +} -result {wrong # args: should be ".c rotate tagOrId x y angle"} +test canvas-21.4 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate all 75 75 123 123 +} -returnCodes error -cleanup { + destroy .c +} -result {wrong # args: should be ".c rotate tagOrId x y angle"} +test canvas-21.5 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate {!} 1 1 1 +} -returnCodes error -cleanup { + destroy .c +} -result {missing tag in tag search expression} +test canvas-21.6 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate all x 1 1 +} -returnCodes error -cleanup { + destroy .c +} -result {bad screen distance "x"} +test canvas-21.7 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate all 1 x 1 +} -returnCodes error -cleanup { + destroy .c +} -result {bad screen distance "x"} +test canvas-21.8 {canvas rotate: syntax} -setup { + pack [canvas .c] +} -body { + .c rotate all 1 1 x +} -returnCodes error -cleanup { + destroy .c +} -result {expected floating-point number but got "x"} +test canvas-21.9 {canvas rotate: nothing to rotate} -setup { + pack [canvas .c] +} -body { + .c rotate all 75 75 10 +} -cleanup { + destroy .c +} -result {} +test canvas-21.10 {canvas rotate: multiple things to rotate} -setup { + pack [canvas .c] +} -body { + .c create line 50 50 50 100 -tag a + .c create line 50 50 100 50 -tag b + .c rotate all 75 75 45 + list [lmap c [.c coords a] {format %.2f $c}] [lmap c [.c coords b] {format %.2f $c}] +} -cleanup { + destroy .c +} -result {{39.64 75.00 75.00 110.36} {39.64 75.00 75.00 39.64}} + +test canvas-22.1 {canvas rotate: arc item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create arc 50 50 75 75 -start 45 -extent 90 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {-start -extent} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 125.00 75.00 150.00} {45.0 90.0} {52 123 73 140}} +test canvas-22.2 {canvas rotate: bitmap item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create bitmap 50 50 -bitmap info -anchor se + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {-bitmap -anchor} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 150.00} {info se} {42 129 50 150}} +test canvas-22.3 {canvas rotate: image item rotation behaviour} -setup { + pack [canvas .c] + image create photo dummy -width 50 -height 50 +} -body { + .c create image 50 50 -image dummy -anchor se + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {-image -anchor} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c + image delete dummy +} -result {{50.00 150.00} {dummy se} {0 100 50 150}} +test canvas-22.4 {canvas rotate: line item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create line 50 50 75 50 50 75 75 75 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 150.00 50.00 125.00 75.00 150.00 75.00 125.00} {} {48 123 77 152}} +test canvas-22.5 {canvas rotate: oval item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create oval 50 50 65 85 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{60.00 125.00 75.00 160.00} {} {59 124 76 161}} +test canvas-22.6 {canvas rotate: polygon item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create polygon 50 50 75 50 50 75 75 75 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 150.00 50.00 125.00 75.00 150.00 75.00 125.00} {} {49 124 76 151}} +test canvas-22.7 {canvas rotate: rectangle item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create rectangle 50 50 75 75 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 125.00 75.00 150.00} {} {49 124 76 151}} +test canvas-22.8 {canvas rotate: text item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create text 50 50 -text foo -angle 45 + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {-text -angle} {.c itemcget all $o}] + # [.c bbox all] + # No testing of text bounding box; fonts too variable! +} -cleanup { + destroy .c +} -result {{50.00 150.00} {foo 45.0}} +test canvas-22.9 {canvas rotate: window item rotation behaviour} -setup { + pack [canvas .c] +} -body { + .c create window 50 50 -window [frame .c.f -width 25 -height 25] \ + -anchor se + .c rotate all 100 100 90 + list [lmap c [.c coords all] {format %.2f $c}] \ + [lmap o {} {.c itemcget all $o}] \ + [.c bbox all] +} -cleanup { + destroy .c +} -result {{50.00 150.00} {} {25 125 50 150}} + # cleanup imageCleanup cleanupTests return