1/*
2 * tkCanvPoly.c --
3 *
4 *	This file implements polygon items for canvas widgets.
5 *
6 * Copyright (c) 1991-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 * Copyright (c) 1998-2000 Ajuba Solutions.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tkCanvPoly.c,v 1.10.2.3 2006/10/16 15:35:50 das Exp $
14 */
15
16#include <stdio.h>
17#include "tkInt.h"
18#include "tkPort.h"
19#include "tkCanvas.h"
20
21/*
22 * The structure below defines the record for each polygon item.
23 */
24
25typedef struct PolygonItem  {
26    Tk_Item header;		/* Generic stuff that's the same for all
27				 * types.  MUST BE FIRST IN STRUCTURE. */
28    Tk_Outline outline;		/* Outline structure */
29    int numPoints;		/* Number of points in polygon.
30				 * Polygon is always closed. */
31    int pointsAllocated;	/* Number of points for which space is
32				 * allocated at *coordPtr. */
33    double *coordPtr;		/* Pointer to malloc-ed array containing
34				 * x- and y-coords of all points in polygon.
35				 * X-coords are even-valued indices, y-coords
36				 * are corresponding odd-valued indices. */
37    int joinStyle;		/* Join style for outline */
38    Tk_TSOffset tsoffset;
39    XColor *fillColor;		/* Foreground color for polygon. */
40    XColor *activeFillColor;	/* Foreground color for polygon if state is active. */
41    XColor *disabledFillColor;	/* Foreground color for polygon if state is disabled. */
42    Pixmap fillStipple;		/* Stipple bitmap for filling polygon. */
43    Pixmap activeFillStipple;	/* Stipple bitmap for filling polygon if state is active. */
44    Pixmap disabledFillStipple;	/* Stipple bitmap for filling polygon if state is disabled. */
45    GC fillGC;			/* Graphics context for filling polygon. */
46    Tk_SmoothMethod *smooth;	/* Non-zero means draw shape smoothed (i.e.
47				 * with Bezier splines). */
48    int splineSteps;		/* Number of steps in each spline segment. */
49    int autoClosed;		/* Zero means the given polygon was closed,
50				   one means that we auto closed it. */
51} PolygonItem;
52
53/*
54 * Information used for parsing configuration specs:
55 */
56
57static Tk_CustomOption smoothOption = {
58    (Tk_OptionParseProc *) TkSmoothParseProc,
59    TkSmoothPrintProc, (ClientData) NULL
60};
61static Tk_CustomOption stateOption = {
62    (Tk_OptionParseProc *) TkStateParseProc,
63    TkStatePrintProc, (ClientData) 2
64};
65static Tk_CustomOption tagsOption = {
66    (Tk_OptionParseProc *) Tk_CanvasTagsParseProc,
67    Tk_CanvasTagsPrintProc, (ClientData) NULL
68};
69static Tk_CustomOption dashOption = {
70    (Tk_OptionParseProc *) TkCanvasDashParseProc,
71    TkCanvasDashPrintProc, (ClientData) NULL
72};
73static Tk_CustomOption offsetOption = {
74    (Tk_OptionParseProc *) TkOffsetParseProc,
75    TkOffsetPrintProc,
76    (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX)
77};
78static Tk_CustomOption pixelOption = {
79    (Tk_OptionParseProc *) TkPixelParseProc,
80    TkPixelPrintProc, (ClientData) NULL
81};
82
83static Tk_ConfigSpec configSpecs[] = {
84    {TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL,
85	(char *) NULL, Tk_Offset(PolygonItem, outline.activeDash),
86	TK_CONFIG_NULL_OK, &dashOption},
87    {TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL,
88	(char *) NULL, Tk_Offset(PolygonItem, activeFillColor),
89	TK_CONFIG_NULL_OK},
90    {TK_CONFIG_COLOR, "-activeoutline", (char *) NULL, (char *) NULL,
91	(char *) NULL, Tk_Offset(PolygonItem, outline.activeColor),
92	TK_CONFIG_NULL_OK},
93    {TK_CONFIG_BITMAP, "-activeoutlinestipple", (char *) NULL, (char *) NULL,
94	(char *) NULL, Tk_Offset(PolygonItem, outline.activeStipple),
95	TK_CONFIG_NULL_OK},
96    {TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL,
97	(char *) NULL, Tk_Offset(PolygonItem, activeFillStipple),
98	TK_CONFIG_NULL_OK},
99    {TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL,
100	"0.0", Tk_Offset(PolygonItem, outline.activeWidth),
101	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
102    {TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL,
103	(char *) NULL, Tk_Offset(PolygonItem, outline.dash),
104	TK_CONFIG_NULL_OK, &dashOption},
105    {TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL,
106	"0", Tk_Offset(PolygonItem, outline.offset),
107	TK_CONFIG_DONT_SET_DEFAULT},
108    {TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL,
109	(char *) NULL, Tk_Offset(PolygonItem, outline.disabledDash),
110	TK_CONFIG_NULL_OK, &dashOption},
111    {TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL,
112	(char *) NULL, Tk_Offset(PolygonItem, disabledFillColor),
113	TK_CONFIG_NULL_OK},
114    {TK_CONFIG_COLOR, "-disabledoutline", (char *) NULL, (char *) NULL,
115	(char *) NULL, Tk_Offset(PolygonItem, outline.disabledColor),
116	TK_CONFIG_NULL_OK},
117    {TK_CONFIG_BITMAP, "-disabledoutlinestipple", (char *) NULL, (char *) NULL,
118	(char *) NULL, Tk_Offset(PolygonItem, outline.disabledStipple),
119	TK_CONFIG_NULL_OK},
120    {TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL,
121	(char *) NULL, Tk_Offset(PolygonItem, disabledFillStipple),
122	TK_CONFIG_NULL_OK},
123    {TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL,
124	"0.0", Tk_Offset(PolygonItem, outline.disabledWidth),
125	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
126    {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
127	"black", Tk_Offset(PolygonItem, fillColor), TK_CONFIG_NULL_OK},
128    {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
129	"round", Tk_Offset(PolygonItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
130    {TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL,
131	"0,0", Tk_Offset(PolygonItem, tsoffset),
132	TK_CONFIG_NULL_OK, &offsetOption},
133    {TK_CONFIG_COLOR, "-outline", (char *) NULL, (char *) NULL,
134	(char *) NULL, Tk_Offset(PolygonItem, outline.color),
135	TK_CONFIG_NULL_OK},
136    {TK_CONFIG_CUSTOM, "-outlineoffset", (char *) NULL, (char *) NULL,
137	"0,0", Tk_Offset(PolygonItem, outline.tsoffset),
138	TK_CONFIG_NULL_OK, &offsetOption},
139    {TK_CONFIG_BITMAP, "-outlinestipple", (char *) NULL, (char *) NULL,
140	(char *) NULL, Tk_Offset(PolygonItem, outline.stipple),
141	TK_CONFIG_NULL_OK},
142    {TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL,
143	"0", Tk_Offset(PolygonItem, smooth),
144	TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
145    {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
146	"12", Tk_Offset(PolygonItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
147    {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL,
148	(char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK,
149	&stateOption},
150    {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
151	(char *) NULL, Tk_Offset(PolygonItem, fillStipple), TK_CONFIG_NULL_OK},
152    {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
153	(char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
154    {TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL,
155	"1.0", Tk_Offset(PolygonItem, outline.width),
156	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
157    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
158	(char *) NULL, 0, 0}
159};
160
161/*
162 * Prototypes for procedures defined in this file:
163 */
164
165static void		ComputePolygonBbox _ANSI_ARGS_((Tk_Canvas canvas,
166			    PolygonItem *polyPtr));
167static int		ConfigurePolygon _ANSI_ARGS_((Tcl_Interp *interp,
168			    Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
169			    Tcl_Obj *CONST objv[], int flags));
170static int		CreatePolygon _ANSI_ARGS_((Tcl_Interp *interp,
171			    Tk_Canvas canvas, struct Tk_Item *itemPtr,
172			    int objc, Tcl_Obj *CONST objv[]));
173static void		DeletePolygon _ANSI_ARGS_((Tk_Canvas canvas,
174			    Tk_Item *itemPtr,  Display *display));
175static void		DisplayPolygon _ANSI_ARGS_((Tk_Canvas canvas,
176			    Tk_Item *itemPtr, Display *display, Drawable dst,
177			    int x, int y, int width, int height));
178static int		GetPolygonIndex _ANSI_ARGS_((Tcl_Interp *interp,
179			    Tk_Canvas canvas, Tk_Item *itemPtr,
180			    Tcl_Obj *obj, int *indexPtr));
181static int		PolygonCoords _ANSI_ARGS_((Tcl_Interp *interp,
182			    Tk_Canvas canvas, Tk_Item *itemPtr,
183			    int objc, Tcl_Obj *CONST objv[]));
184static void		PolygonDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas,
185			    Tk_Item *itemPtr, int first, int last));
186static void		PolygonInsert _ANSI_ARGS_((Tk_Canvas canvas,
187			    Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj));
188static int		PolygonToArea _ANSI_ARGS_((Tk_Canvas canvas,
189			    Tk_Item *itemPtr, double *rectPtr));
190static double		PolygonToPoint _ANSI_ARGS_((Tk_Canvas canvas,
191			    Tk_Item *itemPtr, double *pointPtr));
192static int		PolygonToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
193			    Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
194static void		ScalePolygon _ANSI_ARGS_((Tk_Canvas canvas,
195			    Tk_Item *itemPtr, double originX, double originY,
196			    double scaleX, double scaleY));
197static void		TranslatePolygon _ANSI_ARGS_((Tk_Canvas canvas,
198			    Tk_Item *itemPtr, double deltaX, double deltaY));
199
200/*
201 * The structures below defines the polygon item type by means
202 * of procedures that can be invoked by generic item code.
203 */
204
205Tk_ItemType tkPolygonType = {
206    "polygon",				/* name */
207    sizeof(PolygonItem),		/* itemSize */
208    CreatePolygon,			/* createProc */
209    configSpecs,			/* configSpecs */
210    ConfigurePolygon,			/* configureProc */
211    PolygonCoords,			/* coordProc */
212    DeletePolygon,			/* deleteProc */
213    DisplayPolygon,			/* displayProc */
214    TK_CONFIG_OBJS,			/* flags */
215    PolygonToPoint,			/* pointProc */
216    PolygonToArea,			/* areaProc */
217    PolygonToPostscript,		/* postscriptProc */
218    ScalePolygon,			/* scaleProc */
219    TranslatePolygon,			/* translateProc */
220    (Tk_ItemIndexProc *) GetPolygonIndex,/* indexProc */
221    (Tk_ItemCursorProc *) NULL,		/* icursorProc */
222    (Tk_ItemSelectionProc *) NULL,	/* selectionProc */
223    (Tk_ItemInsertProc *) PolygonInsert,/* insertProc */
224    PolygonDeleteCoords,		/* dTextProc */
225    (Tk_ItemType *) NULL,		/* nextPtr */
226};
227
228/*
229 * The definition below determines how large are static arrays
230 * used to hold spline points (splines larger than this have to
231 * have their arrays malloc-ed).
232 */
233
234#define MAX_STATIC_POINTS 200
235
236/*
237 *--------------------------------------------------------------
238 *
239 * CreatePolygon --
240 *
241 *	This procedure is invoked to create a new polygon item in
242 *	a canvas.
243 *
244 * Results:
245 *	A standard Tcl return value.  If an error occurred in
246 *	creating the item, then an error message is left in
247 *	the interp's result;  in this case itemPtr is
248 *	left uninitialized, so it can be safely freed by the
249 *	caller.
250 *
251 * Side effects:
252 *	A new polygon item is created.
253 *
254 *--------------------------------------------------------------
255 */
256
257static int
258CreatePolygon(interp, canvas, itemPtr, objc, objv)
259    Tcl_Interp *interp;			/* Interpreter for error reporting. */
260    Tk_Canvas canvas;			/* Canvas to hold new item. */
261    Tk_Item *itemPtr;			/* Record to hold new item;  header
262					 * has been initialized by caller. */
263    int objc;				/* Number of arguments in objv. */
264    Tcl_Obj *CONST objv[];		/* Arguments describing polygon. */
265{
266    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
267    int i;
268
269    if (objc == 0) {
270	panic("canvas did not pass any coords\n");
271    }
272
273    /*
274     * Carry out initialization that is needed in order to clean
275     * up after errors during the the remainder of this procedure.
276     */
277
278    Tk_CreateOutline(&(polyPtr->outline));
279    polyPtr->numPoints = 0;
280    polyPtr->pointsAllocated = 0;
281    polyPtr->coordPtr = NULL;
282    polyPtr->joinStyle = JoinRound;
283    polyPtr->tsoffset.flags = 0;
284    polyPtr->tsoffset.xoffset = 0;
285    polyPtr->tsoffset.yoffset = 0;
286    polyPtr->fillColor = NULL;
287    polyPtr->activeFillColor = NULL;
288    polyPtr->disabledFillColor = NULL;
289    polyPtr->fillStipple = None;
290    polyPtr->activeFillStipple = None;
291    polyPtr->disabledFillStipple = None;
292    polyPtr->fillGC = None;
293    polyPtr->smooth = (Tk_SmoothMethod *) NULL;
294    polyPtr->splineSteps = 12;
295    polyPtr->autoClosed = 0;
296
297    /*
298     * Count the number of points and then parse them into a point
299     * array.  Leading arguments are assumed to be points if they
300     * start with a digit or a minus sign followed by a digit.
301     */
302
303    for (i = 0; i < objc; i++) {
304	char *arg = Tcl_GetString(objv[i]);
305	if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
306	    break;
307	}
308    }
309    if (i && PolygonCoords(interp, canvas, itemPtr, i, objv) != TCL_OK) {
310	goto error;
311    }
312
313    if (ConfigurePolygon(interp, canvas, itemPtr, objc-i, objv+i, 0)
314	    == TCL_OK) {
315	return TCL_OK;
316    }
317
318    error:
319    DeletePolygon(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
320    return TCL_ERROR;
321}
322
323/*
324 *--------------------------------------------------------------
325 *
326 * PolygonCoords --
327 *
328 *	This procedure is invoked to process the "coords" widget
329 *	command on polygons.  See the user documentation for details
330 *	on what it does.
331 *
332 * Results:
333 *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
334 *
335 * Side effects:
336 *	The coordinates for the given item may be changed.
337 *
338 *--------------------------------------------------------------
339 */
340
341static int
342PolygonCoords(interp, canvas, itemPtr, objc, objv)
343    Tcl_Interp *interp;			/* Used for error reporting. */
344    Tk_Canvas canvas;			/* Canvas containing item. */
345    Tk_Item *itemPtr;			/* Item whose coordinates are to be
346					 * read or modified. */
347    int objc;				/* Number of coordinates supplied in
348					 * objv. */
349    Tcl_Obj *CONST objv[];		/* Array of coordinates: x1, y1,
350					 * x2, y2, ... */
351{
352    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
353    int i, numPoints;
354
355    if (objc == 0) {
356	/*
357	 * Print the coords used to create the polygon.  If we auto
358	 * closed the polygon then we don't report the last point.
359	 */
360	Tcl_Obj *subobj, *obj = Tcl_NewObj();
361	for (i = 0; i < 2*(polyPtr->numPoints - polyPtr->autoClosed); i++) {
362	    subobj = Tcl_NewDoubleObj(polyPtr->coordPtr[i]);
363	    Tcl_ListObjAppendElement(interp, obj, subobj);
364	}
365	Tcl_SetObjResult(interp, obj);
366	return TCL_OK;
367    }
368    if (objc == 1) {
369	if (Tcl_ListObjGetElements(interp, objv[0], &objc,
370		(Tcl_Obj ***) &objv) != TCL_OK) {
371	    return TCL_ERROR;
372	}
373    }
374    if (objc & 1) {
375	char buf[64 + TCL_INTEGER_SPACE];
376	sprintf(buf, "wrong # coordinates: expected an even number, got %d",
377		objc);
378	Tcl_SetResult(interp, buf, TCL_VOLATILE);
379	return TCL_ERROR;
380    } else {
381	numPoints = objc/2;
382	if (polyPtr->pointsAllocated <= numPoints) {
383	    if (polyPtr->coordPtr != NULL) {
384		ckfree((char *) polyPtr->coordPtr);
385	    }
386
387	    /*
388	     * One extra point gets allocated here, because we always
389	     * add another point to close the polygon.
390	     */
391
392	    polyPtr->coordPtr = (double *) ckalloc((unsigned)
393		    (sizeof(double) * (objc+2)));
394	    polyPtr->pointsAllocated = numPoints+1;
395	}
396	for (i = objc-1; i >= 0; i--) {
397	    if (Tk_CanvasGetCoordFromObj(interp, canvas, objv[i],
398		    &polyPtr->coordPtr[i]) != TCL_OK) {
399		return TCL_ERROR;
400	    }
401	}
402	polyPtr->numPoints = numPoints;
403	polyPtr->autoClosed = 0;
404
405	/*
406	 * Close the polygon if it isn't already closed.
407	 */
408
409	if (objc>2 && ((polyPtr->coordPtr[objc-2] != polyPtr->coordPtr[0])
410		|| (polyPtr->coordPtr[objc-1] != polyPtr->coordPtr[1]))) {
411	    polyPtr->autoClosed = 1;
412	    polyPtr->numPoints++;
413	    polyPtr->coordPtr[objc] = polyPtr->coordPtr[0];
414	    polyPtr->coordPtr[objc+1] = polyPtr->coordPtr[1];
415	}
416	ComputePolygonBbox(canvas, polyPtr);
417    }
418    return TCL_OK;
419}
420
421/*
422 *--------------------------------------------------------------
423 *
424 * ConfigurePolygon --
425 *
426 *	This procedure is invoked to configure various aspects
427 *	of a polygon item such as its background color.
428 *
429 * Results:
430 *	A standard Tcl result code.  If an error occurs, then
431 *	an error message is left in the interp's result.
432 *
433 * Side effects:
434 *	Configuration information, such as colors and stipple
435 *	patterns, may be set for itemPtr.
436 *
437 *--------------------------------------------------------------
438 */
439
440static int
441ConfigurePolygon(interp, canvas, itemPtr, objc, objv, flags)
442    Tcl_Interp *interp;		/* Interpreter for error reporting. */
443    Tk_Canvas canvas;		/* Canvas containing itemPtr. */
444    Tk_Item *itemPtr;		/* Polygon item to reconfigure. */
445    int objc;			/* Number of elements in objv.  */
446    Tcl_Obj *CONST objv[];	/* Arguments describing things to configure. */
447    int flags;			/* Flags to pass to Tk_ConfigureWidget. */
448{
449    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
450    XGCValues gcValues;
451    GC newGC;
452    unsigned long mask;
453    Tk_Window tkwin;
454    XColor *color;
455    Pixmap stipple;
456    Tk_State state;
457
458    tkwin = Tk_CanvasTkwin(canvas);
459    if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
460	    (CONST char **) objv, (char *) polyPtr, flags|TK_CONFIG_OBJS)) {
461	return TCL_ERROR;
462    }
463
464    /*
465     * A few of the options require additional processing, such as
466     * graphics contexts.
467     */
468
469    state = itemPtr->state;
470
471    if (polyPtr->outline.activeWidth > polyPtr->outline.width ||
472	    polyPtr->outline.activeDash.number != 0 ||
473	    polyPtr->outline.activeColor != NULL ||
474	    polyPtr->outline.activeStipple != None ||
475	    polyPtr->activeFillColor != NULL ||
476	    polyPtr->activeFillStipple != None) {
477	itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
478    } else {
479	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
480    }
481
482    if(state == TK_STATE_NULL) {
483	state = ((TkCanvas *)canvas)->canvas_state;
484    }
485    if (state==TK_STATE_HIDDEN) {
486	ComputePolygonBbox(canvas, polyPtr);
487	return TCL_OK;
488    }
489
490    mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, &(polyPtr->outline));
491    if (mask) {
492	gcValues.cap_style = CapRound;
493	gcValues.join_style = polyPtr->joinStyle;
494	mask |= GCCapStyle|GCJoinStyle;
495	newGC = Tk_GetGC(tkwin, mask, &gcValues);
496    } else {
497	newGC = None;
498    }
499    if (polyPtr->outline.gc != None) {
500	Tk_FreeGC(Tk_Display(tkwin), polyPtr->outline.gc);
501    }
502    polyPtr->outline.gc = newGC;
503
504    color = polyPtr->fillColor;
505    stipple = polyPtr->fillStipple;
506    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
507	if (polyPtr->activeFillColor!=NULL) {
508	    color = polyPtr->activeFillColor;
509	}
510	if (polyPtr->activeFillStipple!=None) {
511	    stipple = polyPtr->activeFillStipple;
512	}
513    } else if (state==TK_STATE_DISABLED) {
514	if (polyPtr->disabledFillColor!=NULL) {
515	    color = polyPtr->disabledFillColor;
516	}
517	if (polyPtr->disabledFillStipple!=None) {
518	    stipple = polyPtr->disabledFillStipple;
519	}
520    }
521
522    if (color == NULL) {
523	newGC = None;
524    } else {
525	gcValues.foreground = color->pixel;
526	mask = GCForeground;
527	if (stipple != None) {
528	    gcValues.stipple = stipple;
529	    gcValues.fill_style = FillStippled;
530	    mask |= GCStipple|GCFillStyle;
531	}
532#ifdef MAC_OSX_TK
533	/*
534	 * Mac OS X CG drawing needs access to the outline linewidth
535	 * even for fills (as linewidth controls antialiasing).
536	 */
537	gcValues.line_width = polyPtr->outline.gc != None ?
538		polyPtr->outline.gc->line_width : 0;
539	mask |= GCLineWidth;
540#endif
541	newGC = Tk_GetGC(tkwin, mask, &gcValues);
542    }
543    if (polyPtr->fillGC != None) {
544	Tk_FreeGC(Tk_Display(tkwin), polyPtr->fillGC);
545    }
546    polyPtr->fillGC = newGC;
547
548    /*
549     * Keep spline parameters within reasonable limits.
550     */
551
552    if (polyPtr->splineSteps < 1) {
553	polyPtr->splineSteps = 1;
554    } else if (polyPtr->splineSteps > 100) {
555	polyPtr->splineSteps = 100;
556    }
557
558    ComputePolygonBbox(canvas, polyPtr);
559    return TCL_OK;
560}
561
562/*
563 *--------------------------------------------------------------
564 *
565 * DeletePolygon --
566 *
567 *	This procedure is called to clean up the data structure
568 *	associated with a polygon item.
569 *
570 * Results:
571 *	None.
572 *
573 * Side effects:
574 *	Resources associated with itemPtr are released.
575 *
576 *--------------------------------------------------------------
577 */
578
579static void
580DeletePolygon(canvas, itemPtr, display)
581    Tk_Canvas canvas;			/* Info about overall canvas widget. */
582    Tk_Item *itemPtr;			/* Item that is being deleted. */
583    Display *display;			/* Display containing window for
584					 * canvas. */
585{
586    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
587
588    Tk_DeleteOutline(display,&(polyPtr->outline));
589    if (polyPtr->coordPtr != NULL) {
590	ckfree((char *) polyPtr->coordPtr);
591    }
592    if (polyPtr->fillColor != NULL) {
593	Tk_FreeColor(polyPtr->fillColor);
594    }
595    if (polyPtr->activeFillColor != NULL) {
596	Tk_FreeColor(polyPtr->activeFillColor);
597    }
598    if (polyPtr->disabledFillColor != NULL) {
599	Tk_FreeColor(polyPtr->disabledFillColor);
600    }
601    if (polyPtr->fillStipple != None) {
602	Tk_FreeBitmap(display, polyPtr->fillStipple);
603    }
604    if (polyPtr->activeFillStipple != None) {
605	Tk_FreeBitmap(display, polyPtr->activeFillStipple);
606    }
607    if (polyPtr->disabledFillStipple != None) {
608	Tk_FreeBitmap(display, polyPtr->disabledFillStipple);
609    }
610    if (polyPtr->fillGC != None) {
611	Tk_FreeGC(display, polyPtr->fillGC);
612    }
613}
614
615/*
616 *--------------------------------------------------------------
617 *
618 * ComputePolygonBbox --
619 *
620 *	This procedure is invoked to compute the bounding box of
621 *	all the pixels that may be drawn as part of a polygon.
622 *
623 * Results:
624 *	None.
625 *
626 * Side effects:
627 *	The fields x1, y1, x2, and y2 are updated in the header
628 *	for itemPtr.
629 *
630 *--------------------------------------------------------------
631 */
632
633static void
634ComputePolygonBbox(canvas, polyPtr)
635    Tk_Canvas canvas;			/* Canvas that contains item. */
636    PolygonItem *polyPtr;		/* Item whose bbox is to be
637					 * recomputed. */
638{
639    double *coordPtr;
640    int i;
641    double width;
642    Tk_State state = polyPtr->header.state;
643    Tk_TSOffset *tsoffset;
644
645    if(state == TK_STATE_NULL) {
646	state = ((TkCanvas *)canvas)->canvas_state;
647    }
648    width = polyPtr->outline.width;
649    if (polyPtr->coordPtr == NULL || (polyPtr->numPoints < 1) || (state==TK_STATE_HIDDEN)) {
650	polyPtr->header.x1 = polyPtr->header.x2 =
651	polyPtr->header.y1 = polyPtr->header.y2 = -1;
652	return;
653    }
654    if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)polyPtr) {
655	if (polyPtr->outline.activeWidth>width) {
656	    width = polyPtr->outline.activeWidth;
657	}
658    } else if (state==TK_STATE_DISABLED) {
659	if (polyPtr->outline.disabledWidth>0.0) {
660	    width = polyPtr->outline.disabledWidth;
661	}
662    }
663
664    coordPtr = polyPtr->coordPtr;
665    polyPtr->header.x1 = polyPtr->header.x2 = (int) *coordPtr;
666    polyPtr->header.y1 = polyPtr->header.y2 = (int) coordPtr[1];
667
668    /*
669     * Compute the bounding box of all the points in the polygon,
670     * then expand in all directions by the outline's width to take
671     * care of butting or rounded corners and projecting or
672     * rounded caps.  This expansion is an overestimate (worst-case
673     * is square root of two over two) but it's simple.  Don't do
674     * anything special for curves.  This causes an additional
675     * overestimate in the bounding box, but is faster.
676     */
677
678    for (i = 1, coordPtr = polyPtr->coordPtr+2; i < polyPtr->numPoints-1;
679	    i++, coordPtr += 2) {
680	TkIncludePoint((Tk_Item *) polyPtr, coordPtr);
681    }
682
683    tsoffset = &polyPtr->tsoffset;
684    if (tsoffset->flags & TK_OFFSET_INDEX) {
685	int index = tsoffset->flags & ~TK_OFFSET_INDEX;
686	if (tsoffset->flags == INT_MAX) {
687	    index = (polyPtr->numPoints - polyPtr->autoClosed) * 2;
688	    if (index < 0) {
689		index = 0;
690	    }
691	}
692	index %= (polyPtr->numPoints - polyPtr->autoClosed) * 2;
693	if (index <0) {
694	    index += (polyPtr->numPoints - polyPtr->autoClosed) * 2;
695	}
696 	tsoffset->xoffset = (int) (polyPtr->coordPtr[index] + 0.5);
697	tsoffset->yoffset = (int) (polyPtr->coordPtr[index+1] + 0.5);
698    } else {
699	if (tsoffset->flags & TK_OFFSET_LEFT) {
700	    tsoffset->xoffset = polyPtr->header.x1;
701	} else if (tsoffset->flags & TK_OFFSET_CENTER) {
702	    tsoffset->xoffset = (polyPtr->header.x1 + polyPtr->header.x2)/2;
703	} else if (tsoffset->flags & TK_OFFSET_RIGHT) {
704	    tsoffset->xoffset = polyPtr->header.x2;
705	}
706	if (tsoffset->flags & TK_OFFSET_TOP) {
707	    tsoffset->yoffset = polyPtr->header.y1;
708	} else if (tsoffset->flags & TK_OFFSET_MIDDLE) {
709	    tsoffset->yoffset = (polyPtr->header.y1 + polyPtr->header.y2)/2;
710	} else if (tsoffset->flags & TK_OFFSET_BOTTOM) {
711	    tsoffset->yoffset = polyPtr->header.y2;
712	}
713    }
714
715    if (polyPtr->outline.gc != None) {
716	tsoffset = &polyPtr->outline.tsoffset;
717	if (tsoffset) {
718	    if (tsoffset->flags & TK_OFFSET_INDEX) {
719		int index = tsoffset->flags & ~TK_OFFSET_INDEX;
720		if (tsoffset->flags == INT_MAX) {
721		    index = (polyPtr->numPoints - 1) * 2;
722		}
723		index %= (polyPtr->numPoints - 1) * 2;
724		if (index <0) {
725		    index += (polyPtr->numPoints - 1) * 2;
726		}
727		tsoffset->xoffset = (int) (polyPtr->coordPtr[index] + 0.5);
728		tsoffset->yoffset = (int) (polyPtr->coordPtr[index+1] + 0.5);
729	    } else {
730		if (tsoffset->flags & TK_OFFSET_LEFT) {
731		    tsoffset->xoffset = polyPtr->header.x1;
732		} else if (tsoffset->flags & TK_OFFSET_CENTER) {
733		    tsoffset->xoffset = (polyPtr->header.x1 + polyPtr->header.x2)/2;
734		} else if (tsoffset->flags & TK_OFFSET_RIGHT) {
735		    tsoffset->xoffset = polyPtr->header.x2;
736		}
737		if (tsoffset->flags & TK_OFFSET_TOP) {
738		    tsoffset->yoffset = polyPtr->header.y1;
739		} else if (tsoffset->flags & TK_OFFSET_MIDDLE) {
740		    tsoffset->yoffset = (polyPtr->header.y1 + polyPtr->header.y2)/2;
741		} else if (tsoffset->flags & TK_OFFSET_BOTTOM) {
742		    tsoffset->yoffset = polyPtr->header.y2;
743		}
744	    }
745	}
746
747	i = (int) ((width+1.5)/2.0);
748	polyPtr->header.x1 -= i;
749	polyPtr->header.x2 += i;
750	polyPtr->header.y1 -= i;
751	polyPtr->header.y2 += i;
752
753	/*
754	 * For mitered lines, make a second pass through all the points.
755	 * Compute the locations of the two miter vertex points and add
756	 * those into the bounding box.
757	 */
758
759	if (polyPtr->joinStyle == JoinMiter) {
760	    double miter[4];
761	    int j;
762	    coordPtr = polyPtr->coordPtr;
763	    if (polyPtr->numPoints>3) {
764		if (TkGetMiterPoints(coordPtr+2*(polyPtr->numPoints-2),
765			coordPtr, coordPtr+2, width,
766			miter, miter+2)) {
767		    for (j = 0; j < 4; j += 2) {
768			TkIncludePoint((Tk_Item *) polyPtr, miter+j);
769		    }
770		}
771	     }
772	    for (i = polyPtr->numPoints ; i >= 3;
773		    i--, coordPtr += 2) {
774
775		if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
776			width, miter, miter+2)) {
777		    for (j = 0; j < 4; j += 2) {
778			TkIncludePoint((Tk_Item *) polyPtr, miter+j);
779		    }
780		}
781	    }
782	}
783    }
784
785    /*
786     * Add one more pixel of fudge factor just to be safe (e.g.
787     * X may round differently than we do).
788     */
789
790    polyPtr->header.x1 -= 1;
791    polyPtr->header.x2 += 1;
792    polyPtr->header.y1 -= 1;
793    polyPtr->header.y2 += 1;
794}
795
796/*
797 *--------------------------------------------------------------
798 *
799 * TkFillPolygon --
800 *
801 *	This procedure is invoked to convert a polygon to screen
802 *	coordinates and display it using a particular GC.
803 *
804 * Results:
805 *	None.
806 *
807 * Side effects:
808 *	ItemPtr is drawn in drawable using the transformation
809 *	information in canvas.
810 *
811 *--------------------------------------------------------------
812 */
813
814void
815TkFillPolygon(canvas, coordPtr, numPoints, display, drawable, gc, outlineGC)
816    Tk_Canvas canvas;			/* Canvas whose coordinate system
817					 * is to be used for drawing. */
818    double *coordPtr;			/* Array of coordinates for polygon:
819					 * x1, y1, x2, y2, .... */
820    int numPoints;			/* Twice this many coordinates are
821					 * present at *coordPtr. */
822    Display *display;			/* Display on which to draw polygon. */
823    Drawable drawable;			/* Pixmap or window in which to draw
824					 * polygon. */
825    GC gc;				/* Graphics context for drawing. */
826    GC outlineGC;			/* If not None, use this to draw an
827					 * outline around the polygon after
828					 * filling it. */
829{
830    XPoint staticPoints[MAX_STATIC_POINTS];
831    XPoint *pointPtr;
832    XPoint *pPtr;
833    int i;
834
835    /*
836     * Build up an array of points in screen coordinates.  Use a
837     * static array unless the polygon has an enormous number of points;
838     * in this case, dynamically allocate an array.
839     */
840
841    if (numPoints <= MAX_STATIC_POINTS) {
842	pointPtr = staticPoints;
843    } else {
844	pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
845    }
846
847    for (i = 0, pPtr = pointPtr; i < numPoints; i += 1, coordPtr += 2, pPtr++) {
848	Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1], &pPtr->x,
849		&pPtr->y);
850    }
851
852    /*
853     * Display polygon, then free up polygon storage if it was dynamically
854     * allocated.
855     */
856
857    if (gc != None && numPoints>3) {
858	XFillPolygon(display, drawable, gc, pointPtr, numPoints, Complex,
859		CoordModeOrigin);
860    }
861    if (outlineGC != None) {
862	XDrawLines(display, drawable, outlineGC, pointPtr,
863	    numPoints, CoordModeOrigin);
864    }
865    if (pointPtr != staticPoints) {
866	ckfree((char *) pointPtr);
867    }
868}
869
870/*
871 *--------------------------------------------------------------
872 *
873 * DisplayPolygon --
874 *
875 *	This procedure is invoked to draw a polygon item in a given
876 *	drawable.
877 *
878 * Results:
879 *	None.
880 *
881 * Side effects:
882 *	ItemPtr is drawn in drawable using the transformation
883 *	information in canvas.
884 *
885 *--------------------------------------------------------------
886 */
887
888static void
889DisplayPolygon(canvas, itemPtr, display, drawable, x, y, width, height)
890    Tk_Canvas canvas;			/* Canvas that contains item. */
891    Tk_Item *itemPtr;			/* Item to be displayed. */
892    Display *display;			/* Display on which to draw item. */
893    Drawable drawable;			/* Pixmap or window in which to draw
894					 * item. */
895    int x, y, width, height;		/* Describes region of canvas that
896					 * must be redisplayed (not used). */
897{
898    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
899    Tk_State state = itemPtr->state;
900    Pixmap stipple = polyPtr->fillStipple;
901    double linewidth = polyPtr->outline.width;
902
903    if (((polyPtr->fillGC == None) && (polyPtr->outline.gc == None)) ||
904	    (polyPtr->numPoints < 1) ||
905	    (polyPtr->numPoints < 3 && polyPtr->outline.gc == None)) {
906	return;
907    }
908
909    if(state == TK_STATE_NULL) {
910	state = ((TkCanvas *)canvas)->canvas_state;
911    }
912    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
913	if (polyPtr->outline.activeWidth>linewidth) {
914	    linewidth = polyPtr->outline.activeWidth;
915	}
916	if (polyPtr->activeFillStipple != None) {
917	    stipple = polyPtr->activeFillStipple;
918	}
919    } else if (state==TK_STATE_DISABLED) {
920	if (polyPtr->outline.disabledWidth>0.0) {
921	    linewidth = polyPtr->outline.disabledWidth;
922	}
923	if (polyPtr->disabledFillStipple != None) {
924	    stipple = polyPtr->disabledFillStipple;
925	}
926    }
927    /*
928     * If we're stippling then modify the stipple offset in the GC.  Be
929     * sure to reset the offset when done, since the GC is supposed to be
930     * read-only.
931     */
932
933    if ((stipple != None) && (polyPtr->fillGC != None)) {
934	Tk_TSOffset *tsoffset = &polyPtr->tsoffset;
935	int w=0; int h=0;
936	int flags = tsoffset->flags;
937	if (!(flags & TK_OFFSET_INDEX) && (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) {
938	    Tk_SizeOfBitmap(display, stipple, &w, &h);
939	    if (flags & TK_OFFSET_CENTER) {
940		w /= 2;
941	    } else {
942		w = 0;
943	    }
944	    if (flags & TK_OFFSET_MIDDLE) {
945		h /= 2;
946	    } else {
947		h = 0;
948	    }
949	}
950	tsoffset->xoffset -= w;
951	tsoffset->yoffset -= h;
952	Tk_CanvasSetOffset(canvas, polyPtr->fillGC, tsoffset);
953	tsoffset->xoffset += w;
954	tsoffset->yoffset += h;
955    }
956    Tk_ChangeOutlineGC(canvas, itemPtr, &(polyPtr->outline));
957
958    if(polyPtr->numPoints < 3) {
959	short x,y;
960	int intLineWidth = (int) (linewidth + 0.5);
961	if (intLineWidth < 1) {
962	    intLineWidth = 1;
963	}
964	Tk_CanvasDrawableCoords(canvas, polyPtr->coordPtr[0],
965		    polyPtr->coordPtr[1], &x,&y);
966	XFillArc(display, drawable, polyPtr->outline.gc,
967		x - intLineWidth/2, y - intLineWidth/2,
968		(unsigned int)intLineWidth+1, (unsigned int)intLineWidth+1,
969		0, 64*360);
970    } else if (!polyPtr->smooth || polyPtr->numPoints < 4) {
971	TkFillPolygon(canvas, polyPtr->coordPtr, polyPtr->numPoints,
972		    display, drawable, polyPtr->fillGC, polyPtr->outline.gc);
973    } else {
974	int numPoints;
975	XPoint staticPoints[MAX_STATIC_POINTS];
976	XPoint *pointPtr;
977
978	/*
979	 * This is a smoothed polygon.  Display using a set of generated
980	 * spline points rather than the original points.
981	 */
982
983	numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL,
984		polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL,
985		(double *) NULL);
986	if (numPoints <= MAX_STATIC_POINTS) {
987	    pointPtr = staticPoints;
988	} else {
989	    pointPtr = (XPoint *) ckalloc((unsigned)
990		    (numPoints * sizeof(XPoint)));
991	}
992	numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr,
993		polyPtr->numPoints, polyPtr->splineSteps, pointPtr,
994		(double *) NULL);
995	if (polyPtr->fillGC != None) {
996	    XFillPolygon(display, drawable, polyPtr->fillGC, pointPtr,
997		    numPoints, Complex, CoordModeOrigin);
998	}
999	if (polyPtr->outline.gc != None) {
1000	    XDrawLines(display, drawable, polyPtr->outline.gc, pointPtr,
1001		    numPoints, CoordModeOrigin);
1002	}
1003	if (pointPtr != staticPoints) {
1004	    ckfree((char *) pointPtr);
1005	}
1006    }
1007    Tk_ResetOutlineGC(canvas, itemPtr, &(polyPtr->outline));
1008    if ((stipple != None) && (polyPtr->fillGC != None)) {
1009	XSetTSOrigin(display, polyPtr->fillGC, 0, 0);
1010    }
1011}
1012
1013/*
1014 *--------------------------------------------------------------
1015 *
1016 * PolygonInsert --
1017 *
1018 *	Insert coords into a polugon item at a given index.
1019 *
1020 * Results:
1021 *	None.
1022 *
1023 * Side effects:
1024 *	The coords in the given item is modified.
1025 *
1026 *--------------------------------------------------------------
1027 */
1028
1029static void
1030PolygonInsert(canvas, itemPtr, beforeThis, obj)
1031    Tk_Canvas canvas;		/* Canvas containing text item. */
1032    Tk_Item *itemPtr;		/* Line item to be modified. */
1033    int beforeThis;		/* Index before which new coordinates
1034				 * are to be inserted. */
1035    Tcl_Obj *obj;		/* New coordinates to be inserted. */
1036{
1037    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1038    int length, objc, i;
1039    Tcl_Obj **objv;
1040    double *new;
1041    Tk_State state = itemPtr->state;
1042
1043    if (state == TK_STATE_NULL) {
1044	state = ((TkCanvas *)canvas)->canvas_state;
1045    }
1046
1047    if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &objc, &objv) != TCL_OK)
1048	    || !objc || objc&1) {
1049	return;
1050    }
1051    length = 2*(polyPtr->numPoints - polyPtr->autoClosed);
1052    while(beforeThis>length) beforeThis-=length;
1053    while(beforeThis<0) beforeThis+=length;
1054    new = (double *) ckalloc((unsigned)(sizeof(double) * (length + 2 + objc)));
1055    for (i=0; i<beforeThis; i++) {
1056	new[i] = polyPtr->coordPtr[i];
1057    }
1058    for (i=0; i<objc; i++) {
1059	if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i],
1060		new+(i+beforeThis))!=TCL_OK) {
1061	    ckfree((char *) new);
1062	    return;
1063	}
1064    }
1065
1066    for(i=beforeThis; i<length; i++) {
1067	new[i+objc] = polyPtr->coordPtr[i];
1068    }
1069    if(polyPtr->coordPtr) ckfree((char *) polyPtr->coordPtr);
1070    length+=objc;
1071    polyPtr->coordPtr = new;
1072    polyPtr->numPoints = (length/2) + polyPtr->autoClosed;
1073
1074    /*
1075     * Close the polygon if it isn't already closed, or remove autoclosing
1076     * if the user's coordinates are now closed.
1077     */
1078
1079    if (polyPtr->autoClosed) {
1080	if ((new[length-2] == new[0]) && (new[length-1] == new[1])) {
1081	    polyPtr->autoClosed = 0;
1082	    polyPtr->numPoints--;
1083	}
1084    }
1085    else {
1086	if ((new[length-2] != new[0]) || (new[length-1] != new[1])) {
1087	    polyPtr->autoClosed = 1;
1088	    polyPtr->numPoints++;
1089	}
1090    }
1091
1092    new[length] = new[0];
1093    new[length+1] = new[1];
1094    if (((length-objc)>3) && (state != TK_STATE_HIDDEN)) {
1095	/*
1096	 * This is some optimizing code that will result that only the part
1097	 * of the polygon that changed (and the objects that are overlapping
1098	 * with that part) need to be redrawn. A special flag is set that
1099	 * instructs the general canvas code not to redraw the whole
1100	 * object. If this flag is not set, the canvas will do the redrawing,
1101	 * otherwise I have to do it here.
1102	 */
1103    	double width;
1104	int j;
1105	itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;
1106
1107	/*
1108	 * The header elements that normally are used for the
1109	 * bounding box, are now used to calculate the bounding
1110	 * box for only the part that has to be redrawn. That
1111	 * doesn't matter, because afterwards the bounding
1112	 * box has to be re-calculated anyway.
1113	 */
1114
1115	itemPtr->x1 = itemPtr->x2 = (int) polyPtr->coordPtr[beforeThis];
1116	itemPtr->y1 = itemPtr->y2 = (int) polyPtr->coordPtr[beforeThis+1];
1117	beforeThis-=2; objc+=4;
1118	if(polyPtr->smooth) {
1119	    beforeThis-=2; objc+=4;
1120	} /* be carefull; beforeThis could now be negative */
1121	for(i=beforeThis; i<beforeThis+objc; i+=2) {
1122		j=i;
1123		if(j<0) j+=length;
1124		if(j>=length) j-=length;
1125		TkIncludePoint(itemPtr, polyPtr->coordPtr+j);
1126	}
1127	width = polyPtr->outline.width;
1128	if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1129		if (polyPtr->outline.activeWidth>width) {
1130		    width = polyPtr->outline.activeWidth;
1131		}
1132	} else if (state==TK_STATE_DISABLED) {
1133		if (polyPtr->outline.disabledWidth>0.0) {
1134		    width = polyPtr->outline.disabledWidth;
1135		}
1136	}
1137	itemPtr->x1 -= (int) width; itemPtr->y1 -= (int) width;
1138	itemPtr->x2 += (int) width; itemPtr->y2 += (int) width;
1139	Tk_CanvasEventuallyRedraw(canvas,
1140		itemPtr->x1, itemPtr->y1,
1141		itemPtr->x2, itemPtr->y2);
1142    }
1143
1144    ComputePolygonBbox(canvas, polyPtr);
1145}
1146
1147/*
1148 *--------------------------------------------------------------
1149 *
1150 * PolygonDeleteCoords --
1151 *
1152 *	Delete one or more coordinates from a polygon item.
1153 *
1154 * Results:
1155 *	None.
1156 *
1157 * Side effects:
1158 *	Characters between "first" and "last", inclusive, get
1159 *	deleted from itemPtr.
1160 *
1161 *--------------------------------------------------------------
1162 */
1163
1164static void
1165PolygonDeleteCoords(canvas, itemPtr, first, last)
1166    Tk_Canvas canvas;		/* Canvas containing itemPtr. */
1167    Tk_Item *itemPtr;		/* Item in which to delete characters. */
1168    int first;			/* Index of first character to delete. */
1169    int last;			/* Index of last character to delete. */
1170{
1171    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1172    int count, i;
1173    int length = 2*(polyPtr->numPoints - polyPtr->autoClosed);
1174
1175    while(first>=length) first-=length;
1176    while(first<0)	 first+=length;
1177    while(last>=length)	 last-=length;
1178    while(last<0)	 last+=length;
1179
1180    first &= -2;
1181    last &= -2;
1182
1183    count = last + 2 - first;
1184    if(count<=0) count +=length;
1185
1186    if(count >= length) {
1187	polyPtr->numPoints = 0;
1188	if(polyPtr->coordPtr != NULL) {
1189	    ckfree((char *) polyPtr->coordPtr);
1190	}
1191	ComputePolygonBbox(canvas, polyPtr);
1192	return;
1193    }
1194
1195    if(last>=first) {
1196	for(i=last+2; i<length; i++) {
1197	    polyPtr->coordPtr[i-count] = polyPtr->coordPtr[i];
1198	}
1199    } else {
1200	for(i=last; i<=first; i++) {
1201	    polyPtr->coordPtr[i-last] = polyPtr->coordPtr[i];
1202	}
1203    }
1204    polyPtr->coordPtr[length-count] = polyPtr->coordPtr[0];
1205    polyPtr->coordPtr[length-count+1] = polyPtr->coordPtr[1];
1206    polyPtr->numPoints -= count/2;
1207    ComputePolygonBbox(canvas, polyPtr);
1208}
1209
1210/*
1211 *--------------------------------------------------------------
1212 *
1213 * PolygonToPoint --
1214 *
1215 *	Computes the distance from a given point to a given
1216 *	polygon, in canvas units.
1217 *
1218 * Results:
1219 *	The return value is 0 if the point whose x and y coordinates
1220 *	are pointPtr[0] and pointPtr[1] is inside the polygon.  If the
1221 *	point isn't inside the polygon then the return value is the
1222 *	distance from the point to the polygon.
1223 *
1224 * Side effects:
1225 *	None.
1226 *
1227 *--------------------------------------------------------------
1228 */
1229
1230	/* ARGSUSED */
1231static double
1232PolygonToPoint(canvas, itemPtr, pointPtr)
1233    Tk_Canvas canvas;		/* Canvas containing item. */
1234    Tk_Item *itemPtr;		/* Item to check against point. */
1235    double *pointPtr;		/* Pointer to x and y coordinates. */
1236{
1237    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1238    double *coordPtr, *polyPoints;
1239    double staticSpace[2*MAX_STATIC_POINTS];
1240    double poly[10];
1241    double radius;
1242    double bestDist, dist;
1243    int numPoints, count;
1244    int changedMiterToBevel;	/* Non-zero means that a mitered corner
1245				 * had to be treated as beveled after all
1246				 * because the angle was < 11 degrees. */
1247    double width;
1248    Tk_State state = itemPtr->state;
1249
1250    bestDist = 1.0e36;
1251
1252    if(state == TK_STATE_NULL) {
1253	state = ((TkCanvas *)canvas)->canvas_state;
1254    }
1255    width = polyPtr->outline.width;
1256    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1257	if (polyPtr->outline.activeWidth>width) {
1258	    width = polyPtr->outline.activeWidth;
1259	}
1260    } else if (state==TK_STATE_DISABLED) {
1261	if (polyPtr->outline.disabledWidth>0.0) {
1262	    width = polyPtr->outline.disabledWidth;
1263	}
1264    }
1265    radius = width/2.0;
1266
1267    /*
1268     * Handle smoothed polygons by generating an expanded set of points
1269     * against which to do the check.
1270     */
1271
1272    if ((polyPtr->smooth) && (polyPtr->numPoints>2)) {
1273	numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL,
1274		polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL,
1275		(double *) NULL);
1276	if (numPoints <= MAX_STATIC_POINTS) {
1277	    polyPoints = staticSpace;
1278	} else {
1279	    polyPoints = (double *) ckalloc((unsigned)
1280		    (2*numPoints*sizeof(double)));
1281	}
1282	numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr,
1283		polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL,
1284		polyPoints);
1285    } else {
1286	numPoints = polyPtr->numPoints;
1287	polyPoints = polyPtr->coordPtr;
1288    }
1289
1290    bestDist = TkPolygonToPoint(polyPoints, numPoints, pointPtr);
1291    if (bestDist<=0.0) {
1292	goto donepoint;
1293    }
1294    if ((polyPtr->outline.gc != None) && (polyPtr->joinStyle == JoinRound)) {
1295	dist = bestDist - radius;
1296	if (dist <= 0.0) {
1297	    bestDist = 0.0;
1298	    goto donepoint;
1299	} else {
1300	    bestDist = dist;
1301	}
1302    }
1303
1304    if ((polyPtr->outline.gc == None) || (width <= 1)) goto donepoint;
1305
1306    /*
1307     * The overall idea is to iterate through all of the edges of
1308     * the line, computing a polygon for each edge and testing the
1309     * point against that polygon.  In addition, there are additional
1310     * tests to deal with rounded joints and caps.
1311     */
1312
1313    changedMiterToBevel = 0;
1314    for (count = numPoints, coordPtr = polyPoints; count >= 2;
1315	    count--, coordPtr += 2) {
1316
1317	/*
1318	 * If rounding is done around the first point then compute
1319	 * the distance between the point and the point.
1320	 */
1321
1322	if (polyPtr->joinStyle == JoinRound) {
1323	    dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
1324		    - radius;
1325	    if (dist <= 0.0) {
1326		bestDist = 0.0;
1327		goto donepoint;
1328	    } else if (dist < bestDist) {
1329		bestDist = dist;
1330	    }
1331	}
1332
1333	/*
1334	 * Compute the polygonal shape corresponding to this edge,
1335	 * consisting of two points for the first point of the edge
1336	 * and two points for the last point of the edge.
1337	 */
1338
1339	if (count == numPoints) {
1340	    TkGetButtPoints(coordPtr+2, coordPtr, (double) width,
1341		    0, poly, poly+2);
1342	} else if ((polyPtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
1343	    poly[0] = poly[6];
1344	    poly[1] = poly[7];
1345	    poly[2] = poly[4];
1346	    poly[3] = poly[5];
1347	} else {
1348	    TkGetButtPoints(coordPtr+2, coordPtr, (double) width, 0,
1349		    poly, poly+2);
1350
1351	    /*
1352	     * If this line uses beveled joints, then check the distance
1353	     * to a polygon comprising the last two points of the previous
1354	     * polygon and the first two from this polygon;  this checks
1355	     * the wedges that fill the mitered joint.
1356	     */
1357
1358	    if ((polyPtr->joinStyle == JoinBevel) || changedMiterToBevel) {
1359		poly[8] = poly[0];
1360		poly[9] = poly[1];
1361		dist = TkPolygonToPoint(poly, 5, pointPtr);
1362		if (dist <= 0.0) {
1363		    bestDist = 0.0;
1364		    goto donepoint;
1365		} else if (dist < bestDist) {
1366		    bestDist = dist;
1367		}
1368		changedMiterToBevel = 0;
1369	    }
1370	}
1371	if (count == 2) {
1372	    TkGetButtPoints(coordPtr, coordPtr+2, (double) width,
1373		    0, poly+4, poly+6);
1374	} else if (polyPtr->joinStyle == JoinMiter) {
1375	    if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
1376		    (double) width, poly+4, poly+6) == 0) {
1377		changedMiterToBevel = 1;
1378		TkGetButtPoints(coordPtr, coordPtr+2, (double) width,
1379			0, poly+4, poly+6);
1380	    }
1381	} else {
1382	    TkGetButtPoints(coordPtr, coordPtr+2, (double) width, 0,
1383		    poly+4, poly+6);
1384	}
1385	poly[8] = poly[0];
1386	poly[9] = poly[1];
1387	dist = TkPolygonToPoint(poly, 5, pointPtr);
1388	if (dist <= 0.0) {
1389	    bestDist = 0.0;
1390	    goto donepoint;
1391	} else if (dist < bestDist) {
1392	    bestDist = dist;
1393	}
1394    }
1395
1396    donepoint:
1397    if ((polyPoints != staticSpace) && polyPoints != polyPtr->coordPtr) {
1398	ckfree((char *) polyPoints);
1399    }
1400    return bestDist;
1401}
1402
1403/*
1404 *--------------------------------------------------------------
1405 *
1406 * PolygonToArea --
1407 *
1408 *	This procedure is called to determine whether an item
1409 *	lies entirely inside, entirely outside, or overlapping
1410 *	a given rectangular area.
1411 *
1412 * Results:
1413 *	-1 is returned if the item is entirely outside the area
1414 *	given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1415 *	inside the given area.
1416 *
1417 * Side effects:
1418 *	None.
1419 *
1420 *--------------------------------------------------------------
1421 */
1422
1423	/* ARGSUSED */
1424static int
1425PolygonToArea(canvas, itemPtr, rectPtr)
1426    Tk_Canvas canvas;		/* Canvas containing item. */
1427    Tk_Item *itemPtr;		/* Item to check against polygon. */
1428    double *rectPtr;		/* Pointer to array of four coordinates
1429				 * (x1, y1, x2, y2) describing rectangular
1430				 * area.  */
1431{
1432    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1433    double *coordPtr;
1434    double staticSpace[2*MAX_STATIC_POINTS];
1435    double *polyPoints, poly[10];
1436    double radius;
1437    int numPoints, count;
1438    int changedMiterToBevel;	/* Non-zero means that a mitered corner
1439				 * had to be treated as beveled after all
1440				 * because the angle was < 11 degrees. */
1441    int inside;			/* Tentative guess about what to return,
1442				 * based on all points seen so far:  one
1443				 * means everything seen so far was
1444				 * inside the area;  -1 means everything
1445				 * was outside the area.  0 means overlap
1446				 * has been found. */
1447    double width;
1448    Tk_State state = itemPtr->state;
1449
1450    if(state == TK_STATE_NULL) {
1451	state = ((TkCanvas *)canvas)->canvas_state;
1452    }
1453
1454    width = polyPtr->outline.width;
1455    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1456	if (polyPtr->outline.activeWidth>width) {
1457	    width = polyPtr->outline.activeWidth;
1458	}
1459    } else if (state==TK_STATE_DISABLED) {
1460	if (polyPtr->outline.disabledWidth>0.0) {
1461	    width = polyPtr->outline.disabledWidth;
1462	}
1463    }
1464
1465    radius = width/2.0;
1466    inside = -1;
1467
1468    if ((state==TK_STATE_HIDDEN) || polyPtr->numPoints<2) {
1469	return -1;
1470    } else if (polyPtr->numPoints <3) {
1471	double oval[4];
1472	oval[0] = polyPtr->coordPtr[0]-radius;
1473	oval[1] = polyPtr->coordPtr[1]-radius;
1474	oval[2] = polyPtr->coordPtr[0]+radius;
1475	oval[3] = polyPtr->coordPtr[1]+radius;
1476	return TkOvalToArea(oval, rectPtr);
1477    }
1478    /*
1479     * Handle smoothed polygons by generating an expanded set of points
1480     * against which to do the check.
1481     */
1482
1483    if (polyPtr->smooth) {
1484	numPoints = polyPtr->smooth->coordProc(canvas, (double *) NULL,
1485		polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL,
1486		(double *) NULL);
1487	if (numPoints <= MAX_STATIC_POINTS) {
1488	    polyPoints = staticSpace;
1489	} else {
1490	    polyPoints = (double *) ckalloc((unsigned)
1491		    (2*numPoints*sizeof(double)));
1492	}
1493	numPoints = polyPtr->smooth->coordProc(canvas, polyPtr->coordPtr,
1494		polyPtr->numPoints, polyPtr->splineSteps, (XPoint *) NULL,
1495		polyPoints);
1496    } else {
1497	numPoints = polyPtr->numPoints;
1498	polyPoints = polyPtr->coordPtr;
1499    }
1500
1501    /*
1502     * Simple test to see if we are in the polygon.  Polygons are
1503     * different from othe canvas items in that they register points
1504     * being inside even if it isn't filled.
1505     */
1506    inside = TkPolygonToArea(polyPoints, numPoints, rectPtr);
1507    if (inside==0) goto donearea;
1508
1509    if (polyPtr->outline.gc == None) goto donearea ;
1510
1511    /*
1512     * Iterate through all of the edges of the line, computing a polygon
1513     * for each edge and testing the area against that polygon.  In
1514     * addition, there are additional tests to deal with rounded joints
1515     * and caps.
1516     */
1517
1518    changedMiterToBevel = 0;
1519    for (count = numPoints, coordPtr = polyPoints; count >= 2;
1520	    count--, coordPtr += 2) {
1521
1522	/*
1523	 * If rounding is done around the first point of the edge
1524	 * then test a circular region around the point with the
1525	 * area.
1526	 */
1527
1528	if (polyPtr->joinStyle == JoinRound) {
1529	    poly[0] = coordPtr[0] - radius;
1530	    poly[1] = coordPtr[1] - radius;
1531	    poly[2] = coordPtr[0] + radius;
1532	    poly[3] = coordPtr[1] + radius;
1533	    if (TkOvalToArea(poly, rectPtr) != inside) {
1534		inside = 0;
1535		goto donearea;
1536	    }
1537	}
1538
1539	/*
1540	 * Compute the polygonal shape corresponding to this edge,
1541	 * consisting of two points for the first point of the edge
1542	 * and two points for the last point of the edge.
1543	 */
1544
1545	if (count == numPoints) {
1546	    TkGetButtPoints(coordPtr+2, coordPtr, width,
1547		    0, poly, poly+2);
1548	} else if ((polyPtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
1549	    poly[0] = poly[6];
1550	    poly[1] = poly[7];
1551	    poly[2] = poly[4];
1552	    poly[3] = poly[5];
1553	} else {
1554	    TkGetButtPoints(coordPtr+2, coordPtr, width, 0,
1555		    poly, poly+2);
1556
1557	    /*
1558	     * If the last joint was beveled, then also check a
1559	     * polygon comprising the last two points of the previous
1560	     * polygon and the first two from this polygon;  this checks
1561	     * the wedges that fill the beveled joint.
1562	     */
1563
1564	    if ((polyPtr->joinStyle == JoinBevel) || changedMiterToBevel) {
1565		poly[8] = poly[0];
1566		poly[9] = poly[1];
1567		if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
1568		    inside = 0;
1569		    goto donearea;
1570		}
1571		changedMiterToBevel = 0;
1572	    }
1573	}
1574	if (count == 2) {
1575	    TkGetButtPoints(coordPtr, coordPtr+2, width,
1576		    0, poly+4, poly+6);
1577	} else if (polyPtr->joinStyle == JoinMiter) {
1578	    if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
1579		    width, poly+4, poly+6) == 0) {
1580		changedMiterToBevel = 1;
1581		TkGetButtPoints(coordPtr, coordPtr+2, width,
1582			0, poly+4, poly+6);
1583	    }
1584	} else {
1585	    TkGetButtPoints(coordPtr, coordPtr+2, width, 0,
1586		    poly+4, poly+6);
1587	}
1588	poly[8] = poly[0];
1589	poly[9] = poly[1];
1590	if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
1591	    inside = 0;
1592	    goto donearea;
1593	}
1594    }
1595
1596    donearea:
1597    if ((polyPoints != staticSpace) && (polyPoints != polyPtr->coordPtr)) {
1598	ckfree((char *) polyPoints);
1599    }
1600    return inside;
1601}
1602
1603/*
1604 *--------------------------------------------------------------
1605 *
1606 * ScalePolygon --
1607 *
1608 *	This procedure is invoked to rescale a polygon item.
1609 *
1610 * Results:
1611 *	None.
1612 *
1613 * Side effects:
1614 *	The polygon referred to by itemPtr is rescaled so that the
1615 *	following transformation is applied to all point
1616 *	coordinates:
1617 *		x' = originX + scaleX*(x-originX)
1618 *		y' = originY + scaleY*(y-originY)
1619 *
1620 *--------------------------------------------------------------
1621 */
1622
1623static void
1624ScalePolygon(canvas, itemPtr, originX, originY, scaleX, scaleY)
1625    Tk_Canvas canvas;			/* Canvas containing polygon. */
1626    Tk_Item *itemPtr;			/* Polygon to be scaled. */
1627    double originX, originY;		/* Origin about which to scale rect. */
1628    double scaleX;			/* Amount to scale in X direction. */
1629    double scaleY;			/* Amount to scale in Y direction. */
1630{
1631    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1632    double *coordPtr;
1633    int i;
1634
1635    for (i = 0, coordPtr = polyPtr->coordPtr; i < polyPtr->numPoints;
1636	    i++, coordPtr += 2) {
1637	*coordPtr = originX + scaleX*(*coordPtr - originX);
1638	coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
1639    }
1640    ComputePolygonBbox(canvas, polyPtr);
1641}
1642
1643/*
1644 *--------------------------------------------------------------
1645 *
1646 * GetPolygonIndex --
1647 *
1648 *	Parse an index into a polygon item and return either its value
1649 *	or an error.
1650 *
1651 * Results:
1652 *	A standard Tcl result.  If all went well, then *indexPtr is
1653 *	filled in with the index (into itemPtr) corresponding to
1654 *	string.  Otherwise an error message is left in
1655 *	interp->result.
1656 *
1657 * Side effects:
1658 *	None.
1659 *
1660 *--------------------------------------------------------------
1661 */
1662
1663static int
1664GetPolygonIndex(interp, canvas, itemPtr, obj, indexPtr)
1665    Tcl_Interp *interp;		/* Used for error reporting. */
1666    Tk_Canvas canvas;		/* Canvas containing item. */
1667    Tk_Item *itemPtr;		/* Item for which the index is being
1668				 * specified. */
1669    Tcl_Obj *obj;		/* Specification of a particular coord
1670				 * in itemPtr's line. */
1671    int *indexPtr;		/* Where to store converted index. */
1672{
1673    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1674    int length;
1675    char *string = Tcl_GetStringFromObj(obj, &length);
1676
1677    if (string[0] == 'e') {
1678	if (strncmp(string, "end", (unsigned) length) == 0) {
1679	    *indexPtr = 2*(polyPtr->numPoints - polyPtr->autoClosed);
1680	} else {
1681	    badIndex:
1682
1683	    /*
1684	     * Some of the paths here leave messages in interp->result,
1685	     * so we have to clear it out before storing our own message.
1686	     */
1687
1688	    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1689	    Tcl_AppendResult(interp, "bad index \"", string, "\"",
1690		    (char *) NULL);
1691	    return TCL_ERROR;
1692	}
1693    } else if (string[0] == '@') {
1694	int i;
1695	double x ,y, bestDist, dist, *coordPtr;
1696	char *end, *p;
1697
1698	p = string+1;
1699	x = strtod(p, &end);
1700	if ((end == p) || (*end != ',')) {
1701	    goto badIndex;
1702	}
1703	p = end+1;
1704	y = strtod(p, &end);
1705	if ((end == p) || (*end != 0)) {
1706	    goto badIndex;
1707	}
1708	bestDist = 1.0e36;
1709	coordPtr = polyPtr->coordPtr;
1710	*indexPtr = 0;
1711	for(i=0; i<(polyPtr->numPoints-1); i++) {
1712	    dist = hypot(coordPtr[0] - x, coordPtr[1] - y);
1713	    if (dist<bestDist) {
1714		bestDist = dist;
1715		*indexPtr = 2*i;
1716	    }
1717	    coordPtr += 2;
1718	}
1719    } else {
1720	int count = 2*(polyPtr->numPoints - polyPtr->autoClosed);
1721	if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) {
1722	    goto badIndex;
1723	}
1724	*indexPtr &= -2; /* if odd, make it even */
1725	if (count) {
1726	    if (*indexPtr > 0) {
1727		*indexPtr = ((*indexPtr - 2) % count) + 2;
1728	    } else {
1729		*indexPtr = -((-(*indexPtr)) % count);
1730	    }
1731	} else {
1732	    *indexPtr = 0;
1733	}
1734    }
1735    return TCL_OK;
1736}
1737
1738/*
1739 *--------------------------------------------------------------
1740 *
1741 * TranslatePolygon --
1742 *
1743 *	This procedure is called to move a polygon by a given
1744 *	amount.
1745 *
1746 * Results:
1747 *	None.
1748 *
1749 * Side effects:
1750 *	The position of the polygon is offset by (xDelta, yDelta),
1751 *	and the bounding box is updated in the generic part of the
1752 *	item structure.
1753 *
1754 *--------------------------------------------------------------
1755 */
1756
1757static void
1758TranslatePolygon(canvas, itemPtr, deltaX, deltaY)
1759    Tk_Canvas canvas;			/* Canvas containing item. */
1760    Tk_Item *itemPtr;			/* Item that is being moved. */
1761    double deltaX, deltaY;		/* Amount by which item is to be
1762					 * moved. */
1763{
1764    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1765    double *coordPtr;
1766    int i;
1767
1768    for (i = 0, coordPtr = polyPtr->coordPtr; i < polyPtr->numPoints;
1769	    i++, coordPtr += 2) {
1770	*coordPtr += deltaX;
1771	coordPtr[1] += deltaY;
1772    }
1773    ComputePolygonBbox(canvas, polyPtr);
1774}
1775
1776/*
1777 *--------------------------------------------------------------
1778 *
1779 * PolygonToPostscript --
1780 *
1781 *	This procedure is called to generate Postscript for
1782 *	polygon items.
1783 *
1784 * Results:
1785 *	The return value is a standard Tcl result.  If an error
1786 *	occurs in generating Postscript then an error message is
1787 *	left in the interp's result, replacing whatever used
1788 *	to be there.  If no error occurs, then Postscript for the
1789 *	item is appended to the result.
1790 *
1791 * Side effects:
1792 *	None.
1793 *
1794 *--------------------------------------------------------------
1795 */
1796
1797static int
1798PolygonToPostscript(interp, canvas, itemPtr, prepass)
1799    Tcl_Interp *interp;			/* Leave Postscript or error message
1800					 * here. */
1801    Tk_Canvas canvas;			/* Information about overall canvas. */
1802    Tk_Item *itemPtr;			/* Item for which Postscript is
1803					 * wanted. */
1804    int prepass;			/* 1 means this is a prepass to
1805					 * collect font information;  0 means
1806					 * final Postscript is being created. */
1807{
1808    PolygonItem *polyPtr = (PolygonItem *) itemPtr;
1809    char *style;
1810    XColor *color;
1811    XColor *fillColor;
1812    Pixmap stipple;
1813    Pixmap fillStipple;
1814    Tk_State state = itemPtr->state;
1815    double width;
1816
1817    if (polyPtr->numPoints<2 || polyPtr->coordPtr==NULL) {
1818	return TCL_OK;
1819    }
1820
1821    if(state == TK_STATE_NULL) {
1822	state = ((TkCanvas *)canvas)->canvas_state;
1823    }
1824    width = polyPtr->outline.width;
1825    color = polyPtr->outline.color;
1826    stipple = polyPtr->fillStipple;
1827    fillColor = polyPtr->fillColor;
1828    fillStipple = polyPtr->fillStipple;
1829    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1830	if (polyPtr->outline.activeWidth>width) {
1831	    width = polyPtr->outline.activeWidth;
1832	}
1833	if (polyPtr->outline.activeColor!=NULL) {
1834	    color = polyPtr->outline.activeColor;
1835	}
1836	if (polyPtr->outline.activeStipple!=None) {
1837	    stipple = polyPtr->outline.activeStipple;
1838	}
1839	if (polyPtr->activeFillColor!=NULL) {
1840	    fillColor = polyPtr->activeFillColor;
1841	}
1842	if (polyPtr->activeFillStipple!=None) {
1843	    fillStipple = polyPtr->activeFillStipple;
1844	}
1845    } else if (state==TK_STATE_DISABLED) {
1846	if (polyPtr->outline.disabledWidth>0.0) {
1847	    width = polyPtr->outline.disabledWidth;
1848	}
1849	if (polyPtr->outline.disabledColor!=NULL) {
1850	    color = polyPtr->outline.disabledColor;
1851	}
1852	if (polyPtr->outline.disabledStipple!=None) {
1853	    stipple = polyPtr->outline.disabledStipple;
1854	}
1855	if (polyPtr->disabledFillColor!=NULL) {
1856	    fillColor = polyPtr->disabledFillColor;
1857	}
1858	if (polyPtr->disabledFillStipple!=None) {
1859	    fillStipple = polyPtr->disabledFillStipple;
1860	}
1861    }
1862    if (polyPtr->numPoints==2) {
1863	char string[128];
1864	if (color == NULL) {
1865	    return TCL_OK;
1866	}
1867
1868	sprintf(string, "%.15g %.15g translate %.15g %.15g",
1869		polyPtr->coordPtr[0], Tk_CanvasPsY(canvas, polyPtr->coordPtr[1]),
1870		width/2.0, width/2.0);
1871	Tcl_AppendResult(interp, "matrix currentmatrix\n",string,
1872		" scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", (char *) NULL);
1873	if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
1874	    return TCL_ERROR;
1875	}
1876	if (stipple != None) {
1877	    Tcl_AppendResult(interp, "clip ", (char *) NULL);
1878	    if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
1879		return TCL_ERROR;
1880	    }
1881	} else {
1882	    Tcl_AppendResult(interp, "fill\n", (char *) NULL);
1883	}
1884	return TCL_OK;
1885    }
1886
1887    /*
1888     * Fill the area of the polygon.
1889     */
1890
1891    if (fillColor != NULL && polyPtr->numPoints>3) {
1892	if (!polyPtr->smooth || !polyPtr->smooth->postscriptProc) {
1893	    Tk_CanvasPsPath(interp, canvas, polyPtr->coordPtr,
1894		    polyPtr->numPoints);
1895	} else {
1896	    polyPtr->smooth->postscriptProc(interp, canvas, polyPtr->coordPtr,
1897		    polyPtr->numPoints, polyPtr->splineSteps);
1898	}
1899	if (Tk_CanvasPsColor(interp, canvas, fillColor) != TCL_OK) {
1900	    return TCL_ERROR;
1901	}
1902	if (fillStipple != None) {
1903	    Tcl_AppendResult(interp, "eoclip ", (char *) NULL);
1904	    if (Tk_CanvasPsStipple(interp, canvas, fillStipple)
1905		    != TCL_OK) {
1906		return TCL_ERROR;
1907	    }
1908	    if (color != NULL) {
1909		Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL);
1910	    }
1911	} else {
1912	    Tcl_AppendResult(interp, "eofill\n", (char *) NULL);
1913	}
1914    }
1915
1916    /*
1917     * Now draw the outline, if there is one.
1918     */
1919
1920    if (color != NULL) {
1921
1922	if (!polyPtr->smooth || !polyPtr->smooth->postscriptProc) {
1923	    Tk_CanvasPsPath(interp, canvas, polyPtr->coordPtr,
1924		polyPtr->numPoints);
1925	} else {
1926	    polyPtr->smooth->postscriptProc(interp, canvas, polyPtr->coordPtr,
1927		polyPtr->numPoints, polyPtr->splineSteps);
1928	}
1929
1930	if (polyPtr->joinStyle == JoinRound) {
1931	    style = "1";
1932	} else if (polyPtr->joinStyle == JoinBevel) {
1933	    style = "2";
1934	} else {
1935	    style = "0";
1936	}
1937	Tcl_AppendResult(interp, style," setlinejoin 1 setlinecap\n",
1938		(char *) NULL);
1939	if (Tk_CanvasPsOutline(canvas, itemPtr,
1940		&(polyPtr->outline)) != TCL_OK) {
1941	    return TCL_ERROR;
1942	}
1943    }
1944    return TCL_OK;
1945}
1946