From 3fa224a2450a99a43c66f5f85951bcadb65430c2 Mon Sep 17 00:00:00 2001 From: dkf Date: Thu, 8 Nov 2012 13:28:49 +0000 Subject: Changes from Simon Geard to act as baseline implementation of TIP #415. --- doc/canvas.n | 29 ++++++++++++-- generic/tkCanvArc.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++----- tests/canvas.test | 6 ++- 3 files changed, 127 insertions(+), 15 deletions(-) diff --git a/doc/canvas.n b/doc/canvas.n index 9eb0ec0..e334e68 100644 --- a/doc/canvas.n +++ b/doc/canvas.n @@ -1314,8 +1314,8 @@ arc's region. .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? @@ -1323,7 +1323,9 @@ Arcs are created with widget commands of the following form: .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 @@ -1364,6 +1366,27 @@ 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: +.CS + +\fIdistance\fR > 0 creates a clockwise arc +\fIdistance\fR < 0 creates an counter-clockwise arc +\fIdistance\fR = 0 creates an arc as if this option had not been specified + +If you want the arc to have a specific radius, r, use the formula + +\fIdistance\fR = r +- sqrt(r**2 - (chordLength/2)**2) + +choosing the minus sign for the minor arc and the plus sign for the major arc. + +Note that \fBitemcget -height\fR always returns 0 so that introspection code can be kept simple. +.CE +.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 diff --git a/generic/tkCanvArc.c b/generic/tkCanvArc.c index 4e4c582..dfa0671 100644 --- a/generic/tkCanvArc.c +++ b/generic/tkCanvArc.c @@ -62,6 +62,10 @@ typedef struct ArcItem { * 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; /* @@ -140,6 +144,8 @@ static const Tk_ConfigSpec configSpecs[] = { "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}, @@ -175,6 +181,7 @@ 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[]); @@ -291,7 +298,9 @@ CreateArc( arcPtr->disabledFillStipple = None; arcPtr->style = PIESLICE_STYLE; arcPtr->fillGC = None; + arcPtr->height = 0; + /* * Process the arguments to fill in the item record. */ @@ -374,6 +383,16 @@ ArcCoords( &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( @@ -447,6 +466,23 @@ ConfigureArc( 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) { @@ -463,15 +499,7 @@ ConfigureArc( } 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; @@ -509,7 +537,7 @@ ConfigureArc( if (arcPtr->disabledFillStipple!=None) { stipple = arcPtr->disabledFillStipple; } - } + } if (arcPtr->style == ARC_STYLE) { newGC = None; @@ -559,6 +587,65 @@ ConfigureArc( /* *-------------------------------------------------------------- * + * 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 -- * * This function is called to clean up the data structure associated with diff --git a/tests/canvas.test b/tests/canvas.test index 2b0da48..5b6c0e4 100644 --- a/tests/canvas.test +++ b/tests/canvas.test @@ -340,8 +340,10 @@ test canvas-8.1 {canvas arc bbox} -setup { 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} -- cgit v0.12