Index: doc/canvas.n ================================================================== --- doc/canvas.n +++ doc/canvas.n @@ -311,10 +311,11 @@ 5 possible characters .QW "\fB.,-_ \fR" . The space can be used to enlarge the space between other line elements, and cannot occur as the first position in the string. Some examples: +.PP .CS \-dash . \(-> \-dash {2 4} \-dash - \(-> \-dash {6 4} \-dash -. \(-> \-dash {6 4 2 4} \-dash -.. \(-> \-dash {6 4 2 4 2 4} @@ -1335,20 +1336,22 @@ .SH "STANDARD ITEM TYPES" .SS "ARC ITEMS" .PP Items of type \fBarc\fR appear on the display as arc-shaped regions. An arc is a section of an oval delimited by two angles (specified -by the \fB\-start\fR and \fB\-extent\fR options) and displayed in -one of several ways (specified by the \fB\-style\fR option). +by either the \fB\-start\fR and \fB\-extent\fR options or the \fB\-height\fR option) +and displayed in one of several ways (specified by the \fB\-style\fR option). Arcs are created with widget commands of the following form: .CS \fIpathName \fBcreate arc \fIx1 y1 x2 y2 \fR?\fIoption value ...\fR? \fIpathName \fBcreate arc \fIcoordList\fR ?\fIoption value ...\fR? .CE The arguments \fIx1\fR, \fIy1\fR, \fIx2\fR, and \fIy2\fR or \fIcoordList\fR give the coordinates of two diagonally opposite corners of a -rectangular region enclosing the oval that defines the arc. +rectangular region enclosing the oval that defines the arc (except when +\fB\-height\fR is specified - see below). +. After the coordinates there may be any number of \fIoption\fR\-\fIvalue\fR pairs, each of which sets one of the configuration options for the item. These same \fIoption\fR\-\fIvalue\fR pairs may be used in \fBitemconfigure\fR widget commands to change the item's configuration. An arc item becomes the current item when the mouse pointer is @@ -1384,10 +1387,38 @@ \fB\-start \fIdegrees\fR Specifies the beginning of the angular range occupied by the arc. \fIDegrees\fR is given in units of degrees measured counter-clockwise from the 3-o'clock position; it may be either positive or negative. +.TP +\fB\-height \fIdistance\fR +Provides a shortcut for creating a circular arc segment by defining the +distance of the mid-point of the arc from its chord. When this option +is used the coordinates are interpreted as the start and end coordinates +of the chord, and the options \fB\-start\fR and \fB-extent\fR are ignored. +The value of \fIdistance\fR has the following meaning: +.RS +.PP +.RS +\fIdistance\fR > 0 creates a clockwise arc +.br +\fIdistance\fR < 0 creates an counter-clockwise arc +.br +\fIdistance\fR = 0 creates an arc as if this option had not been specified +.RE +.PP +If you want the arc to have a specific radius, \fIr\fR, use the formula: +.PP +.RS +\fIdistance\fR = \fIr\fR \(+- sqrt(\fIr\fR**2 - (chordLength / 2)**2) +.RE +.PP +choosing the minus sign for the minor arc and the plus sign for the major arc. +.PP +Note that \fBitemcget \-height\fR always returns 0 so that introspection code +can be kept simple. +.RE .TP \fB\-style \fItype\fR Specifies how to draw the arc. If \fItype\fR is \fBpieslice\fR (the default) then the arc's region is defined by a section of the oval's perimeter plus two line segments, one between the center Index: generic/tkCanvArc.c ================================================================== --- generic/tkCanvArc.c +++ generic/tkCanvArc.c @@ -60,10 +60,16 @@ GC fillGC; /* Graphics context for filling item. */ double center1[2]; /* Coordinates of center of arc outline at * start (see ComputeArcOutline). */ double center2[2]; /* Coordinates of center of arc outline at * start+extent (see ComputeArcOutline). */ + double height; /* Distance from the arc's chord to its + * mid-point. */ + double startPoint[2]; /* Start point of arc used when specifying + * height. */ + double endPoint[2]; /* End point of arc used when specifying + * height. */ } ArcItem; /* * The definitions below define the sizes of the polygons used to display * outline information for various styles of arcs: @@ -138,10 +144,12 @@ TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, {TK_CONFIG_DOUBLE, "-extent", NULL, NULL, "90", Tk_Offset(ArcItem, extent), TK_CONFIG_DONT_SET_DEFAULT, NULL}, {TK_CONFIG_COLOR, "-fill", NULL, NULL, NULL, Tk_Offset(ArcItem, fillColor), TK_CONFIG_NULL_OK, NULL}, + {TK_CONFIG_DOUBLE, "-height", NULL, NULL, + 0, Tk_Offset(ArcItem, height), TK_CONFIG_DONT_SET_DEFAULT, NULL}, {TK_CONFIG_CUSTOM, "-offset", NULL, NULL, "0,0", Tk_Offset(ArcItem, tsoffset), TK_CONFIG_DONT_SET_DEFAULT, &offsetOption}, {TK_CONFIG_COLOR, "-outline", NULL, NULL, "black", Tk_Offset(ArcItem, outline.color), TK_CONFIG_NULL_OK, NULL}, @@ -173,10 +181,11 @@ static void ComputeArcBbox(Tk_Canvas canvas, ArcItem *arcPtr); static int ConfigureArc(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int objc, Tcl_Obj *const objv[], int flags); +static void ComputeArcFromHeight(ArcItem *arcPtr); static int CreateArc(Tcl_Interp *interp, Tk_Canvas canvas, struct Tk_Item *itemPtr, int objc, Tcl_Obj *const objv[]); static void DeleteArc(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display); @@ -289,10 +298,11 @@ arcPtr->fillStipple = None; arcPtr->activeFillStipple = None; arcPtr->disabledFillStipple = None; arcPtr->style = PIESLICE_STYLE; arcPtr->fillGC = None; + arcPtr->height = 0; /* * Process the arguments to fill in the item record. */ @@ -349,12 +359,12 @@ objs[0] = Tcl_NewDoubleObj(arcPtr->bbox[0]); objs[1] = Tcl_NewDoubleObj(arcPtr->bbox[1]); objs[2] = Tcl_NewDoubleObj(arcPtr->bbox[2]); objs[3] = Tcl_NewDoubleObj(arcPtr->bbox[3]); Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs)); - } else if ((objc == 1)||(objc == 4)) { - if (objc==1) { + } else if ((objc == 1) || (objc == 4)) { + if (objc == 1) { if (Tcl_ListObjGetElements(interp, objv[0], &objc, (Tcl_Obj ***) &objv) != TCL_OK) { return TCL_ERROR; } else if (objc != 4) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -372,10 +382,21 @@ &arcPtr->bbox[2]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[3], &arcPtr->bbox[3]) != TCL_OK)) { return TCL_ERROR; } + + /* + * Store bbox as start and end points so they can be used if either + * radius or height is specified. + */ + + arcPtr->startPoint[0] = arcPtr->bbox[0]; + arcPtr->startPoint[1] = arcPtr->bbox[1]; + arcPtr->endPoint[0] = arcPtr->bbox[2]; + arcPtr->endPoint[1] = arcPtr->bbox[3]; + ComputeArcBbox(canvas, arcPtr); } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "wrong # coordinates: expected 0 or 4, got %d", objc)); Tcl_SetErrorCode(interp, "TK", "CANVAS", "COORDS", "ARC", NULL); @@ -445,10 +466,27 @@ itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT; } else { itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; } + /* + * If either the height is provided then the start and extent will be + * overridden. + */ + if (arcPtr->height != 0) { + ComputeArcFromHeight(arcPtr); + ComputeArcBbox(canvas, arcPtr); + } + + i = (int) (arcPtr->start/360.0); + arcPtr->start -= i*360.0; + if (arcPtr->start < 0) { + arcPtr->start += 360.0; + } + i = (int) (arcPtr->extent/360.0); + arcPtr->extent -= i*360.0; + tsoffset = &arcPtr->outline.tsoffset; flags = tsoffset->flags; if (flags & TK_OFFSET_LEFT) { tsoffset->xoffset = (int) (arcPtr->bbox[0] + 0.5); } else if (flags & TK_OFFSET_CENTER) { @@ -462,18 +500,10 @@ tsoffset->yoffset = (int) ((arcPtr->bbox[1]+arcPtr->bbox[3]+1)/2); } else if (flags & TK_OFFSET_BOTTOM) { tsoffset->yoffset = (int) (arcPtr->bbox[2] + 0.5); } - i = (int) (arcPtr->start/360.0); - arcPtr->start -= i*360.0; - if (arcPtr->start < 0) { - arcPtr->start += 360.0; - } - i = (int) (arcPtr->extent/360.0); - arcPtr->extent -= i*360.0; - mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, &(arcPtr->outline)); if (mask) { gcValues.cap_style = CapButt; mask |= GCCapStyle; newGC = Tk_GetGC(tkwin, mask, &gcValues); @@ -507,11 +537,11 @@ color = arcPtr->disabledFillColor; } if (arcPtr->disabledFillStipple!=None) { stipple = arcPtr->disabledFillStipple; } - } + } if (arcPtr->style == ARC_STYLE) { newGC = None; } else if (color == NULL) { newGC = None; @@ -553,10 +583,94 @@ } ComputeArcBbox(canvas, arcPtr); return TCL_OK; } + +/* + *-------------------------------------------------------------- + * + * ComputeArcFromHeight -- + * + * This function calculates the arc parameters given start-point, + * end-point and height (!= 0). + * + * Results: + * None. + * + * Side effects: + * The height parameter is set to 0 on exit. + * + *-------------------------------------------------------------- + */ + +static void +ComputeArcFromHeight( + ArcItem* arcPtr) +{ + double chordLen, chordDir[2], chordCen[2], arcCen[2], d, radToDeg, radius; + + /* + * The chord. + */ + + chordLen = hypot(arcPtr->endPoint[1] - arcPtr->startPoint[1], + arcPtr->startPoint[0] - arcPtr->endPoint[0]); + chordDir[0] = (arcPtr->endPoint[0] - arcPtr->startPoint[0]) / chordLen; + chordDir[1] = (arcPtr->endPoint[1] - arcPtr->startPoint[1]) / chordLen; + chordCen[0] = (arcPtr->startPoint[0] + arcPtr->endPoint[0]) / 2; + chordCen[1] = (arcPtr->startPoint[1] + arcPtr->endPoint[1]) / 2; + + /* + * Calculate the radius (assumes height != 0). + */ + + radius = (4*pow(arcPtr->height, 2) + pow(chordLen, 2)) + / (8 * arcPtr->height); + + /* + * The arc centre. + */ + + d = radius - arcPtr->height; + arcCen[0] = chordCen[0] - d * chordDir[1]; + arcCen[1] = chordCen[1] + d * chordDir[0]; + + /* + * The arc start and span. Angles are negated because the coordinate + * system is left-handed. + */ + + radToDeg = 45 / atan(1); + arcPtr->start = atan2(arcCen[1] - arcPtr->startPoint[1], + arcPtr->startPoint[0] - arcCen[0]) * radToDeg; + arcPtr->extent = -2 * asin(chordLen / (2 * radius)) * radToDeg; + + /* + * Handle spans > 180. + */ + + if (fabs(2 * arcPtr->height) > chordLen) { + arcPtr->extent = arcPtr->extent > 0 ? (360 - arcPtr->extent) + : -(360 + arcPtr->extent); + } + + /* + * Create the bounding box. + */ + + arcPtr->bbox[0] = arcCen[0] - radius; + arcPtr->bbox[1] = arcCen[1] - radius; + arcPtr->bbox[2] = arcCen[0] + radius; + arcPtr->bbox[3] = arcCen[1] + radius; + + /* + * Set the height to 0 so that itemcget -height returns 0. + */ + + arcPtr->height = 0; +} /* *-------------------------------------------------------------- * * DeleteArc -- Index: tests/canvas.test ================================================================== --- tests/canvas.test +++ tests/canvas.test @@ -338,12 +338,14 @@ set arcBox [.c bbox arc1] .c create arc 100 10 300 210 -start 10 -extent 50 -style chord -tags arc2 set coordBox [.c bbox arc2] .c create arc 300 10 500 210 -start 10 -extent 50 -style pieslice -tags arc3 set pieBox [.c bbox arc3] - list $arcBox $coordBox $pieBox -} -result {{48 21 100 94} {248 21 300 94} {398 21 500 112}} + .c create arc 100 200 300 200 -height [expr {(1-0.5*sqrt(3))*200}] -style arc -tags arc4 + set arcSegBox [.c bbox arc4] + list $arcBox $coordBox $pieBox $arcSegBox +} -result {{48 21 100 94} {248 21 300 94} {398 21 500 112} {98 171 302 202}} test canvas-9.1 {canvas id creation and deletion} -setup { catch {destroy .c} canvas .c } -body {