diff options
Diffstat (limited to 'generic/tkCanvPoly.c')
-rw-r--r-- | generic/tkCanvPoly.c | 1288 |
1 files changed, 1111 insertions, 177 deletions
diff --git a/generic/tkCanvPoly.c b/generic/tkCanvPoly.c index ad5eb80..f64ccd6 100644 --- a/generic/tkCanvPoly.c +++ b/generic/tkCanvPoly.c @@ -9,12 +9,13 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkCanvPoly.c,v 1.3 1999/04/16 01:51:11 stanton Exp $ + * RCS: @(#) $Id: tkCanvPoly.c,v 1.4 1999/12/14 06:52:26 hobbs Exp $ */ #include <stdio.h> #include "tkInt.h" #include "tkPort.h" +#include "tkCanvas.h" /* * The structure below defines the record for each polygon item. @@ -23,7 +24,8 @@ typedef struct PolygonItem { Tk_Item header; /* Generic stuff that's the same for all * types. MUST BE FIRST IN STRUCTURE. */ - int numPoints; /* Number of points in polygon (always >= 3). + Tk_Outline outline; /* Outline structure */ + int numPoints; /* Number of points in polygon. * Polygon is always closed. */ int pointsAllocated; /* Number of points for which space is * allocated at *coordPtr. */ @@ -31,13 +33,16 @@ typedef struct PolygonItem { * x- and y-coords of all points in polygon. * X-coords are even-valued indices, y-coords * are corresponding odd-valued indices. */ - int width; /* Width of outline. */ - XColor *outlineColor; /* Color for outline. */ - GC outlineGC; /* Graphics context for drawing outline. */ + int joinStyle; /* Join style for outline */ + Tk_TSOffset tsoffset; XColor *fillColor; /* Foreground color for polygon. */ + XColor *activeFillColor; /* Foreground color for polygon if state is active. */ + XColor *disabledFillColor; /* Foreground color for polygon if state is disabled. */ Pixmap fillStipple; /* Stipple bitmap for filling polygon. */ + Pixmap activeFillStipple; /* Stipple bitmap for filling polygon if state is active. */ + Pixmap disabledFillStipple; /* Stipple bitmap for filling polygon if state is disabled. */ GC fillGC; /* Graphics context for filling polygon. */ - int smooth; /* Non-zero means draw shape smoothed (i.e. + Tk_SmoothMethod *smooth; /* Non-zero means draw shape smoothed (i.e. * with Bezier splines). */ int splineSteps; /* Number of steps in each spline segment. */ int autoClosed; /* Zero means the given polygon was closed, @@ -48,25 +53,106 @@ typedef struct PolygonItem { * Information used for parsing configuration specs: */ -static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc, +static Tk_CustomOption smoothOption = { + (Tk_OptionParseProc *) TkSmoothParseProc, + TkSmoothPrintProc, (ClientData) NULL +}; +static Tk_CustomOption stateOption = { + (Tk_OptionParseProc *) TkStateParseProc, + TkStatePrintProc, (ClientData) 2 +}; +static Tk_CustomOption tagsOption = { + (Tk_OptionParseProc *) Tk_CanvasTagsParseProc, Tk_CanvasTagsPrintProc, (ClientData) NULL }; +static Tk_CustomOption dashOption = { + (Tk_OptionParseProc *) TkCanvasDashParseProc, + TkCanvasDashPrintProc, (ClientData) NULL +}; +static Tk_CustomOption offsetOption = { + (Tk_OptionParseProc *) TkOffsetParseProc, + TkOffsetPrintProc, + (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX) +}; +static Tk_CustomOption pixelOption = { + (Tk_OptionParseProc *) TkPixelParseProc, + TkPixelPrintProc, (ClientData) NULL +}; static Tk_ConfigSpec configSpecs[] = { + {TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.activeDash), + TK_CONFIG_NULL_OK, &dashOption}, + {TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, activeFillColor), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_COLOR, "-activeoutline", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.activeColor), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_BITMAP, "-activeoutlinestipple", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.activeStipple), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, activeFillStipple), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL, + "0.0", Tk_Offset(PolygonItem, outline.activeWidth), + TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, + {TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.dash), + TK_CONFIG_NULL_OK, &dashOption}, + {TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL, + "0", Tk_Offset(PolygonItem, outline.offset), + TK_CONFIG_DONT_SET_DEFAULT}, + {TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.disabledDash), + TK_CONFIG_NULL_OK, &dashOption}, + {TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, disabledFillColor), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_COLOR, "-disabledoutline", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.disabledColor), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_BITMAP, "-disabledoutlinestipple", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.disabledStipple), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, disabledFillStipple), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL, + "0.0", Tk_Offset(PolygonItem, outline.disabledWidth), + TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL, "black", Tk_Offset(PolygonItem, fillColor), TK_CONFIG_NULL_OK}, + {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL, + "round", Tk_Offset(PolygonItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, + {TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL, + "0,0", Tk_Offset(PolygonItem, tsoffset), + TK_CONFIG_NULL_OK, &offsetOption}, {TK_CONFIG_COLOR, "-outline", (char *) NULL, (char *) NULL, - (char *) NULL, Tk_Offset(PolygonItem, outlineColor), TK_CONFIG_NULL_OK}, - {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL, - "0", Tk_Offset(PolygonItem, smooth), TK_CONFIG_DONT_SET_DEFAULT}, + (char *) NULL, Tk_Offset(PolygonItem, outline.color), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_CUSTOM, "-outlineoffset", (char *) NULL, (char *) NULL, + "0,0", Tk_Offset(PolygonItem, outline.tsoffset), + TK_CONFIG_NULL_OK, &offsetOption}, + {TK_CONFIG_BITMAP, "-outlinestipple", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(PolygonItem, outline.stipple), + TK_CONFIG_NULL_OK}, + {TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL, + "0", Tk_Offset(PolygonItem, smooth), + TK_CONFIG_DONT_SET_DEFAULT, &smoothOption}, {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL, "12", Tk_Offset(PolygonItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT}, + {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, + &stateOption}, {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL, (char *) NULL, Tk_Offset(PolygonItem, fillStipple), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL, (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, - {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL, - "1", Tk_Offset(PolygonItem, width), TK_CONFIG_DONT_SET_DEFAULT}, + {TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL, + "1.0", Tk_Offset(PolygonItem, outline.width), + TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; @@ -79,18 +165,25 @@ static void ComputePolygonBbox _ANSI_ARGS_((Tk_Canvas canvas, PolygonItem *polyPtr)); static int ConfigurePolygon _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int argc, - char **argv, int flags)); + Tcl_Obj *CONST argv[], int flags)); static int CreatePolygon _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, struct Tk_Item *itemPtr, - int argc, char **argv)); + int argc, Tcl_Obj *CONST argv[])); static void DeletePolygon _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)); static void DisplayPolygon _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, Display *display, Drawable dst, int x, int y, int width, int height)); +static int GetPolygonIndex _ANSI_ARGS_((Tcl_Interp *interp, + Tk_Canvas canvas, Tk_Item *itemPtr, + Tcl_Obj *obj, int *indexPtr)); static int PolygonCoords _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, - int argc, char **argv)); + int argc, Tcl_Obj *CONST argv[])); +static void PolygonDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas, + Tk_Item *itemPtr, int first, int last)); +static void PolygonInsert _ANSI_ARGS_((Tk_Canvas canvas, + Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj)); static int PolygonToArea _ANSI_ARGS_((Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)); static double PolygonToPoint _ANSI_ARGS_((Tk_Canvas canvas, @@ -117,18 +210,18 @@ Tk_ItemType tkPolygonType = { PolygonCoords, /* coordProc */ DeletePolygon, /* deleteProc */ DisplayPolygon, /* displayProc */ - 0, /* alwaysRedraw */ + TK_CONFIG_OBJS, /* flags */ PolygonToPoint, /* pointProc */ PolygonToArea, /* areaProc */ PolygonToPostscript, /* postscriptProc */ ScalePolygon, /* scaleProc */ TranslatePolygon, /* translateProc */ - (Tk_ItemIndexProc *) NULL, /* indexProc */ + (Tk_ItemIndexProc *) GetPolygonIndex,/* indexProc */ (Tk_ItemCursorProc *) NULL, /* icursorProc */ (Tk_ItemSelectionProc *) NULL, /* selectionProc */ - (Tk_ItemInsertProc *) NULL, /* insertProc */ - (Tk_ItemDCharsProc *) NULL, /* dTextProc */ - (Tk_ItemType *) NULL /* nextPtr */ + (Tk_ItemInsertProc *) PolygonInsert,/* insertProc */ + PolygonDeleteCoords, /* dTextProc */ + (Tk_ItemType *) NULL, /* nextPtr */ }; /* @@ -167,34 +260,32 @@ CreatePolygon(interp, canvas, itemPtr, argc, argv) Tk_Item *itemPtr; /* Record to hold new item; header * has been initialized by caller. */ int argc; /* Number of arguments in argv. */ - char **argv; /* Arguments describing polygon. */ + Tcl_Obj *CONST argv[]; /* Arguments describing polygon. */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; int i; - if (argc < 6) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tk_PathName(Tk_CanvasTkwin(canvas)), " create ", - itemPtr->typePtr->name, - " x1 y1 x2 y2 x3 y3 ?x4 y4 ...? ?options?\"", (char *) NULL); - return TCL_ERROR; - } - /* * Carry out initialization that is needed in order to clean * up after errors during the the remainder of this procedure. */ + Tk_CreateOutline(&(polyPtr->outline)); polyPtr->numPoints = 0; polyPtr->pointsAllocated = 0; polyPtr->coordPtr = NULL; - polyPtr->width = 1; - polyPtr->outlineColor = NULL; - polyPtr->outlineGC = None; + polyPtr->joinStyle = JoinRound; + polyPtr->tsoffset.flags = 0; + polyPtr->tsoffset.xoffset = 0; + polyPtr->tsoffset.yoffset = 0; polyPtr->fillColor = NULL; + polyPtr->activeFillColor = NULL; + polyPtr->disabledFillColor = NULL; polyPtr->fillStipple = None; + polyPtr->activeFillStipple = None; + polyPtr->disabledFillStipple = None; polyPtr->fillGC = None; - polyPtr->smooth = 0; + polyPtr->smooth = (Tk_SmoothMethod *) NULL; polyPtr->splineSteps = 12; polyPtr->autoClosed = 0; @@ -204,13 +295,14 @@ CreatePolygon(interp, canvas, itemPtr, argc, argv) * start with a digit or a minus sign followed by a digit. */ - for (i = 4; i < (argc-1); i+=2) { - if ((!isdigit(UCHAR(argv[i][0]))) && - ((argv[i][0] != '-') || (!isdigit(UCHAR(argv[i][1]))))) { + for (i = 0; i < argc; i++) { + char *arg = Tcl_GetStringFromObj((Tcl_Obj *) argv[i], NULL); + if ((arg[0] == '-') && (arg[1] >= 'a') + && (arg[1] <= 'z')) { break; } } - if (PolygonCoords(interp, canvas, itemPtr, i, argv) != TCL_OK) { + if (i && PolygonCoords(interp, canvas, itemPtr, i, argv) != TCL_OK) { goto error; } @@ -250,11 +342,10 @@ PolygonCoords(interp, canvas, itemPtr, argc, argv) * read or modified. */ int argc; /* Number of coordinates supplied in * argv. */ - char **argv; /* Array of coordinates: x1, y1, + Tcl_Obj *CONST argv[]; /* Array of coordinates: x1, y1, * x2, y2, ... */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; - char buffer[TCL_DOUBLE_SPACE]; int i, numPoints; if (argc == 0) { @@ -262,16 +353,21 @@ PolygonCoords(interp, canvas, itemPtr, argc, argv) * Print the coords used to create the polygon. If we auto * closed the polygon then we don't report the last point. */ + Tcl_Obj *subobj, *obj = Tcl_NewObj(); for (i = 0; i < 2*(polyPtr->numPoints - polyPtr->autoClosed); i++) { - Tcl_PrintDouble(interp, polyPtr->coordPtr[i], buffer); - Tcl_AppendElement(interp, buffer); + subobj = Tcl_NewDoubleObj(polyPtr->coordPtr[i]); + Tcl_ListObjAppendElement(interp, obj, subobj); } - } else if (argc < 6) { - Tcl_AppendResult(interp, - "too few coordinates for polygon: must have at least 6", - (char *) NULL); - return TCL_ERROR; - } else if (argc & 1) { + Tcl_SetObjResult(interp, obj); + return TCL_OK; + } + if (argc == 1) { + if (Tcl_ListObjGetElements(interp, argv[0], &argc, + (Tcl_Obj ***) &argv) != TCL_OK) { + return TCL_ERROR; + } + } + if (argc & 1) { Tcl_AppendResult(interp, "odd number of coordinates specified for polygon", (char *) NULL); @@ -284,8 +380,8 @@ PolygonCoords(interp, canvas, itemPtr, argc, argv) } /* - * One extra point gets allocated here, just in case we have - * to add another point to close the polygon. + * One extra point gets allocated here, because we always + * add another point to close the polygon. */ polyPtr->coordPtr = (double *) ckalloc((unsigned) @@ -293,20 +389,20 @@ PolygonCoords(interp, canvas, itemPtr, argc, argv) polyPtr->pointsAllocated = numPoints+1; } for (i = argc-1; i >= 0; i--) { - if (Tk_CanvasGetCoord(interp, canvas, argv[i], + if (Tk_CanvasGetCoordFromObj(interp, canvas, argv[i], &polyPtr->coordPtr[i]) != TCL_OK) { return TCL_ERROR; } } polyPtr->numPoints = numPoints; polyPtr->autoClosed = 0; - + /* * Close the polygon if it isn't already closed. */ - if ((polyPtr->coordPtr[argc-2] != polyPtr->coordPtr[0]) - || (polyPtr->coordPtr[argc-1] != polyPtr->coordPtr[1])) { + if (argc>2 && ((polyPtr->coordPtr[argc-2] != polyPtr->coordPtr[0]) + || (polyPtr->coordPtr[argc-1] != polyPtr->coordPtr[1]))) { polyPtr->autoClosed = 1; polyPtr->numPoints++; polyPtr->coordPtr[argc] = polyPtr->coordPtr[0]; @@ -342,7 +438,7 @@ ConfigurePolygon(interp, canvas, itemPtr, argc, argv, flags) Tk_Canvas canvas; /* Canvas containing itemPtr. */ Tk_Item *itemPtr; /* Polygon item to reconfigure. */ int argc; /* Number of elements in argv. */ - char **argv; /* Arguments describing things to configure. */ + Tcl_Obj *CONST argv[]; /* Arguments describing things to configure. */ int flags; /* Flags to pass to Tk_ConfigureWidget. */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; @@ -350,10 +446,13 @@ ConfigurePolygon(interp, canvas, itemPtr, argc, argv, flags) GC newGC; unsigned long mask; Tk_Window tkwin; + XColor *color; + Pixmap stipple; + Tk_State state; tkwin = Tk_CanvasTkwin(canvas); - if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv, - (char *) polyPtr, flags) != TCL_OK) { + if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, (char **) argv, + (char *) polyPtr, flags|TK_CONFIG_OBJS) != TCL_OK) { return TCL_ERROR; } @@ -362,31 +461,66 @@ ConfigurePolygon(interp, canvas, itemPtr, argc, argv, flags) * graphics contexts. */ - if (polyPtr->width < 1) { - polyPtr->width = 1; - } - if (polyPtr->outlineColor == NULL) { - newGC = None; + state = itemPtr->state; + + if (polyPtr->outline.activeWidth > polyPtr->outline.width || + polyPtr->outline.activeDash.number > 0 || + polyPtr->outline.activeColor != NULL || + polyPtr->outline.activeStipple != None || + polyPtr->activeFillColor != NULL || + polyPtr->activeFillStipple != None) { + itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT; } else { - gcValues.foreground = polyPtr->outlineColor->pixel; - gcValues.line_width = polyPtr->width; + itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; + } + + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + if (state==TK_STATE_HIDDEN) { + ComputePolygonBbox(canvas, polyPtr); + return TCL_OK; + } + + mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, &(polyPtr->outline)); + if (mask) { gcValues.cap_style = CapRound; - gcValues.join_style = JoinRound; - mask = GCForeground|GCLineWidth|GCCapStyle|GCJoinStyle; + gcValues.join_style = polyPtr->joinStyle; + mask |= GCCapStyle|GCJoinStyle; newGC = Tk_GetGC(tkwin, mask, &gcValues); + } else { + newGC = None; + } + if (polyPtr->outline.gc != None) { + Tk_FreeGC(Tk_Display(tkwin), polyPtr->outline.gc); } - if (polyPtr->outlineGC != None) { - Tk_FreeGC(Tk_Display(tkwin), polyPtr->outlineGC); + polyPtr->outline.gc = newGC; + + color = polyPtr->fillColor; + stipple = polyPtr->fillStipple; + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->activeFillColor!=NULL) { + color = polyPtr->activeFillColor; + } + if (polyPtr->activeFillStipple!=None) { + stipple = polyPtr->activeFillStipple; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->disabledFillColor!=NULL) { + color = polyPtr->disabledFillColor; + } + if (polyPtr->disabledFillStipple!=None) { + stipple = polyPtr->disabledFillStipple; + } } - polyPtr->outlineGC = newGC; - if (polyPtr->fillColor == NULL) { + if (color == NULL) { newGC = None; } else { - gcValues.foreground = polyPtr->fillColor->pixel; + gcValues.foreground = color->pixel; mask = GCForeground; - if (polyPtr->fillStipple != None) { - gcValues.stipple = polyPtr->fillStipple; + if (stipple != None) { + gcValues.stipple = stipple; gcValues.fill_style = FillStippled; mask |= GCStipple|GCFillStyle; } @@ -437,20 +571,27 @@ DeletePolygon(canvas, itemPtr, display) { PolygonItem *polyPtr = (PolygonItem *) itemPtr; + Tk_DeleteOutline(display,&(polyPtr->outline)); if (polyPtr->coordPtr != NULL) { ckfree((char *) polyPtr->coordPtr); } if (polyPtr->fillColor != NULL) { Tk_FreeColor(polyPtr->fillColor); } + if (polyPtr->activeFillColor != NULL) { + Tk_FreeColor(polyPtr->activeFillColor); + } + if (polyPtr->disabledFillColor != NULL) { + Tk_FreeColor(polyPtr->disabledFillColor); + } if (polyPtr->fillStipple != None) { Tk_FreeBitmap(display, polyPtr->fillStipple); } - if (polyPtr->outlineColor != NULL) { - Tk_FreeColor(polyPtr->outlineColor); + if (polyPtr->activeFillStipple != None) { + Tk_FreeBitmap(display, polyPtr->activeFillStipple); } - if (polyPtr->outlineGC != None) { - Tk_FreeGC(display, polyPtr->outlineGC); + if (polyPtr->disabledFillStipple != None) { + Tk_FreeBitmap(display, polyPtr->disabledFillStipple); } if (polyPtr->fillGC != None) { Tk_FreeGC(display, polyPtr->fillGC); @@ -483,27 +624,159 @@ ComputePolygonBbox(canvas, polyPtr) { double *coordPtr; int i; + double width; + Tk_State state = polyPtr->header.state; + Tk_TSOffset *tsoffset; + + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + width = polyPtr->outline.width; + if (polyPtr->coordPtr == NULL || (polyPtr->numPoints < 1) || (state==TK_STATE_HIDDEN)) { + polyPtr->header.x1 = polyPtr->header.x2 = + polyPtr->header.y1 = polyPtr->header.y2 = -1; + return; + } + if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)polyPtr) { + if (polyPtr->outline.activeWidth>width) { + width = polyPtr->outline.activeWidth; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + width = polyPtr->outline.disabledWidth; + } + } coordPtr = polyPtr->coordPtr; polyPtr->header.x1 = polyPtr->header.x2 = (int) *coordPtr; polyPtr->header.y1 = polyPtr->header.y2 = (int) coordPtr[1]; - for (i = 1, coordPtr = polyPtr->coordPtr+2; i < polyPtr->numPoints; + /* + * Compute the bounding box of all the points in the polygon, + * then expand in all directions by the outline's width to take + * care of butting or rounded corners and projecting or + * rounded caps. This expansion is an overestimate (worst-case + * is square root of two over two) but it's simple. Don't do + * anything special for curves. This causes an additional + * overestimate in the bounding box, but is faster. + */ + + for (i = 1, coordPtr = polyPtr->coordPtr+2; i < polyPtr->numPoints-1; i++, coordPtr += 2) { TkIncludePoint((Tk_Item *) polyPtr, coordPtr); } + tsoffset = &polyPtr->tsoffset; + if (tsoffset->flags & TK_OFFSET_INDEX) { + int index = tsoffset->flags & ~TK_OFFSET_INDEX; + if (tsoffset->flags == INT_MAX) { + index = (polyPtr->numPoints - polyPtr->autoClosed) * 2; + if (index < 0) { + index = 0; + } + } + index %= (polyPtr->numPoints - polyPtr->autoClosed) * 2; + if (index <0) { + index += (polyPtr->numPoints - polyPtr->autoClosed) * 2; + } + tsoffset->xoffset = (int) (polyPtr->coordPtr[index] + 0.5); + tsoffset->yoffset = (int) (polyPtr->coordPtr[index+1] + 0.5); + } else { + if (tsoffset->flags & TK_OFFSET_LEFT) { + tsoffset->xoffset = polyPtr->header.x1; + } else if (tsoffset->flags & TK_OFFSET_CENTER) { + tsoffset->xoffset = (polyPtr->header.x1 + polyPtr->header.x2)/2; + } else if (tsoffset->flags & TK_OFFSET_RIGHT) { + tsoffset->xoffset = polyPtr->header.x2; + } + if (tsoffset->flags & TK_OFFSET_TOP) { + tsoffset->yoffset = polyPtr->header.y1; + } else if (tsoffset->flags & TK_OFFSET_MIDDLE) { + tsoffset->yoffset = (polyPtr->header.y1 + polyPtr->header.y2)/2; + } else if (tsoffset->flags & TK_OFFSET_BOTTOM) { + tsoffset->yoffset = polyPtr->header.y2; + } + } + + if (polyPtr->outline.gc != None) { + tsoffset = &polyPtr->outline.tsoffset; + if (tsoffset) { + if (tsoffset->flags & TK_OFFSET_INDEX) { + int index = tsoffset->flags & ~TK_OFFSET_INDEX; + if (tsoffset->flags == INT_MAX) { + index = (polyPtr->numPoints - 1) * 2; + } + index %= (polyPtr->numPoints - 1) * 2; + if (index <0) { + index += (polyPtr->numPoints - 1) * 2; + } + tsoffset->xoffset = (int) (polyPtr->coordPtr[index] + 0.5); + tsoffset->yoffset = (int) (polyPtr->coordPtr[index+1] + 0.5); + } else { + if (tsoffset->flags & TK_OFFSET_LEFT) { + tsoffset->xoffset = polyPtr->header.x1; + } else if (tsoffset->flags & TK_OFFSET_CENTER) { + tsoffset->xoffset = (polyPtr->header.x1 + polyPtr->header.x2)/2; + } else if (tsoffset->flags & TK_OFFSET_RIGHT) { + tsoffset->xoffset = polyPtr->header.x2; + } + if (tsoffset->flags & TK_OFFSET_TOP) { + tsoffset->yoffset = polyPtr->header.y1; + } else if (tsoffset->flags & TK_OFFSET_MIDDLE) { + tsoffset->yoffset = (polyPtr->header.y1 + polyPtr->header.y2)/2; + } else if (tsoffset->flags & TK_OFFSET_BOTTOM) { + tsoffset->yoffset = polyPtr->header.y2; + } + } + } + + i = (int) ((width+1.5)/2.0); + polyPtr->header.x1 -= i; + polyPtr->header.x2 += i; + polyPtr->header.y1 -= i; + polyPtr->header.y2 += i; + + /* + * For mitered lines, make a second pass through all the points. + * Compute the locations of the two miter vertex points and add + * those into the bounding box. + */ + + if (polyPtr->joinStyle == JoinMiter) { + double miter[4]; + int j; + coordPtr = polyPtr->coordPtr; + if (polyPtr->numPoints>3) { + if (TkGetMiterPoints(coordPtr+2*(polyPtr->numPoints-2), + coordPtr, coordPtr+2, width, + miter, miter+2)) { + for (j = 0; j < 4; j += 2) { + TkIncludePoint((Tk_Item *) polyPtr, miter+j); + } + } + } + for (i = polyPtr->numPoints ; i >= 3; + i--, coordPtr += 2) { + + if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, + width, miter, miter+2)) { + for (j = 0; j < 4; j += 2) { + TkIncludePoint((Tk_Item *) polyPtr, miter+j); + } + } + } + } + } + /* - * Expand bounding box in all directions to account for the outline, - * which can stick out beyond the polygon. Add one extra pixel of - * fudge, just in case X rounds differently than we do. + * Add one more pixel of fudge factor just to be safe (e.g. + * X may round differently than we do). */ - i = (polyPtr->width+1)/2 + 1; - polyPtr->header.x1 -= i; - polyPtr->header.x2 += i; - polyPtr->header.y1 -= i; - polyPtr->header.y2 += i; + polyPtr->header.x1 -= 1; + polyPtr->header.x2 += 1; + polyPtr->header.y1 -= 1; + polyPtr->header.y2 += 1; } /* @@ -567,7 +840,7 @@ TkFillPolygon(canvas, coordPtr, numPoints, display, drawable, gc, outlineGC) * allocated. */ - if (gc != None) { + if (gc != None && numPoints>3) { XFillPolygon(display, drawable, gc, pointPtr, numPoints, Complex, CoordModeOrigin); } @@ -609,24 +882,80 @@ DisplayPolygon(canvas, itemPtr, display, drawable, x, y, width, height) * must be redisplayed (not used). */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; + Tk_State state = itemPtr->state; + Pixmap stipple = polyPtr->fillStipple; + double linewidth = polyPtr->outline.width; - if ((polyPtr->fillGC == None) && (polyPtr->outlineGC == None)) { + if (((polyPtr->fillGC == None) && (polyPtr->outline.gc == None)) || + (polyPtr->numPoints < 1) || + (polyPtr->numPoints < 3 && polyPtr->outline.gc == None)) { return; } + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->outline.activeWidth>linewidth) { + linewidth = polyPtr->outline.activeWidth; + } + if (polyPtr->activeFillStipple != None) { + stipple = polyPtr->activeFillStipple; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + linewidth = polyPtr->outline.disabledWidth; + } + if (polyPtr->disabledFillStipple != None) { + stipple = polyPtr->disabledFillStipple; + } + } /* * If we're stippling then modify the stipple offset in the GC. Be * sure to reset the offset when done, since the GC is supposed to be * read-only. */ - if ((polyPtr->fillStipple != None) && (polyPtr->fillGC != None)) { - Tk_CanvasSetStippleOrigin(canvas, polyPtr->fillGC); + if (stipple != None) { + Tk_TSOffset *tsoffset = &polyPtr->tsoffset; + int w=0; int h=0; + int flags = tsoffset->flags; + if (!(flags & TK_OFFSET_INDEX) && (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) { + Tk_SizeOfBitmap(display, stipple, &w, &h); + if (flags & TK_OFFSET_CENTER) { + w /= 2; + } else { + w = 0; + } + if (flags & TK_OFFSET_MIDDLE) { + h /= 2; + } else { + h = 0; + } + } + tsoffset->xoffset -= w; + tsoffset->yoffset -= h; + Tk_CanvasSetOffset(canvas, polyPtr->fillGC, tsoffset); + tsoffset->xoffset += w; + tsoffset->yoffset += h; } + Tk_ChangeOutlineGC(canvas, itemPtr, &(polyPtr->outline)); - if (!polyPtr->smooth) { + if(polyPtr->numPoints < 3) { + short x,y; + int intLineWidth = (int) (linewidth + 0.5); + if (intLineWidth < 1) { + intLineWidth = 1; + } + Tk_CanvasDrawableCoords(canvas, polyPtr->coordPtr[0], + polyPtr->coordPtr[1], &x,&y); + XFillArc(display, drawable, polyPtr->outline.gc, + x - intLineWidth/2, y - intLineWidth/2, + (unsigned int)intLineWidth+1, (unsigned int)intLineWidth+1, + 0, 64*360); + } else if (!polyPtr->smooth || polyPtr->numPoints < 4) { TkFillPolygon(canvas, polyPtr->coordPtr, polyPtr->numPoints, - display, drawable, polyPtr->fillGC, polyPtr->outlineGC); + display, drawable, polyPtr->fillGC, polyPtr->outline.gc); } else { int numPoints; XPoint staticPoints[MAX_STATIC_POINTS]; @@ -637,29 +966,32 @@ DisplayPolygon(canvas, itemPtr, display, drawable, x, y, width, height) * spline points rather than the original points. */ - numPoints = 1 + polyPtr->numPoints*polyPtr->splineSteps; + numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL, + polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL, + (double *) NULL); if (numPoints <= MAX_STATIC_POINTS) { pointPtr = staticPoints; } else { pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint))); } - numPoints = TkMakeBezierCurve(canvas, polyPtr->coordPtr, + numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr, polyPtr->numPoints, polyPtr->splineSteps, pointPtr, (double *) NULL); if (polyPtr->fillGC != None) { XFillPolygon(display, drawable, polyPtr->fillGC, pointPtr, numPoints, Complex, CoordModeOrigin); } - if (polyPtr->outlineGC != None) { - XDrawLines(display, drawable, polyPtr->outlineGC, pointPtr, + if (polyPtr->outline.gc != None) { + XDrawLines(display, drawable, polyPtr->outline.gc, pointPtr, numPoints, CoordModeOrigin); } if (pointPtr != staticPoints) { ckfree((char *) pointPtr); } } - if ((polyPtr->fillStipple != None) && (polyPtr->fillGC != None)) { + Tk_ResetOutlineGC(canvas, itemPtr, &(polyPtr->outline)); + if (stipple != None) { XSetTSOrigin(display, polyPtr->fillGC, 0, 0); } } @@ -667,6 +999,203 @@ DisplayPolygon(canvas, itemPtr, display, drawable, x, y, width, height) /* *-------------------------------------------------------------- * + * PolygonInsert -- + * + * Insert coords into a polugon item at a given index. + * + * Results: + * None. + * + * Side effects: + * The coords in the given item is modified. + * + *-------------------------------------------------------------- + */ + +static void +PolygonInsert(canvas, itemPtr, beforeThis, obj) + Tk_Canvas canvas; /* Canvas containing text item. */ + Tk_Item *itemPtr; /* Line item to be modified. */ + int beforeThis; /* Index before which new coordinates + * are to be inserted. */ + Tcl_Obj *obj; /* New coordinates to be inserted. */ +{ + PolygonItem *polyPtr = (PolygonItem *) itemPtr; + int length, argc, i; + Tcl_Obj **objv; + double *new; + Tk_State state = itemPtr->state; + + if (state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + + if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &argc, &objv) != TCL_OK) + || !argc || argc&1) { + return; + } + length = 2*(polyPtr->numPoints - polyPtr->autoClosed); + while(beforeThis>length) beforeThis-=length; + while(beforeThis<0) beforeThis+=length; + new = (double *) ckalloc((unsigned)(sizeof(double) * (length + 2 + argc))); + for (i=0; i<beforeThis; i++) { + new[i] = polyPtr->coordPtr[i]; + } + for (i=0; i<argc; i++) { + if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i], + new+(i+beforeThis))!=TCL_OK) { + ckfree((char *) new); + return; + } + } + + for(i=beforeThis; i<length; i++) { + new[i+argc] = polyPtr->coordPtr[i]; + } + if(polyPtr->coordPtr) ckfree((char *) polyPtr->coordPtr); + length+=argc; + polyPtr->coordPtr = new; + polyPtr->numPoints = (length/2) + polyPtr->autoClosed; + + /* + * Close the polygon if it isn't already closed, or remove autoclosing + * if the user's coordinates are now closed. + */ + + if (polyPtr->autoClosed) { + if ((new[length-2] == new[0]) && (new[length-1] == new[1])) { + polyPtr->autoClosed = 0; + polyPtr->numPoints--; + } + } + else { + if ((new[length-2] != new[0]) || (new[length-1] != new[1])) { + polyPtr->autoClosed = 1; + polyPtr->numPoints++; + } + } + + new[length] = new[0]; + new[length+1] = new[1]; + if (((length-argc)>3) && (state != TK_STATE_HIDDEN)) { + /* + * This is some optimizing code that will result that only the part + * of the polygon that changed (and the objects that are overlapping + * with that part) need to be redrawn. A special flag is set that + * instructs the general canvas code not to redraw the whole + * object. If this flag is not set, the canvas will do the redrawing, + * otherwise I have to do it here. + */ + double width; + int j; + itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; + + /* + * The header elements that normally are used for the + * bounding box, are now used to calculate the bounding + * box for only the part that has to be redrawn. That + * doesn't matter, because afterwards the bounding + * box has to be re-calculated anyway. + */ + + itemPtr->x1 = itemPtr->x2 = (int) polyPtr->coordPtr[beforeThis]; + itemPtr->y1 = itemPtr->y2 = (int) polyPtr->coordPtr[beforeThis+1]; + beforeThis-=2; argc+=4; + if(polyPtr->smooth) { + beforeThis-=2; argc+=4; + } /* be carefull; beforeThis could now be negative */ + for(i=beforeThis; i<beforeThis+argc; i+=2) { + j=i; + if(j<0) j+=length; + if(j>=length) j-=length; + TkIncludePoint(itemPtr, polyPtr->coordPtr+j); + } + width = polyPtr->outline.width; + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->outline.activeWidth>width) { + width = polyPtr->outline.activeWidth; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + width = polyPtr->outline.disabledWidth; + } + } + itemPtr->x1 -= (int) width; itemPtr->y1 -= (int) width; + itemPtr->x2 += (int) width; itemPtr->y2 += (int) width; + Tk_CanvasEventuallyRedraw(canvas, + itemPtr->x1, itemPtr->y1, + itemPtr->x2, itemPtr->y2); + } + + ComputePolygonBbox(canvas, polyPtr); +} + +/* + *-------------------------------------------------------------- + * + * PolygonDeleteCoords -- + * + * Delete one or more coordinates from a polygon item. + * + * Results: + * None. + * + * Side effects: + * Characters between "first" and "last", inclusive, get + * deleted from itemPtr. + * + *-------------------------------------------------------------- + */ + +static void +PolygonDeleteCoords(canvas, itemPtr, first, last) + Tk_Canvas canvas; /* Canvas containing itemPtr. */ + Tk_Item *itemPtr; /* Item in which to delete characters. */ + int first; /* Index of first character to delete. */ + int last; /* Index of last character to delete. */ +{ + PolygonItem *polyPtr = (PolygonItem *) itemPtr; + int count, i; + int length = 2*(polyPtr->numPoints - polyPtr->autoClosed); + + while(first>=length) first-=length; + while(first<0) first+=length; + while(last>=length) last-=length; + while(last<0) last+=length; + + first &= -2; + last &= -2; + + count = last + 2 - first; + if(count<=0) count +=length; + + if(count >= length) { + polyPtr->numPoints = 0; + if(polyPtr->coordPtr != NULL) { + ckfree((char *) polyPtr->coordPtr); + } + ComputePolygonBbox(canvas, polyPtr); + return; + } + + if(last>=first) { + for(i=last+2; i<length; i++) { + polyPtr->coordPtr[i-count] = polyPtr->coordPtr[i]; + } + } else { + for(i=last; i<=first; i++) { + polyPtr->coordPtr[i-last] = polyPtr->coordPtr[i]; + } + } + polyPtr->coordPtr[length-count] = polyPtr->coordPtr[0]; + polyPtr->coordPtr[length-count+1] = polyPtr->coordPtr[1]; + polyPtr->numPoints -= count/2; + ComputePolygonBbox(canvas, polyPtr); +} + +/* + *-------------------------------------------------------------- + * * PolygonToPoint -- * * Computes the distance from a given point to a given @@ -692,41 +1221,169 @@ PolygonToPoint(canvas, itemPtr, pointPtr) double *pointPtr; /* Pointer to x and y coordinates. */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; - double *coordPtr, distance; + double *coordPtr, *polyPoints; double staticSpace[2*MAX_STATIC_POINTS]; - int numPoints; + double poly[10]; + double radius; + double bestDist, dist; + int numPoints, count; + int changedMiterToBevel; /* Non-zero means that a mitered corner + * had to be treated as beveled after all + * because the angle was < 11 degrees. */ + double width; + Tk_State state = itemPtr->state; + + bestDist = 1.0e36; + + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + width = polyPtr->outline.width; + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->outline.activeWidth>width) { + width = polyPtr->outline.activeWidth; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + width = polyPtr->outline.disabledWidth; + } + } + radius = width/2.0; - if (!polyPtr->smooth) { - distance = TkPolygonToPoint(polyPtr->coordPtr, polyPtr->numPoints, - pointPtr); - } else { - /* - * Smoothed polygon. Generate a new set of points and use them - * for comparison. - */ - - numPoints = 1 + polyPtr->numPoints*polyPtr->splineSteps; + /* + * Handle smoothed polygons by generating an expanded set of points + * against which to do the check. + */ + + if ((polyPtr->smooth) && (polyPtr->numPoints>2)) { + numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL, + polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL, + (double *) NULL); if (numPoints <= MAX_STATIC_POINTS) { - coordPtr = staticSpace; + polyPoints = staticSpace; } else { - coordPtr = (double *) ckalloc((unsigned) + polyPoints = (double *) ckalloc((unsigned) (2*numPoints*sizeof(double))); } - numPoints = TkMakeBezierCurve(canvas, polyPtr->coordPtr, + numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr, polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL, - coordPtr); - distance = TkPolygonToPoint(coordPtr, numPoints, pointPtr); - if (coordPtr != staticSpace) { - ckfree((char *) coordPtr); + polyPoints); + } else { + numPoints = polyPtr->numPoints; + polyPoints = polyPtr->coordPtr; + } + + bestDist = TkPolygonToPoint(polyPoints, numPoints, pointPtr); + if (bestDist<=0.0) { + goto donepoint; + } + if ((polyPtr->outline.gc != None) && (polyPtr->joinStyle == JoinRound)) { + dist = bestDist - radius; + if (dist <= 0.0) { + bestDist = 0.0; + goto donepoint; + } else { + bestDist = dist; } } - if (polyPtr->outlineColor != NULL) { - distance -= polyPtr->width/2.0; - if (distance < 0) { - distance = 0; + + if ((polyPtr->outline.gc == None) || (width <= 1)) goto donepoint; + + /* + * The overall idea is to iterate through all of the edges of + * the line, computing a polygon for each edge and testing the + * point against that polygon. In addition, there are additional + * tests to deal with rounded joints and caps. + */ + + changedMiterToBevel = 0; + for (count = numPoints, coordPtr = polyPoints; count >= 2; + count--, coordPtr += 2) { + + /* + * If rounding is done around the first point then compute + * the distance between the point and the point. + */ + + if (polyPtr->joinStyle == JoinRound) { + dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) + - radius; + if (dist <= 0.0) { + bestDist = 0.0; + goto donepoint; + } else if (dist < bestDist) { + bestDist = dist; + } } + + /* + * Compute the polygonal shape corresponding to this edge, + * consisting of two points for the first point of the edge + * and two points for the last point of the edge. + */ + + if (count == numPoints) { + TkGetButtPoints(coordPtr+2, coordPtr, (double) width, + 0, poly, poly+2); + } else if ((polyPtr->joinStyle == JoinMiter) && !changedMiterToBevel) { + poly[0] = poly[6]; + poly[1] = poly[7]; + poly[2] = poly[4]; + poly[3] = poly[5]; + } else { + TkGetButtPoints(coordPtr+2, coordPtr, (double) width, 0, + poly, poly+2); + + /* + * If this line uses beveled joints, then check the distance + * to a polygon comprising the last two points of the previous + * polygon and the first two from this polygon; this checks + * the wedges that fill the mitered joint. + */ + + if ((polyPtr->joinStyle == JoinBevel) || changedMiterToBevel) { + poly[8] = poly[0]; + poly[9] = poly[1]; + dist = TkPolygonToPoint(poly, 5, pointPtr); + if (dist <= 0.0) { + bestDist = 0.0; + goto donepoint; + } else if (dist < bestDist) { + bestDist = dist; + } + changedMiterToBevel = 0; + } + } + if (count == 2) { + TkGetButtPoints(coordPtr, coordPtr+2, (double) width, + 0, poly+4, poly+6); + } else if (polyPtr->joinStyle == JoinMiter) { + if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, + (double) width, poly+4, poly+6) == 0) { + changedMiterToBevel = 1; + TkGetButtPoints(coordPtr, coordPtr+2, (double) width, + 0, poly+4, poly+6); + } + } else { + TkGetButtPoints(coordPtr, coordPtr+2, (double) width, 0, + poly+4, poly+6); + } + poly[8] = poly[0]; + poly[9] = poly[1]; + dist = TkPolygonToPoint(poly, 5, pointPtr); + if (dist <= 0.0) { + bestDist = 0.0; + goto donepoint; + } else if (dist < bestDist) { + bestDist = dist; + } + } + + donepoint: + if ((polyPoints != staticSpace) && polyPoints != polyPtr->coordPtr) { + ckfree((char *) polyPoints); } - return distance; + return bestDist; } /* @@ -759,75 +1416,179 @@ PolygonToArea(canvas, itemPtr, rectPtr) * area. */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; - double *coordPtr, rect2[4], halfWidth; + double *coordPtr; double staticSpace[2*MAX_STATIC_POINTS]; - int numPoints, result; + double *polyPoints, poly[10]; + double radius; + int numPoints, count; + int changedMiterToBevel; /* Non-zero means that a mitered corner + * had to be treated as beveled after all + * because the angle was < 11 degrees. */ + int inside; /* Tentative guess about what to return, + * based on all points seen so far: one + * means everything seen so far was + * inside the area; -1 means everything + * was outside the area. 0 means overlap + * has been found. */ + double width; + Tk_State state = itemPtr->state; + + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + width = polyPtr->outline.width; + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->outline.activeWidth>width) { + width = polyPtr->outline.activeWidth; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + width = polyPtr->outline.disabledWidth; + } + } + + radius = width/2.0; + inside = -1; + + if ((state==TK_STATE_HIDDEN) || polyPtr->numPoints<2) { + return -1; + } else if (polyPtr->numPoints <3) { + double oval[4]; + oval[0] = polyPtr->coordPtr[0]-radius; + oval[1] = polyPtr->coordPtr[1]-radius; + oval[2] = polyPtr->coordPtr[0]+radius; + oval[3] = polyPtr->coordPtr[1]+radius; + return TkOvalToArea(oval, rectPtr); + } /* * Handle smoothed polygons by generating an expanded set of points * against which to do the check. */ if (polyPtr->smooth) { - numPoints = 1 + polyPtr->numPoints*polyPtr->splineSteps; + numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL, + polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL, + (double *) NULL); if (numPoints <= MAX_STATIC_POINTS) { - coordPtr = staticSpace; + polyPoints = staticSpace; } else { - coordPtr = (double *) ckalloc((unsigned) + polyPoints = (double *) ckalloc((unsigned) (2*numPoints*sizeof(double))); } - numPoints = TkMakeBezierCurve(canvas, polyPtr->coordPtr, + numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr, polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL, - coordPtr); + polyPoints); } else { numPoints = polyPtr->numPoints; - coordPtr = polyPtr->coordPtr; + polyPoints = polyPtr->coordPtr; } - if (polyPtr->width <= 1) { - /* - * The outline of the polygon doesn't stick out, so we can - * do a simple check. - */ - - result = TkPolygonToArea(coordPtr, numPoints, rectPtr); + if (polyPtr->fillGC != None) { + inside = TkPolygonToArea(polyPoints, numPoints, rectPtr); + if (inside==0) goto donearea; } else { + if ((polyPoints[0] >= rectPtr[0]) + && (polyPoints[0] <= rectPtr[2]) + && (polyPoints[1] >= rectPtr[1]) + && (polyPoints[1] <= rectPtr[3])) { + inside = 1; + } + } + + if (polyPtr->outline.gc == None) goto donearea ; + + + /* + * Iterate through all of the edges of the line, computing a polygon + * for each edge and testing the area against that polygon. In + * addition, there are additional tests to deal with rounded joints + * and caps. + */ + + changedMiterToBevel = 0; + for (count = numPoints, coordPtr = polyPoints; count >= 2; + count--, coordPtr += 2) { + /* - * The polygon has a wide outline, so the check is more complicated. - * First, check the line segments to see if they overlap the area. + * If rounding is done around the first point of the edge + * then test a circular region around the point with the + * area. */ - result = TkThickPolyLineToArea(coordPtr, numPoints, - (double) polyPtr->width, CapRound, JoinRound, rectPtr); - if (result >= 0) { - goto done; + if (polyPtr->joinStyle == JoinRound) { + poly[0] = coordPtr[0] - radius; + poly[1] = coordPtr[1] - radius; + poly[2] = coordPtr[0] + radius; + poly[3] = coordPtr[1] + radius; + if (TkOvalToArea(poly, rectPtr) != inside) { + inside = 0; + goto donearea; + } } /* - * There is no overlap between the polygon's outline and the - * rectangle. This means either the rectangle is entirely outside - * the polygon or entirely inside. To tell the difference, - * see whether the polygon (with 0 outline width) overlaps the - * rectangle bloated by half the outline width. + * Compute the polygonal shape corresponding to this edge, + * consisting of two points for the first point of the edge + * and two points for the last point of the edge. */ - halfWidth = polyPtr->width/2.0; - rect2[0] = rectPtr[0] - halfWidth; - rect2[1] = rectPtr[1] - halfWidth; - rect2[2] = rectPtr[2] + halfWidth; - rect2[3] = rectPtr[3] + halfWidth; - if (TkPolygonToArea(coordPtr, numPoints, rect2) == -1) { - result = -1; + if (count == numPoints) { + TkGetButtPoints(coordPtr+2, coordPtr, width, + 0, poly, poly+2); + } else if ((polyPtr->joinStyle == JoinMiter) && !changedMiterToBevel) { + poly[0] = poly[6]; + poly[1] = poly[7]; + poly[2] = poly[4]; + poly[3] = poly[5]; } else { - result = 0; + TkGetButtPoints(coordPtr+2, coordPtr, width, 0, + poly, poly+2); + + /* + * If the last joint was beveled, then also check a + * polygon comprising the last two points of the previous + * polygon and the first two from this polygon; this checks + * the wedges that fill the beveled joint. + */ + + if ((polyPtr->joinStyle == JoinBevel) || changedMiterToBevel) { + poly[8] = poly[0]; + poly[9] = poly[1]; + if (TkPolygonToArea(poly, 5, rectPtr) != inside) { + inside = 0; + goto donearea; + } + changedMiterToBevel = 0; + } + } + if (count == 2) { + TkGetButtPoints(coordPtr, coordPtr+2, width, + 0, poly+4, poly+6); + } else if (polyPtr->joinStyle == JoinMiter) { + if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, + width, poly+4, poly+6) == 0) { + changedMiterToBevel = 1; + TkGetButtPoints(coordPtr, coordPtr+2, width, + 0, poly+4, poly+6); + } + } else { + TkGetButtPoints(coordPtr, coordPtr+2, width, 0, + poly+4, poly+6); + } + poly[8] = poly[0]; + poly[9] = poly[1]; + if (TkPolygonToArea(poly, 5, rectPtr) != inside) { + inside = 0; + goto donearea; } } - done: - if ((coordPtr != staticSpace) && (coordPtr != polyPtr->coordPtr)) { - ckfree((char *) coordPtr); + donearea: + if ((polyPoints != staticSpace) && (polyPoints != polyPtr->coordPtr)) { + ckfree((char *) polyPoints); } - return result; + return inside; } /* @@ -873,6 +1634,101 @@ ScalePolygon(canvas, itemPtr, originX, originY, scaleX, scaleY) /* *-------------------------------------------------------------- * + * GetPolygonIndex -- + * + * Parse an index into a polygon item and return either its value + * or an error. + * + * Results: + * A standard Tcl result. If all went well, then *indexPtr is + * filled in with the index (into itemPtr) corresponding to + * string. Otherwise an error message is left in + * interp->result. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static int +GetPolygonIndex(interp, canvas, itemPtr, obj, indexPtr) + Tcl_Interp *interp; /* Used for error reporting. */ + Tk_Canvas canvas; /* Canvas containing item. */ + Tk_Item *itemPtr; /* Item for which the index is being + * specified. */ + Tcl_Obj *obj; /* Specification of a particular coord + * in itemPtr's line. */ + int *indexPtr; /* Where to store converted index. */ +{ + PolygonItem *polyPtr = (PolygonItem *) itemPtr; + size_t length; + char *string = Tcl_GetStringFromObj(obj, (int *) &length); + + if (string[0] == 'e') { + if (strncmp(string, "end", length) == 0) { + *indexPtr = 2*(polyPtr->numPoints - polyPtr->autoClosed); + } else { + badIndex: + + /* + * Some of the paths here leave messages in interp->result, + * so we have to clear it out before storing our own message. + */ + + Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); + Tcl_AppendResult(interp, "bad index \"", string, "\"", + (char *) NULL); + return TCL_ERROR; + } + } else if (string[0] == '@') { + int i; + double x ,y, bestDist, dist, *coordPtr; + char *end, *p; + + p = string+1; + x = strtod(p, &end); + if ((end == p) || (*end != ',')) { + goto badIndex; + } + p = end+1; + y = strtod(p, &end); + if ((end == p) || (*end != 0)) { + goto badIndex; + } + bestDist = 1.0e36; + coordPtr = polyPtr->coordPtr; + *indexPtr = 0; + for(i=0; i<(polyPtr->numPoints-1); i++) { + dist = hypot(coordPtr[0] - x, coordPtr[1] - y); + if (dist<bestDist) { + bestDist = dist; + *indexPtr = 2*i; + } + coordPtr += 2; + } + } else { + int count = 2*(polyPtr->numPoints - polyPtr->autoClosed); + if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) { + goto badIndex; + } + *indexPtr &= -2; /* if odd, make it even */ + if (count) { + if (*indexPtr > 0) { + *indexPtr = ((*indexPtr - 2) % count) + 2; + } else { + *indexPtr = -((-(*indexPtr)) % count); + } + } else { + *indexPtr = 0; + } + } + return TCL_OK; +} + +/* + *-------------------------------------------------------------- + * * TranslatePolygon -- * * This procedure is called to move a polygon by a given @@ -941,29 +1797,103 @@ PolygonToPostscript(interp, canvas, itemPtr, prepass) * final Postscript is being created. */ { PolygonItem *polyPtr = (PolygonItem *) itemPtr; + char *style; + XColor *color; + XColor *fillColor; + Pixmap stipple; + Pixmap fillStipple; + Tk_State state = itemPtr->state; + double width; + + if (polyPtr->numPoints<2 || polyPtr->coordPtr==NULL) { + return TCL_OK; + } + + if(state == TK_STATE_NULL) { + state = ((TkCanvas *)canvas)->canvas_state; + } + width = polyPtr->outline.width; + color = polyPtr->outline.color; + stipple = polyPtr->fillStipple; + fillColor = polyPtr->fillColor; + fillStipple = polyPtr->fillStipple; + if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { + if (polyPtr->outline.activeWidth>width) { + width = polyPtr->outline.activeWidth; + } + if (polyPtr->outline.activeColor!=NULL) { + color = polyPtr->outline.activeColor; + } + if (polyPtr->outline.activeStipple!=None) { + stipple = polyPtr->outline.activeStipple; + } + if (polyPtr->activeFillColor!=NULL) { + fillColor = polyPtr->activeFillColor; + } + if (polyPtr->activeFillStipple!=None) { + fillStipple = polyPtr->activeFillStipple; + } + } else if (state==TK_STATE_DISABLED) { + if (polyPtr->outline.disabledWidth>0.0) { + width = polyPtr->outline.disabledWidth; + } + if (polyPtr->outline.disabledColor!=NULL) { + color = polyPtr->outline.disabledColor; + } + if (polyPtr->outline.disabledStipple!=None) { + stipple = polyPtr->outline.disabledStipple; + } + if (polyPtr->disabledFillColor!=NULL) { + fillColor = polyPtr->disabledFillColor; + } + if (polyPtr->disabledFillStipple!=None) { + fillStipple = polyPtr->disabledFillStipple; + } + } + if (polyPtr->numPoints==2) { + char string[128]; + sprintf(string, "%.15g %.15g translate %.15g %.15g", + polyPtr->coordPtr[0], Tk_CanvasPsY(canvas, polyPtr->coordPtr[1]), + width/2.0, width/2.0); + Tcl_AppendResult(interp, "matrix currentmatrix\n",string, + " scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", (char *) NULL); + if (Tk_CanvasPsColor(interp, canvas, color) + != TCL_OK) { + return TCL_ERROR; + } + if (stipple != None) { + Tcl_AppendResult(interp, "clip ", (char *) NULL); + if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) { + return TCL_ERROR; + } + } else { + Tcl_AppendResult(interp, "fill\n", (char *) NULL); + } + return TCL_OK; + } /* * Fill the area of the polygon. */ - if (polyPtr->fillColor != NULL) { - if (!polyPtr->smooth) { + if (fillColor != NULL && polyPtr->numPoints>3) { + if (!polyPtr->smooth || !polyPtr->smooth->postscriptProc) { Tk_CanvasPsPath(interp, canvas, polyPtr->coordPtr, polyPtr->numPoints); } else { - TkMakeBezierPostscript(interp, canvas, polyPtr->coordPtr, - polyPtr->numPoints); + polyPtr->smooth->postscriptProc(interp, canvas, polyPtr->coordPtr, + polyPtr->numPoints, polyPtr->splineSteps); } - if (Tk_CanvasPsColor(interp, canvas, polyPtr->fillColor) != TCL_OK) { + if (Tk_CanvasPsColor(interp, canvas, fillColor) != TCL_OK) { return TCL_ERROR; } - if (polyPtr->fillStipple != None) { + if (fillStipple != None) { Tcl_AppendResult(interp, "eoclip ", (char *) NULL); - if (Tk_CanvasPsStipple(interp, canvas, polyPtr->fillStipple) + if (Tk_CanvasPsStipple(interp, canvas, fillStipple) != TCL_OK) { return TCL_ERROR; } - if (polyPtr->outlineColor != NULL) { + if (color != NULL) { Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL); } } else { @@ -975,25 +1905,29 @@ PolygonToPostscript(interp, canvas, itemPtr, prepass) * Now draw the outline, if there is one. */ - if (polyPtr->outlineColor != NULL) { - char string[32 + TCL_INTEGER_SPACE]; + if (color != NULL) { - if (!polyPtr->smooth) { + if (!polyPtr->smooth || !polyPtr->smooth->postscriptProc) { Tk_CanvasPsPath(interp, canvas, polyPtr->coordPtr, polyPtr->numPoints); } else { - TkMakeBezierPostscript(interp, canvas, polyPtr->coordPtr, - polyPtr->numPoints); + polyPtr->smooth->postscriptProc(interp, canvas, polyPtr->coordPtr, + polyPtr->numPoints, polyPtr->splineSteps); } - sprintf(string, "%d setlinewidth\n", polyPtr->width); - Tcl_AppendResult(interp, string, - "1 setlinecap\n1 setlinejoin\n", (char *) NULL); - if (Tk_CanvasPsColor(interp, canvas, polyPtr->outlineColor) - != TCL_OK) { + if (polyPtr->joinStyle == JoinRound) { + style = "1"; + } else if (polyPtr->joinStyle == JoinBevel) { + style = "2"; + } else { + style = "0"; + } + Tcl_AppendResult(interp, style," setlinejoin 1 setlinecap\n", + (char *) NULL); + if (Tk_CanvasPsOutline(canvas, itemPtr, + &(polyPtr->outline)) != TCL_OK) { return TCL_ERROR; } - Tcl_AppendResult(interp, "stroke\n", (char *) NULL); } return TCL_OK; } |