1 /*
2  * tkCanvLine.c --
3  *
4  *	This file implements line 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-1999 by Scriptics Corporation.
9  *
10  * See the file "license.terms" for information on usage and redistribution of
11  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  */
13 
14 #include "tkInt.h"
15 #include "tkCanvas.h"
16 #include "default.h"
17 
18 /*
19  * The structure below defines the record for each line item.
20  */
21 
22 typedef enum {
23     ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH
24 } Arrows;
25 
26 typedef struct LineItem {
27     Tk_Item header;		/* Generic stuff that's the same for all
28 				 * types. MUST BE FIRST IN STRUCTURE. */
29     Tk_Outline outline;		/* Outline structure */
30     Tk_Canvas canvas;		/* Canvas containing item. Needed for parsing
31 				 * arrow shapes. */
32     int numPoints;		/* Number of points in line (always >= 0). */
33     double *coordPtr;		/* Pointer to malloc-ed array containing x-
34 				 * and y-coords of all points in line.
35 				 * X-coords are even-valued indices, y-coords
36 				 * are corresponding odd-valued indices. If
37 				 * the line has arrowheads then the first and
38 				 * last points have been adjusted to refer to
39 				 * the necks of the arrowheads rather than
40 				 * their tips. The actual endpoints are stored
41 				 * in the *firstArrowPtr and *lastArrowPtr, if
42 				 * they exist. */
43     int capStyle;		/* Cap style for line. */
44     int joinStyle;		/* Join style for line. */
45     GC arrowGC;			/* Graphics context for drawing arrowheads. */
46     Arrows arrow;		/* Indicates whether or not to draw arrowheads:
47 				 * "none", "first", "last", or "both". */
48     float arrowShapeA;		/* Distance from tip of arrowhead to center. */
49     float arrowShapeB;		/* Distance from tip of arrowhead to trailing
50 				 * point, measured along shaft. */
51     float arrowShapeC;		/* Distance of trailing points from outside
52 				 * edge of shaft. */
53     double *firstArrowPtr;	/* Points to array of PTS_IN_ARROW points
54 				 * describing polygon for arrowhead at first
55 				 * point in line. First point of arrowhead is
56 				 * tip. Malloc'ed. NULL means no arrowhead at
57 				 * first point. */
58     double *lastArrowPtr;	/* Points to polygon for arrowhead at last
59 				 * point in line (PTS_IN_ARROW points, first
60 				 * of which is tip). Malloc'ed. NULL means no
61 				 * arrowhead at last point. */
62     const Tk_SmoothMethod *smooth; /* Non-zero means draw line smoothed (i.e.
63 				 * with Bezier splines). */
64     int splineSteps;		/* Number of steps in each spline segment. */
65 } LineItem;
66 
67 /*
68  * Number of points in an arrowHead:
69  */
70 
71 #define PTS_IN_ARROW 6
72 
73 /*
74  * Prototypes for functions defined in this file:
75  */
76 
77 static int		ArrowheadPostscript(Tcl_Interp *interp,
78 			    Tk_Canvas canvas, LineItem *linePtr,
79 			    double *arrowPtr, Tcl_Obj *psObj);
80 static void		ComputeLineBbox(Tk_Canvas canvas, LineItem *linePtr);
81 static int		ConfigureLine(Tcl_Interp *interp,
82 			    Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
83 			    Tcl_Obj *const objv[], int flags);
84 static int		ConfigureArrows(Tk_Canvas canvas, LineItem *linePtr);
85 static int		CreateLine(Tcl_Interp *interp,
86 			    Tk_Canvas canvas, struct Tk_Item *itemPtr,
87 			    int objc, Tcl_Obj *const objv[]);
88 static void		DeleteLine(Tk_Canvas canvas,
89 			    Tk_Item *itemPtr, Display *display);
90 static void		DisplayLine(Tk_Canvas canvas,
91 			    Tk_Item *itemPtr, Display *display, Drawable dst,
92 			    int x, int y, int width, int height);
93 static int		GetLineIndex(Tcl_Interp *interp,
94 			    Tk_Canvas canvas, Tk_Item *itemPtr,
95 			    Tcl_Obj *obj, int *indexPtr);
96 static int		LineCoords(Tcl_Interp *interp,
97 			    Tk_Canvas canvas, Tk_Item *itemPtr,
98 			    int objc, Tcl_Obj *const objv[]);
99 static void		LineDeleteCoords(Tk_Canvas canvas,
100 			    Tk_Item *itemPtr, int first, int last);
101 static void		LineInsert(Tk_Canvas canvas,
102 			    Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj);
103 static int		LineToArea(Tk_Canvas canvas,
104 			    Tk_Item *itemPtr, double *rectPtr);
105 static double		LineToPoint(Tk_Canvas canvas,
106 			    Tk_Item *itemPtr, double *coordPtr);
107 static int		LineToPostscript(Tcl_Interp *interp,
108 			    Tk_Canvas canvas, Tk_Item *itemPtr, int prepass);
109 static int		ArrowParseProc(ClientData clientData,
110 			    Tcl_Interp *interp, Tk_Window tkwin,
111 			    const char *value, char *recordPtr, int offset);
112 static const char * ArrowPrintProc(ClientData clientData,
113 			    Tk_Window tkwin, char *recordPtr, int offset,
114 			    Tcl_FreeProc **freeProcPtr);
115 static int		ParseArrowShape(ClientData clientData,
116 			    Tcl_Interp *interp, Tk_Window tkwin,
117 			    const char *value, char *recordPtr, int offset);
118 static const char * PrintArrowShape(ClientData clientData,
119 			    Tk_Window tkwin, char *recordPtr, int offset,
120 			    Tcl_FreeProc **freeProcPtr);
121 static void		ScaleLine(Tk_Canvas canvas,
122 			    Tk_Item *itemPtr, double originX, double originY,
123 			    double scaleX, double scaleY);
124 static void		TranslateLine(Tk_Canvas canvas,
125 			    Tk_Item *itemPtr, double deltaX, double deltaY);
126 
127 /*
128  * Information used for parsing configuration specs. If you change any of the
129  * default strings, be sure to change the corresponding default values in
130  * CreateLine.
131  */
132 
133 static const Tk_CustomOption arrowShapeOption = {
134     ParseArrowShape, PrintArrowShape, NULL
135 };
136 static const Tk_CustomOption arrowOption = {
137     ArrowParseProc, ArrowPrintProc, NULL
138 };
139 static const Tk_CustomOption smoothOption = {
140     TkSmoothParseProc, TkSmoothPrintProc, NULL
141 };
142 static const Tk_CustomOption stateOption = {
143     TkStateParseProc, TkStatePrintProc, INT2PTR(2)
144 };
145 static const Tk_CustomOption tagsOption = {
146     Tk_CanvasTagsParseProc, Tk_CanvasTagsPrintProc, NULL
147 };
148 static const Tk_CustomOption dashOption = {
149     TkCanvasDashParseProc, TkCanvasDashPrintProc, NULL
150 };
151 static const Tk_CustomOption offsetOption = {
152     TkOffsetParseProc, TkOffsetPrintProc,
153     INT2PTR(TK_OFFSET_RELATIVE|TK_OFFSET_INDEX)
154 };
155 static const Tk_CustomOption pixelOption = {
156     TkPixelParseProc, TkPixelPrintProc, NULL
157 };
158 
159 static const Tk_ConfigSpec configSpecs[] = {
160     {TK_CONFIG_CUSTOM, "-activedash", NULL, NULL,
161 	NULL, Tk_Offset(LineItem, outline.activeDash),
162 	TK_CONFIG_NULL_OK, &dashOption},
163     {TK_CONFIG_COLOR, "-activefill", NULL, NULL,
164 	NULL, Tk_Offset(LineItem, outline.activeColor), TK_CONFIG_NULL_OK, NULL},
165     {TK_CONFIG_BITMAP, "-activestipple", NULL, NULL,
166 	NULL, Tk_Offset(LineItem, outline.activeStipple), TK_CONFIG_NULL_OK, NULL},
167     {TK_CONFIG_CUSTOM, "-activewidth", NULL, NULL,
168 	"0.0", Tk_Offset(LineItem, outline.activeWidth),
169 	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
170     {TK_CONFIG_CUSTOM, "-arrow", NULL, NULL,
171 	"none", Tk_Offset(LineItem, arrow),
172 	TK_CONFIG_DONT_SET_DEFAULT, &arrowOption},
173     {TK_CONFIG_CUSTOM, "-arrowshape", NULL, NULL,
174 	"8 10 3", Tk_Offset(LineItem, arrowShapeA),
175 	TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
176     {TK_CONFIG_CAP_STYLE, "-capstyle", NULL, NULL,
177 	"butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT, NULL},
178     {TK_CONFIG_COLOR, "-fill", NULL, NULL,
179 	DEF_CANVITEM_OUTLINE, Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK, NULL},
180     {TK_CONFIG_CUSTOM, "-dash", NULL, NULL,
181 	NULL, Tk_Offset(LineItem, outline.dash),
182 	TK_CONFIG_NULL_OK, &dashOption},
183     {TK_CONFIG_PIXELS, "-dashoffset", NULL, NULL,
184 	"0", Tk_Offset(LineItem, outline.offset), TK_CONFIG_DONT_SET_DEFAULT, NULL},
185     {TK_CONFIG_CUSTOM, "-disableddash", NULL, NULL,
186 	NULL, Tk_Offset(LineItem, outline.disabledDash),
187 	TK_CONFIG_NULL_OK, &dashOption},
188     {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL,
189 	NULL, Tk_Offset(LineItem, outline.disabledColor), TK_CONFIG_NULL_OK, NULL},
190     {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL,
191 	NULL, Tk_Offset(LineItem, outline.disabledStipple), TK_CONFIG_NULL_OK, NULL},
192     {TK_CONFIG_CUSTOM, "-disabledwidth", NULL, NULL,
193 	"0.0", Tk_Offset(LineItem, outline.disabledWidth),
194 	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
195     {TK_CONFIG_JOIN_STYLE, "-joinstyle", NULL, NULL,
196 	"round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT, NULL},
197     {TK_CONFIG_CUSTOM, "-offset", NULL, NULL,
198 	"0,0", Tk_Offset(LineItem, outline.tsoffset),
199 	TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},
200     {TK_CONFIG_CUSTOM, "-smooth", NULL, NULL,
201 	"0", Tk_Offset(LineItem, smooth),
202 	TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
203     {TK_CONFIG_INT, "-splinesteps", NULL, NULL,
204 	"12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT, NULL},
205     {TK_CONFIG_CUSTOM, "-state", NULL, NULL,
206 	NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, &stateOption},
207     {TK_CONFIG_BITMAP, "-stipple", NULL, NULL,
208 	NULL, Tk_Offset(LineItem, outline.stipple), TK_CONFIG_NULL_OK, NULL},
209     {TK_CONFIG_CUSTOM, "-tags", NULL, NULL,
210 	NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
211     {TK_CONFIG_CUSTOM, "-width", NULL, NULL,
212 	"1.0", Tk_Offset(LineItem, outline.width),
213 	TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
214     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0, NULL}
215 };
216 
217 /*
218  * The structures below defines the line item type by means of functions that
219  * can be invoked by generic item code.
220  */
221 
222 Tk_ItemType tkLineType = {
223     "line",				/* name */
224     sizeof(LineItem),			/* itemSize */
225     CreateLine,				/* createProc */
226     configSpecs,			/* configSpecs */
227     ConfigureLine,			/* configureProc */
228     LineCoords,				/* coordProc */
229     DeleteLine,				/* deleteProc */
230     DisplayLine,			/* displayProc */
231     TK_CONFIG_OBJS | TK_MOVABLE_POINTS,	/* flags */
232     LineToPoint,			/* pointProc */
233     LineToArea,				/* areaProc */
234     LineToPostscript,			/* postscriptProc */
235     ScaleLine,				/* scaleProc */
236     TranslateLine,			/* translateProc */
237     GetLineIndex,			/* indexProc */
238     NULL,				/* icursorProc */
239     NULL,				/* selectionProc */
240     LineInsert,				/* insertProc */
241     LineDeleteCoords,			/* dTextProc */
242     NULL,				/* nextPtr */
243     NULL, 0, NULL, NULL
244 };
245 
246 /*
247  * The definition below determines how large are static arrays used to hold
248  * spline points (splines larger than this have to have their arrays
249  * malloc-ed).
250  */
251 
252 #define MAX_STATIC_POINTS 200
253 
254 /*
255  *--------------------------------------------------------------
256  *
257  * CreateLine --
258  *
259  *	This function is invoked to create a new line item in a canvas.
260  *
261  * Results:
262  *	A standard Tcl return value. If an error occurred in creating the
263  *	item, then an error message is left in the interp's result; in this
264  *	case itemPtr is left uninitialized, so it can be safely freed by the
265  *	caller.
266  *
267  * Side effects:
268  *	A new line item is created.
269  *
270  *--------------------------------------------------------------
271  */
272 
273 static int
CreateLine(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[])274 CreateLine(
275     Tcl_Interp *interp,		/* Interpreter for error reporting. */
276     Tk_Canvas canvas,		/* Canvas to hold new item. */
277     Tk_Item *itemPtr,		/* Record to hold new item; header has been
278 				 * initialized by caller. */
279     int objc,			/* Number of arguments in objv. */
280     Tcl_Obj *const objv[])	/* Arguments describing line. */
281 {
282     LineItem *linePtr = (LineItem *) itemPtr;
283     int i;
284 
285     if (objc == 0) {
286 	Tcl_Panic("canvas did not pass any coords");
287     }
288 
289     /*
290      * Carry out initialization that is needed to set defaults and to allow
291      * proper cleanup after errors during the the remainder of this function.
292      */
293 
294     Tk_CreateOutline(&linePtr->outline);
295     linePtr->canvas = canvas;
296     linePtr->numPoints = 0;
297     linePtr->coordPtr = NULL;
298     linePtr->capStyle = CapButt;
299     linePtr->joinStyle = JoinRound;
300     linePtr->arrowGC = NULL;
301     linePtr->arrow = ARROWS_NONE;
302     linePtr->arrowShapeA = (float)8.0;
303     linePtr->arrowShapeB = (float)10.0;
304     linePtr->arrowShapeC = (float)3.0;
305     linePtr->firstArrowPtr = NULL;
306     linePtr->lastArrowPtr = NULL;
307     linePtr->smooth = NULL;
308     linePtr->splineSteps = 12;
309 
310     /*
311      * Count the number of points and then parse them into a point array.
312      * Leading arguments are assumed to be points if they start with a digit
313      * or a minus sign followed by a digit.
314      */
315 
316     for (i = 1; i < objc; i++) {
317 	const char *arg = Tcl_GetString(objv[i]);
318 
319 	if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
320 	    break;
321 	}
322     }
323     if (LineCoords(interp, canvas, itemPtr, i, objv) != TCL_OK) {
324 	goto error;
325     }
326     if (ConfigureLine(interp, canvas, itemPtr, objc-i, objv+i, 0) == TCL_OK) {
327 	return TCL_OK;
328     }
329 
330   error:
331     DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
332     return TCL_ERROR;
333 }
334 
335 /*
336  *--------------------------------------------------------------
337  *
338  * LineCoords --
339  *
340  *	This function is invoked to process the "coords" widget command on
341  *	lines. See the user documentation for details on what it does.
342  *
343  * Results:
344  *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
345  *
346  * Side effects:
347  *	The coordinates for the given item may be changed.
348  *
349  *--------------------------------------------------------------
350  */
351 
352 static int
LineCoords(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[])353 LineCoords(
354     Tcl_Interp *interp,		/* Used for error reporting. */
355     Tk_Canvas canvas,		/* Canvas containing item. */
356     Tk_Item *itemPtr,		/* Item whose coordinates are to be read or
357 				 * modified. */
358     int objc,			/* Number of coordinates supplied in objv. */
359     Tcl_Obj *const objv[])	/* Array of coordinates: x1, y1, x2, y2, ... */
360 {
361     LineItem *linePtr = (LineItem *) itemPtr;
362     int i, numPoints;
363     double *coordPtr;
364 
365     if (objc == 0) {
366 	int numCoords;
367 	Tcl_Obj *subobj, *obj = Tcl_NewObj();
368 
369 	numCoords = 2*linePtr->numPoints;
370 	if (linePtr->firstArrowPtr != NULL) {
371 	    coordPtr = linePtr->firstArrowPtr;
372 	} else {
373 	    coordPtr = linePtr->coordPtr;
374 	}
375 	for (i = 0; i < numCoords; i++, coordPtr++) {
376 	    if (i == 2) {
377 		coordPtr = linePtr->coordPtr+2;
378 	    }
379 	    if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {
380 		coordPtr = linePtr->lastArrowPtr;
381 	    }
382 	    subobj = Tcl_NewDoubleObj(*coordPtr);
383 	    Tcl_ListObjAppendElement(interp, obj, subobj);
384 	}
385 	Tcl_SetObjResult(interp, obj);
386 	return TCL_OK;
387     }
388     if (objc == 1) {
389 	if (Tcl_ListObjGetElements(interp, objv[0], &objc,
390 		(Tcl_Obj ***) &objv) != TCL_OK) {
391 	    return TCL_ERROR;
392 	}
393     }
394     if (objc & 1) {
395 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
396 		"wrong # coordinates: expected an even number, got %d",
397 		objc));
398 	Tcl_SetErrorCode(interp, "TK", "CANVAS", "COORDS", "LINE", NULL);
399 	return TCL_ERROR;
400     } else if (objc < 4) {
401 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
402 		"wrong # coordinates: expected at least 4, got %d", objc));
403 	Tcl_SetErrorCode(interp, "TK", "CANVAS", "COORDS", "LINE", NULL);
404 	return TCL_ERROR;
405     }
406 
407     numPoints = objc/2;
408     if (linePtr->numPoints != numPoints) {
409 	coordPtr = (double *)ckalloc(sizeof(double) * objc);
410 	if (linePtr->coordPtr != NULL) {
411 	    ckfree(linePtr->coordPtr);
412 	}
413 	linePtr->coordPtr = coordPtr;
414 	linePtr->numPoints = numPoints;
415     }
416     coordPtr = linePtr->coordPtr;
417     for (i = 0; i < objc ; i++) {
418 	if (Tk_CanvasGetCoordFromObj(interp, canvas, objv[i],
419 		coordPtr++) != TCL_OK) {
420 	    return TCL_ERROR;
421 	}
422     }
423 
424     /*
425      * Update arrowheads by throwing away any existing arrow-head information
426      * and calling ConfigureArrows to recompute it.
427      */
428 
429     if (linePtr->firstArrowPtr != NULL) {
430 	ckfree(linePtr->firstArrowPtr);
431 	linePtr->firstArrowPtr = NULL;
432     }
433     if (linePtr->lastArrowPtr != NULL) {
434 	ckfree(linePtr->lastArrowPtr);
435 	linePtr->lastArrowPtr = NULL;
436     }
437     if (linePtr->arrow != ARROWS_NONE) {
438 	ConfigureArrows(canvas, linePtr);
439     }
440     ComputeLineBbox(canvas, linePtr);
441     return TCL_OK;
442 }
443 
444 /*
445  *--------------------------------------------------------------
446  *
447  * ConfigureLine --
448  *
449  *	This function is invoked to configure various aspects of a line item
450  *	such as its background color.
451  *
452  * Results:
453  *	A standard Tcl result code. If an error occurs, then an error message
454  *	is left in the interp's result.
455  *
456  * Side effects:
457  *	Configuration information, such as colors and stipple patterns, may be
458  *	set for itemPtr.
459  *
460  *--------------------------------------------------------------
461  */
462 
463 static int
ConfigureLine(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[],int flags)464 ConfigureLine(
465     Tcl_Interp *interp,		/* Used for error reporting. */
466     Tk_Canvas canvas,		/* Canvas containing itemPtr. */
467     Tk_Item *itemPtr,		/* Line item to reconfigure. */
468     int objc,			/* Number of elements in objv. */
469     Tcl_Obj *const objv[],	/* Arguments describing things to configure. */
470     int flags)			/* Flags to pass to Tk_ConfigureWidget. */
471 {
472     LineItem *linePtr = (LineItem *) itemPtr;
473     XGCValues gcValues;
474     GC newGC, arrowGC;
475     unsigned long mask;
476     Tk_Window tkwin;
477     Tk_State state;
478 
479     tkwin = Tk_CanvasTkwin(canvas);
480     if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
481 	    (const char **) objv, (char *) linePtr, flags|TK_CONFIG_OBJS)) {
482 	return TCL_ERROR;
483     }
484 
485     /*
486      * A few of the options require additional processing, such as graphics
487      * contexts.
488      */
489 
490     state = itemPtr->state;
491 
492     if (state == TK_STATE_NULL) {
493 	state = Canvas(canvas)->canvas_state;
494     }
495 
496     if (linePtr->outline.activeWidth > linePtr->outline.width ||
497 	    linePtr->outline.activeDash.number != 0 ||
498 	    linePtr->outline.activeColor != NULL ||
499 	    linePtr->outline.activeStipple != None) {
500 	itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
501     } else {
502 	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
503     }
504     mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, &linePtr->outline);
505     if (mask) {
506 	if (linePtr->arrow == ARROWS_NONE) {
507 	    gcValues.cap_style = linePtr->capStyle;
508 	    mask |= GCCapStyle;
509 	}
510 	gcValues.join_style = linePtr->joinStyle;
511 	mask |= GCJoinStyle;
512 	newGC = Tk_GetGC(tkwin, mask, &gcValues);
513 #ifdef MAC_OSX_TK
514 	/*
515 	 * Mac OS X CG drawing needs access to linewidth even for arrow fills
516 	 * (as linewidth controls antialiasing).
517 	 */
518 
519 	mask |= GCLineWidth;
520 #else
521 	gcValues.line_width = 0;
522 #endif
523 	arrowGC = Tk_GetGC(tkwin, mask, &gcValues);
524     } else {
525 	newGC = arrowGC = NULL;
526     }
527     if (linePtr->outline.gc != NULL) {
528 	Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc);
529     }
530     if (linePtr->arrowGC != NULL) {
531 	Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);
532     }
533     linePtr->outline.gc = newGC;
534     linePtr->arrowGC = arrowGC;
535 
536     /*
537      * Keep spline parameters within reasonable limits.
538      */
539 
540     if (linePtr->splineSteps < 1) {
541 	linePtr->splineSteps = 1;
542     } else if (linePtr->splineSteps > 100) {
543 	linePtr->splineSteps = 100;
544     }
545 
546     if ((!linePtr->numPoints) || (state == TK_STATE_HIDDEN)) {
547 	ComputeLineBbox(canvas, linePtr);
548 	return TCL_OK;
549     }
550 
551     /*
552      * Setup arrowheads, if needed. If arrowheads are turned off, restore the
553      * line's endpoints (they were shortened when the arrowheads were added).
554      */
555 
556     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST)
557 	    && (linePtr->arrow != ARROWS_BOTH)) {
558 	linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
559 	linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
560 	ckfree(linePtr->firstArrowPtr);
561 	linePtr->firstArrowPtr = NULL;
562     }
563     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST)
564 	    && (linePtr->arrow != ARROWS_BOTH)) {
565 	int i;
566 
567 	i = 2*(linePtr->numPoints-1);
568 	linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
569 	linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
570 	ckfree(linePtr->lastArrowPtr);
571 	linePtr->lastArrowPtr = NULL;
572     }
573     if (linePtr->arrow != ARROWS_NONE) {
574 	ConfigureArrows(canvas, linePtr);
575     }
576 
577     /*
578      * Recompute bounding box for line.
579      */
580 
581     ComputeLineBbox(canvas, linePtr);
582 
583     return TCL_OK;
584 }
585 
586 /*
587  *--------------------------------------------------------------
588  *
589  * DeleteLine --
590  *
591  *	This function is called to clean up the data structure associated with
592  *	a line item.
593  *
594  * Results:
595  *	None.
596  *
597  * Side effects:
598  *	Resources associated with itemPtr are released.
599  *
600  *--------------------------------------------------------------
601  */
602 
603 static void
DeleteLine(TCL_UNUSED (Tk_Canvas),Tk_Item * itemPtr,Display * display)604 DeleteLine(
605     TCL_UNUSED(Tk_Canvas),		/* Info about overall canvas widget. */
606     Tk_Item *itemPtr,		/* Item that is being deleted. */
607     Display *display)		/* Display containing window for canvas. */
608 {
609     LineItem *linePtr = (LineItem *) itemPtr;
610 
611     Tk_DeleteOutline(display, &linePtr->outline);
612     if (linePtr->coordPtr != NULL) {
613 	ckfree(linePtr->coordPtr);
614     }
615     if (linePtr->arrowGC != NULL) {
616 	Tk_FreeGC(display, linePtr->arrowGC);
617     }
618     if (linePtr->firstArrowPtr != NULL) {
619 	ckfree(linePtr->firstArrowPtr);
620     }
621     if (linePtr->lastArrowPtr != NULL) {
622 	ckfree(linePtr->lastArrowPtr);
623     }
624 }
625 
626 /*
627  *--------------------------------------------------------------
628  *
629  * ComputeLineBbox --
630  *
631  *	This function is invoked to compute the bounding box of all the pixels
632  *	that may be drawn as part of a line.
633  *
634  * Results:
635  *	None.
636  *
637  * Side effects:
638  *	The fields x1, y1, x2, and y2 are updated in the header for itemPtr.
639  *
640  *--------------------------------------------------------------
641  */
642 
643 static void
ComputeLineBbox(Tk_Canvas canvas,LineItem * linePtr)644 ComputeLineBbox(
645     Tk_Canvas canvas,		/* Canvas that contains item. */
646     LineItem *linePtr)		/* Item whose bbos is to be recomputed. */
647 {
648     double *coordPtr;
649     int i, intWidth;
650     double width;
651     Tk_State state = linePtr->header.state;
652     Tk_TSOffset *tsoffset;
653 
654     if (state == TK_STATE_NULL) {
655 	state = Canvas(canvas)->canvas_state;
656     }
657 
658     if (!(linePtr->numPoints) || (state == TK_STATE_HIDDEN)) {
659 	linePtr->header.x1 = -1;
660 	linePtr->header.x2 = -1;
661 	linePtr->header.y1 = -1;
662 	linePtr->header.y2 = -1;
663 	return;
664     }
665 
666     width = linePtr->outline.width;
667     if (Canvas(canvas)->currentItemPtr == (Tk_Item *)linePtr) {
668 	if (linePtr->outline.activeWidth > width) {
669 	    width = linePtr->outline.activeWidth;
670 	}
671     } else if (state == TK_STATE_DISABLED) {
672 	if (linePtr->outline.disabledWidth > 0) {
673 	    width = linePtr->outline.disabledWidth;
674 	}
675     }
676 
677     coordPtr = linePtr->coordPtr;
678     linePtr->header.x1 = linePtr->header.x2 = (int) coordPtr[0];
679     linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1];
680 
681     /*
682      * Compute the bounding box of all the points in the line, then expand in
683      * all directions by the line's width to take care of butting or rounded
684      * corners and projecting or rounded caps. This expansion is an
685      * overestimate (worst-case is square root of two over two) but it's
686      * simple. Don't do anything special for curves. This causes an additional
687      * overestimate in the bounding box, but is faster.
688      */
689 
690     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
691 	    i++, coordPtr += 2) {
692 	TkIncludePoint((Tk_Item *) linePtr, coordPtr);
693     }
694     width = linePtr->outline.width;
695     if (width < 1.0) {
696 	width = 1.0;
697     }
698     if (linePtr->arrow != ARROWS_NONE) {
699 	if (linePtr->arrow != ARROWS_LAST) {
700 	    TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr);
701 	}
702 	if (linePtr->arrow != ARROWS_FIRST) {
703 	    TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr);
704 	}
705     }
706 
707     tsoffset = &linePtr->outline.tsoffset;
708     if (tsoffset->flags & TK_OFFSET_INDEX) {
709 	coordPtr = linePtr->coordPtr
710 		+ (tsoffset->flags & ~TK_OFFSET_INDEX);
711 
712 	if (tsoffset->flags <= 0) {
713 	    coordPtr = linePtr->coordPtr;
714 	    if ((linePtr->arrow == ARROWS_FIRST)
715 		    || (linePtr->arrow == ARROWS_BOTH)) {
716 		coordPtr = linePtr->firstArrowPtr;
717 	    }
718 	}
719 	if (tsoffset->flags > (linePtr->numPoints * 2)) {
720 	    coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2);
721 	    if ((linePtr->arrow == ARROWS_LAST)
722 		    || (linePtr->arrow == ARROWS_BOTH)) {
723 		coordPtr = linePtr->lastArrowPtr;
724 	    }
725 	}
726 	tsoffset->xoffset = (int) (coordPtr[0] + 0.5);
727 	tsoffset->yoffset = (int) (coordPtr[1] + 0.5);
728     } else {
729 	if (tsoffset->flags & TK_OFFSET_LEFT) {
730 	    tsoffset->xoffset = linePtr->header.x1;
731 	} else if (tsoffset->flags & TK_OFFSET_CENTER) {
732 	    tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2;
733 	} else if (tsoffset->flags & TK_OFFSET_RIGHT) {
734 	    tsoffset->xoffset = linePtr->header.x2;
735 	}
736 	if (tsoffset->flags & TK_OFFSET_TOP) {
737 	    tsoffset->yoffset = linePtr->header.y1;
738 	} else if (tsoffset->flags & TK_OFFSET_MIDDLE) {
739 	    tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2;
740 	} else if (tsoffset->flags & TK_OFFSET_BOTTOM) {
741 	    tsoffset->yoffset = linePtr->header.y2;
742 	}
743     }
744 
745     intWidth = (int) (width + 0.5);
746     linePtr->header.x1 -= intWidth;
747     linePtr->header.x2 += intWidth;
748     linePtr->header.y1 -= intWidth;
749     linePtr->header.y2 += intWidth;
750 
751     if (linePtr->numPoints == 1) {
752 	linePtr->header.x1 -= 1;
753 	linePtr->header.x2 += 1;
754 	linePtr->header.y1 -= 1;
755 	linePtr->header.y2 += 1;
756 	return;
757     }
758 
759     /*
760      * For mitered lines, make a second pass through all the points. Compute
761      * the locations of the two miter vertex points and add those into the
762      * bounding box.
763      */
764 
765     if (linePtr->joinStyle == JoinMiter) {
766 	for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
767 		i--, coordPtr += 2) {
768 	    double miter[4];
769 	    int j;
770 
771 	    if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
772 		    width, miter, miter+2)) {
773 		for (j = 0; j < 4; j += 2) {
774 		    TkIncludePoint((Tk_Item *) linePtr, miter+j);
775 		}
776 	    }
777 	}
778     }
779 
780     /*
781      * Add in the sizes of arrowheads, if any.
782      */
783 
784     if (linePtr->arrow != ARROWS_NONE) {
785 	if (linePtr->arrow != ARROWS_LAST) {
786 	    for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
787 		    i++, coordPtr += 2) {
788 		TkIncludePoint((Tk_Item *) linePtr, coordPtr);
789 	    }
790 	}
791 	if (linePtr->arrow != ARROWS_FIRST) {
792 	    for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
793 		    i++, coordPtr += 2) {
794 		TkIncludePoint((Tk_Item *) linePtr, coordPtr);
795 	    }
796 	}
797     }
798 
799     /*
800      * Add one more pixel of fudge factor just to be safe (e.g. X may round
801      * differently than we do).
802      */
803 
804     linePtr->header.x1 -= 1;
805     linePtr->header.x2 += 1;
806     linePtr->header.y1 -= 1;
807     linePtr->header.y2 += 1;
808 }
809 
810 /*
811  *--------------------------------------------------------------
812  *
813  * DisplayLine --
814  *
815  *	This function is invoked to draw a line item in a given drawable.
816  *
817  * Results:
818  *	None.
819  *
820  * Side effects:
821  *	ItemPtr is drawn in drawable using the transformation information in
822  *	canvas.
823  *
824  *--------------------------------------------------------------
825  */
826 
827 static void
DisplayLine(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display,Drawable drawable,TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int))828 DisplayLine(
829     Tk_Canvas canvas,		/* Canvas that contains item. */
830     Tk_Item *itemPtr,		/* Item to be displayed. */
831     Display *display,		/* Display on which to draw item. */
832     Drawable drawable,		/* Pixmap or window in which to draw item. */
833     TCL_UNUSED(int),		/* Describes region of canvas that must be */
834     TCL_UNUSED(int),		/* redisplayed (not used). */
835     TCL_UNUSED(int),
836     TCL_UNUSED(int))
837 {
838     LineItem *linePtr = (LineItem *) itemPtr;
839     XPoint staticPoints[MAX_STATIC_POINTS*3];
840     XPoint *pointPtr;
841     double linewidth;
842     int numPoints;
843     Tk_State state = itemPtr->state;
844 
845     if (!linePtr->numPoints || (linePtr->outline.gc == NULL)) {
846 	return;
847     }
848 
849     if (state == TK_STATE_NULL) {
850 	    state = Canvas(canvas)->canvas_state;
851     }
852     linewidth = linePtr->outline.width;
853     if (Canvas(canvas)->currentItemPtr == itemPtr) {
854 	if (linePtr->outline.activeWidth != linewidth) {
855 	    linewidth = linePtr->outline.activeWidth;
856 	}
857     } else if (state == TK_STATE_DISABLED) {
858 	if (linePtr->outline.disabledWidth != linewidth) {
859 	    linewidth = linePtr->outline.disabledWidth;
860 	}
861     }
862     /*
863      * Build up an array of points in screen coordinates. Use a static array
864      * unless the line has an enormous number of points; in this case,
865      * dynamically allocate an array. For smoothed lines, generate the curve
866      * points on each redisplay.
867      */
868 
869     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
870 	numPoints = linePtr->smooth->coordProc(canvas, NULL,
871 		linePtr->numPoints, linePtr->splineSteps, NULL, NULL);
872     } else {
873 	numPoints = linePtr->numPoints;
874     }
875 
876     if (numPoints <= MAX_STATIC_POINTS) {
877 	pointPtr = staticPoints;
878     } else {
879 	pointPtr = (XPoint *)ckalloc(numPoints * 3 * sizeof(XPoint));
880     }
881 
882     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
883 	numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
884 		linePtr->numPoints, linePtr->splineSteps, pointPtr, NULL);
885     } else {
886 	numPoints = TkCanvTranslatePath((TkCanvas *) canvas, numPoints,
887 		linePtr->coordPtr, 0, pointPtr);
888     }
889 
890     /*
891      * Display line, the free up line storage if it was dynamically allocated.
892      * If we're stippling, then modify the stipple offset in the GC. Be sure
893      * to reset the offset when done, since the GC is supposed to be
894      * read-only.
895      */
896 
897     if (Tk_ChangeOutlineGC(canvas, itemPtr, &linePtr->outline)) {
898 	Tk_CanvasSetOffset(canvas, linePtr->arrowGC,
899 		&linePtr->outline.tsoffset);
900     }
901     if (numPoints > 1) {
902 	XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints,
903 		CoordModeOrigin);
904     } else {
905 	int intwidth = (int) (linewidth + 0.5);
906 
907 	if (intwidth < 1) {
908 	    intwidth = 1;
909 	}
910 	XFillArc(display, drawable, linePtr->outline.gc,
911 		pointPtr->x - intwidth/2, pointPtr->y - intwidth/2,
912 		(unsigned) intwidth+1, (unsigned) intwidth+1, 0, 64*360);
913     }
914     if (pointPtr != staticPoints) {
915 	ckfree(pointPtr);
916     }
917 
918     /*
919      * Display arrowheads, if they are wanted.
920      */
921 
922     if (linePtr->firstArrowPtr != NULL) {
923 	TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,
924 		display, drawable, linePtr->arrowGC, NULL);
925     }
926     if (linePtr->lastArrowPtr != NULL) {
927 	TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,
928 		display, drawable, linePtr->arrowGC, NULL);
929     }
930     if (Tk_ResetOutlineGC(canvas, itemPtr, &linePtr->outline)) {
931 	XSetTSOrigin(display, linePtr->arrowGC, 0, 0);
932     }
933 }
934 
935 /*
936  *--------------------------------------------------------------
937  *
938  * LineInsert --
939  *
940  *	Insert coords into a line item at a given index.
941  *
942  * Results:
943  *	None.
944  *
945  * Side effects:
946  *	The coords in the given item is modified.
947  *
948  *--------------------------------------------------------------
949  */
950 
951 static void
LineInsert(Tk_Canvas canvas,Tk_Item * itemPtr,int beforeThis,Tcl_Obj * obj)952 LineInsert(
953     Tk_Canvas canvas,		/* Canvas containing text item. */
954     Tk_Item *itemPtr,		/* Line item to be modified. */
955     int beforeThis,		/* Index before which new coordinates are to
956 				 * be inserted. */
957     Tcl_Obj *obj)		/* New coordinates to be inserted. */
958 {
959     LineItem *linePtr = (LineItem *) itemPtr;
960     int length, objc, i;
961     double *newCoordPtr, *coordPtr;
962     Tk_State state = itemPtr->state;
963     Tcl_Obj **objv;
964 
965     if (state == TK_STATE_NULL) {
966 	state = Canvas(canvas)->canvas_state;
967     }
968 
969     if (!obj || (Tcl_ListObjGetElements(NULL, obj, &objc, &objv) != TCL_OK)
970 	    || !objc || objc&1) {
971 	return;
972     }
973     length = 2*linePtr->numPoints;
974     if (beforeThis < 0) {
975 	beforeThis = 0;
976     }
977     if (beforeThis > length) {
978 	beforeThis = length;
979     }
980     if (linePtr->firstArrowPtr != NULL) {
981 	linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
982 	linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
983     }
984     if (linePtr->lastArrowPtr != NULL) {
985 	linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];
986 	linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];
987     }
988     newCoordPtr = (double *)ckalloc(sizeof(double) * (length + objc));
989     for (i=0; i<beforeThis; i++) {
990 	newCoordPtr[i] = linePtr->coordPtr[i];
991     }
992     for (i=0; i<objc; i++) {
993 	if (Tcl_GetDoubleFromObj(NULL, objv[i],
994 		&newCoordPtr[i + beforeThis]) != TCL_OK) {
995 	    Tcl_ResetResult(Canvas(canvas)->interp);
996 	    ckfree(newCoordPtr);
997 	    return;
998 	}
999     }
1000 
1001     for (i=beforeThis; i<length; i++) {
1002 	newCoordPtr[i+objc] = linePtr->coordPtr[i];
1003     }
1004     if (linePtr->coordPtr) {
1005         ckfree(linePtr->coordPtr);
1006     }
1007     linePtr->coordPtr = newCoordPtr;
1008     length += objc ;
1009     linePtr->numPoints = length / 2;
1010 
1011     if ((length > 3) && (state != TK_STATE_HIDDEN)) {
1012 	/*
1013 	 * This is some optimizing code that will result that only the part of
1014 	 * the polygon that changed (and the objects that are overlapping with
1015 	 * that part) need to be redrawn. A special flag is set that instructs
1016 	 * the general canvas code not to redraw the whole object. If this
1017 	 * flag is not set, the canvas will do the redrawing, otherwise I have
1018 	 * to do it here.
1019 	 */
1020 
1021 	itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;
1022 
1023 	if (beforeThis > 0) {
1024 	    beforeThis -= 2;
1025 	    objc += 2;
1026 	}
1027 	if (beforeThis+objc < length) {
1028 	    objc += 2;
1029 	}
1030 	if (linePtr->smooth) {
1031 	    if (beforeThis > 0) {
1032 		beforeThis -= 2;
1033 		objc += 2;
1034 	    }
1035 	    if (beforeThis+objc+2 < length) {
1036 		objc += 2;
1037 	    }
1038 	}
1039 	itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis];
1040 	itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1];
1041 	if ((linePtr->firstArrowPtr != NULL) && (beforeThis < 1)) {
1042 	    /*
1043 	     * Include old first arrow.
1044 	     */
1045 
1046 	    for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1047 		    i++, coordPtr += 2) {
1048 		TkIncludePoint(itemPtr, coordPtr);
1049 	    }
1050 	}
1051 	if ((linePtr->lastArrowPtr != NULL) && (beforeThis+objc >= length)) {
1052 	    /*
1053 	     * Include old last arrow.
1054 	     */
1055 
1056 	    for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1057 		    i++, coordPtr += 2) {
1058 		TkIncludePoint(itemPtr, coordPtr);
1059 	    }
1060 	}
1061 	coordPtr = linePtr->coordPtr + beforeThis + 2;
1062 	for (i=2; i<objc; i+=2) {
1063 	    TkIncludePoint(itemPtr, coordPtr);
1064 	    coordPtr += 2;
1065 	}
1066     }
1067     if (linePtr->firstArrowPtr != NULL) {
1068 	ckfree(linePtr->firstArrowPtr);
1069 	linePtr->firstArrowPtr = NULL;
1070     }
1071     if (linePtr->lastArrowPtr != NULL) {
1072 	ckfree(linePtr->lastArrowPtr);
1073 	linePtr->lastArrowPtr = NULL;
1074     }
1075     if (linePtr->arrow != ARROWS_NONE) {
1076 	ConfigureArrows(canvas, linePtr);
1077     }
1078 
1079     if (itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {
1080 	double width;
1081 	int intWidth;
1082 
1083 	if ((linePtr->firstArrowPtr != NULL) && (beforeThis > 2)) {
1084 	    /*
1085 	     * Include new first arrow.
1086 	     */
1087 
1088 	    for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1089 		    i++, coordPtr += 2) {
1090 		TkIncludePoint(itemPtr, coordPtr);
1091 	    }
1092 	}
1093 	if ((linePtr->lastArrowPtr != NULL) && (beforeThis+objc < length-2)) {
1094 	    /*
1095 	     * Include new right arrow.
1096 	     */
1097 
1098 	    for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1099 		    i++, coordPtr += 2) {
1100 		TkIncludePoint(itemPtr, coordPtr);
1101 	    }
1102 	}
1103 	width = linePtr->outline.width;
1104 	if (Canvas(canvas)->currentItemPtr == itemPtr) {
1105 	    if (linePtr->outline.activeWidth > width) {
1106 		width = linePtr->outline.activeWidth;
1107 	    }
1108 	} else if (state == TK_STATE_DISABLED) {
1109 	    if (linePtr->outline.disabledWidth > 0) {
1110 		width = linePtr->outline.disabledWidth;
1111 	    }
1112 	}
1113 	intWidth = (int) (width + 0.5);
1114 	if (intWidth < 1) {
1115 	    intWidth = 1;
1116 	}
1117 	itemPtr->x1 -= intWidth;
1118 	itemPtr->y1 -= intWidth;
1119 	itemPtr->x2 += intWidth;
1120 	itemPtr->y2 += intWidth;
1121 	Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,
1122 		itemPtr->x2, itemPtr->y2);
1123     }
1124 
1125     ComputeLineBbox(canvas, linePtr);
1126 }
1127 
1128 /*
1129  *--------------------------------------------------------------
1130  *
1131  * LineDeleteCoords --
1132  *
1133  *	Delete one or more coordinates from a line item.
1134  *
1135  * Results:
1136  *	None.
1137  *
1138  * Side effects:
1139  *	Characters between "first" and "last", inclusive, get deleted from
1140  *	itemPtr.
1141  *
1142  *--------------------------------------------------------------
1143  */
1144 
1145 static void
LineDeleteCoords(Tk_Canvas canvas,Tk_Item * itemPtr,int first,int last)1146 LineDeleteCoords(
1147     Tk_Canvas canvas,		/* Canvas containing itemPtr. */
1148     Tk_Item *itemPtr,		/* Item in which to delete characters. */
1149     int first,			/* Index of first character to delete. */
1150     int last)			/* Index of last character to delete. */
1151 {
1152     LineItem *linePtr = (LineItem *) itemPtr;
1153     int count, i, first1, last1;
1154     int length = 2*linePtr->numPoints;
1155     double *coordPtr;
1156     Tk_State state = itemPtr->state;
1157 
1158     if (state == TK_STATE_NULL) {
1159 	state = Canvas(canvas)->canvas_state;
1160     }
1161 
1162     first &= -2;
1163     last &= -2;
1164 
1165     if (first < 0) {
1166 	first = 0;
1167     }
1168     if (last >= length) {
1169 	last = length-2;
1170     }
1171     if (first > last) {
1172 	return;
1173     }
1174     if (linePtr->firstArrowPtr != NULL) {
1175 	linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
1176 	linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
1177     }
1178     if (linePtr->lastArrowPtr != NULL) {
1179 	linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0];
1180 	linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1];
1181     }
1182     first1 = first;
1183     last1 = last;
1184     if (first1 > 0) {
1185 	first1 -= 2;
1186     }
1187     if (last1 < length-2) {
1188 	last1 += 2;
1189     }
1190     if (linePtr->smooth) {
1191 	if (first1 > 0) {
1192 	    first1 -= 2;
1193 	}
1194 	if (last1 < length-2) {
1195 	    last1 += 2;
1196 	}
1197     }
1198 
1199     if ((first1 >= 2) || (last1 < length-2)) {
1200 	/*
1201 	 * This is some optimizing code that will result that only the part of
1202 	 * the line that changed (and the objects that are overlapping with
1203 	 * that part) need to be redrawn. A special flag is set that instructs
1204 	 * the general canvas code not to redraw the whole object. If this
1205 	 * flag is set, the redrawing has to be done here, otherwise the
1206 	 * general Canvas code will take care of it.
1207 	 */
1208 
1209 	itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW;
1210 	itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1];
1211 	itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1];
1212 	if ((linePtr->firstArrowPtr != NULL) && (first1 < 2)) {
1213 	    /*
1214 	     * Include old first arrow.
1215 	     */
1216 
1217 	    for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1218 		    i++, coordPtr += 2) {
1219 		TkIncludePoint(itemPtr, coordPtr);
1220 	    }
1221 	}
1222 	if ((linePtr->lastArrowPtr != NULL) && (last1 >= length-2)) {
1223 	    /*
1224 	     * Include old last arrow.
1225 	     */
1226 
1227 	    for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1228 		    i++, coordPtr += 2) {
1229 		TkIncludePoint(itemPtr, coordPtr);
1230 	    }
1231 	}
1232 	coordPtr = linePtr->coordPtr+first1+2;
1233 	for (i=first1+2; i<=last1; i+=2) {
1234 	    TkIncludePoint(itemPtr, coordPtr);
1235 	    coordPtr += 2;
1236 	}
1237     }
1238 
1239     count = last + 2 - first;
1240     for (i=last+2; i<length; i++) {
1241 	linePtr->coordPtr[i-count] = linePtr->coordPtr[i];
1242     }
1243     linePtr->numPoints -= count/2;
1244     if (linePtr->firstArrowPtr != NULL) {
1245 	ckfree(linePtr->firstArrowPtr);
1246 	linePtr->firstArrowPtr = NULL;
1247     }
1248     if (linePtr->lastArrowPtr != NULL) {
1249 	ckfree(linePtr->lastArrowPtr);
1250 	linePtr->lastArrowPtr = NULL;
1251     }
1252     if (linePtr->arrow != ARROWS_NONE) {
1253 	ConfigureArrows(canvas, linePtr);
1254     }
1255     if (itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) {
1256 	double width;
1257 	int intWidth;
1258 
1259 	if ((linePtr->firstArrowPtr != NULL) && (first1 < 4)) {
1260 	    /*
1261 	     * Include new first arrow.
1262 	     */
1263 
1264 	    for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1265 		    i++, coordPtr += 2) {
1266 		TkIncludePoint(itemPtr, coordPtr);
1267 	    }
1268 	}
1269 	if ((linePtr->lastArrowPtr != NULL) && (last1 > length-4)) {
1270 	    /*
1271 	     * Include new right arrow.
1272 	     */
1273 
1274 	    for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1275 		    i++, coordPtr += 2) {
1276 		TkIncludePoint(itemPtr, coordPtr);
1277 	    }
1278 	}
1279 	width = linePtr->outline.width;
1280 	if (Canvas(canvas)->currentItemPtr == itemPtr) {
1281 	    if (linePtr->outline.activeWidth > width) {
1282 		width = linePtr->outline.activeWidth;
1283 	    }
1284 	} else if (state == TK_STATE_DISABLED) {
1285 	    if (linePtr->outline.disabledWidth > 0) {
1286 		width = linePtr->outline.disabledWidth;
1287 	    }
1288 	}
1289 	intWidth = (int) (width + 0.5);
1290 	if (intWidth < 1) {
1291 	    intWidth = 1;
1292 	}
1293 	itemPtr->x1 -= intWidth;
1294 	itemPtr->y1 -= intWidth;
1295 	itemPtr->x2 += intWidth;
1296 	itemPtr->y2 += intWidth;
1297 	Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1,
1298 		itemPtr->x2, itemPtr->y2);
1299     }
1300     ComputeLineBbox(canvas, linePtr);
1301 }
1302 
1303 /*
1304  *--------------------------------------------------------------
1305  *
1306  * LineToPoint --
1307  *
1308  *	Computes the distance from a given point to a given line, in canvas
1309  *	units.
1310  *
1311  * Results:
1312  *	The return value is 0 if the point whose x and y coordinates are
1313  *	pointPtr[0] and pointPtr[1] is inside the line. If the point isn't
1314  *	inside the line then the return value is the distance from the point
1315  *	to the line.
1316  *
1317  * Side effects:
1318  *	None.
1319  *
1320  *--------------------------------------------------------------
1321  */
1322 
1323 static double
LineToPoint(Tk_Canvas canvas,Tk_Item * itemPtr,double * pointPtr)1324 LineToPoint(
1325     Tk_Canvas canvas,		/* Canvas containing item. */
1326     Tk_Item *itemPtr,		/* Item to check against point. */
1327     double *pointPtr)		/* Pointer to x and y coordinates. */
1328 {
1329     Tk_State state = itemPtr->state;
1330     LineItem *linePtr = (LineItem *) itemPtr;
1331     double *coordPtr, *linePoints;
1332     double staticSpace[2*MAX_STATIC_POINTS];
1333     double poly[10];
1334     double bestDist, dist, width;
1335     int numPoints, count;
1336     int changedMiterToBevel;	/* Non-zero means that a mitered corner had to
1337 				 * be treated as beveled after all because the
1338 				 * angle was < 11 degrees. */
1339 
1340     bestDist = 1.0e36;
1341 
1342     /*
1343      * Handle smoothed lines by generating an expanded set of points against
1344      * which to do the check.
1345      */
1346 
1347     if (state == TK_STATE_NULL) {
1348 	state = Canvas(canvas)->canvas_state;
1349     }
1350 
1351     width = linePtr->outline.width;
1352     if (Canvas(canvas)->currentItemPtr == itemPtr) {
1353 	if (linePtr->outline.activeWidth > width) {
1354 	    width = linePtr->outline.activeWidth;
1355 	}
1356     } else if (state == TK_STATE_DISABLED) {
1357 	if (linePtr->outline.disabledWidth > 0) {
1358 	    width = linePtr->outline.disabledWidth;
1359 	}
1360     }
1361 
1362     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
1363 	numPoints = linePtr->smooth->coordProc(canvas, NULL,
1364 		linePtr->numPoints, linePtr->splineSteps, NULL, NULL);
1365 	if (numPoints <= MAX_STATIC_POINTS) {
1366 	    linePoints = staticSpace;
1367 	} else {
1368 	    linePoints = (double *)ckalloc(2 * numPoints * sizeof(double));
1369 	}
1370 	numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
1371 		linePtr->numPoints, linePtr->splineSteps, NULL, linePoints);
1372     } else {
1373 	numPoints = linePtr->numPoints;
1374 	linePoints = linePtr->coordPtr;
1375     }
1376 
1377     if (width < 1.0) {
1378 	width = 1.0;
1379     }
1380 
1381     if (!numPoints || itemPtr->state == TK_STATE_HIDDEN) {
1382 	return bestDist;
1383     } else if (numPoints == 1) {
1384 	bestDist = hypot(linePoints[0]-pointPtr[0], linePoints[1]-pointPtr[1])
1385 		- width/2.0;
1386 	if (bestDist < 0) {
1387 	    bestDist = 0;
1388 	}
1389 	return bestDist;
1390     }
1391 
1392     /*
1393      * The overall idea is to iterate through all of the edges of the line,
1394      * computing a polygon for each edge and testing the point against that
1395      * polygon. In addition, there are additional tests to deal with rounded
1396      * joints and caps.
1397      */
1398 
1399     changedMiterToBevel = 0;
1400     for (count = numPoints, coordPtr = linePoints; count >= 2;
1401 	    count--, coordPtr += 2) {
1402 	/*
1403 	 * If rounding is done around the first point then compute the
1404 	 * distance between the point and the point.
1405 	 */
1406 
1407 	if (((linePtr->capStyle == CapRound) && (count == numPoints))
1408 		|| ((linePtr->joinStyle == JoinRound)
1409 			&& (count != numPoints))) {
1410 	    dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
1411 		    - width/2.0;
1412 	    if (dist <= 0.0) {
1413 		bestDist = 0.0;
1414 		goto done;
1415 	    } else if (dist < bestDist) {
1416 		bestDist = dist;
1417 	    }
1418 	}
1419 
1420 	/*
1421 	 * Compute the polygonal shape corresponding to this edge, consisting
1422 	 * of two points for the first point of the edge and two points for
1423 	 * the last point of the edge.
1424 	 */
1425 
1426 	if (count == numPoints) {
1427 	    TkGetButtPoints(coordPtr+2, coordPtr, width,
1428 		    linePtr->capStyle == CapProjecting, poly, poly+2);
1429 	} else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
1430 	    poly[0] = poly[6];
1431 	    poly[1] = poly[7];
1432 	    poly[2] = poly[4];
1433 	    poly[3] = poly[5];
1434 	} else {
1435 	    TkGetButtPoints(coordPtr+2, coordPtr, width, 0, poly, poly+2);
1436 
1437 	    /*
1438 	     * If this line uses beveled joints, then check the distance to a
1439 	     * polygon comprising the last two points of the previous polygon
1440 	     * and the first two from this polygon; this checks the wedges
1441 	     * that fill the mitered joint.
1442 	     */
1443 
1444 	    if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
1445 		poly[8] = poly[0];
1446 		poly[9] = poly[1];
1447 		dist = TkPolygonToPoint(poly, 5, pointPtr);
1448 		if (dist <= 0.0) {
1449 		    bestDist = 0.0;
1450 		    goto done;
1451 		} else if (dist < bestDist) {
1452 		    bestDist = dist;
1453 		}
1454 		changedMiterToBevel = 0;
1455 	    }
1456 	}
1457 	if (count == 2) {
1458 	    TkGetButtPoints(coordPtr, coordPtr+2, width,
1459 		    linePtr->capStyle == CapProjecting, poly+4, poly+6);
1460 	} else if (linePtr->joinStyle == JoinMiter) {
1461 	    if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
1462 		    width, poly+4, poly+6) == 0) {
1463 		changedMiterToBevel = 1;
1464 		TkGetButtPoints(coordPtr, coordPtr+2, width, 0,
1465 			poly+4, poly+6);
1466 	    }
1467 	} else {
1468 	    TkGetButtPoints(coordPtr, coordPtr+2, width, 0,
1469 		    poly+4, poly+6);
1470 	}
1471 	poly[8] = poly[0];
1472 	poly[9] = poly[1];
1473 	dist = TkPolygonToPoint(poly, 5, pointPtr);
1474 	if (dist <= 0.0) {
1475 	    bestDist = 0.0;
1476 	    goto done;
1477 	} else if (dist < bestDist) {
1478 	    bestDist = dist;
1479 	}
1480     }
1481 
1482     /*
1483      * If caps are rounded, check the distance to the cap around the final end
1484      * point of the line.
1485      */
1486 
1487     if (linePtr->capStyle == CapRound) {
1488 	dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
1489 		- width/2.0;
1490 	if (dist <= 0.0) {
1491 	    bestDist = 0.0;
1492 	    goto done;
1493 	} else if (dist < bestDist) {
1494 	    bestDist = dist;
1495 	}
1496     }
1497 
1498     /*
1499      * If there are arrowheads, check the distance to the arrowheads.
1500      */
1501 
1502     if (linePtr->arrow != ARROWS_NONE) {
1503 	if (linePtr->arrow != ARROWS_LAST) {
1504 	    dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
1505 		    pointPtr);
1506 	    if (dist <= 0.0) {
1507 		bestDist = 0.0;
1508 		goto done;
1509 	    } else if (dist < bestDist) {
1510 		bestDist = dist;
1511 	    }
1512 	}
1513 	if (linePtr->arrow != ARROWS_FIRST) {
1514 	    dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
1515 		    pointPtr);
1516 	    if (dist <= 0.0) {
1517 		bestDist = 0.0;
1518 		goto done;
1519 	    } else if (dist < bestDist) {
1520 		bestDist = dist;
1521 	    }
1522 	}
1523     }
1524 
1525   done:
1526     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
1527 	ckfree(linePoints);
1528     }
1529     return bestDist;
1530 }
1531 
1532 /*
1533  *--------------------------------------------------------------
1534  *
1535  * LineToArea --
1536  *
1537  *	This function is called to determine whether an item lies entirely
1538  *	inside, entirely outside, or overlapping a given rectangular area.
1539  *
1540  * Results:
1541  *	-1 is returned if the item is entirely outside the area, 0 if it
1542  *	overlaps, and 1 if it is entirely inside the given area.
1543  *
1544  * Side effects:
1545  *	None.
1546  *
1547  *--------------------------------------------------------------
1548  */
1549 
1550 static int
LineToArea(Tk_Canvas canvas,Tk_Item * itemPtr,double * rectPtr)1551 LineToArea(
1552     Tk_Canvas canvas,		/* Canvas containing item. */
1553     Tk_Item *itemPtr,		/* Item to check against line. */
1554     double *rectPtr)
1555 {
1556     LineItem *linePtr = (LineItem *) itemPtr;
1557     double staticSpace[2*MAX_STATIC_POINTS];
1558     double *linePoints;
1559     int numPoints, result;
1560     double radius, width;
1561     Tk_State state = itemPtr->state;
1562 
1563     if (state == TK_STATE_NULL) {
1564 	state = Canvas(canvas)->canvas_state;
1565     }
1566     width = linePtr->outline.width;
1567     if (Canvas(canvas)->currentItemPtr == itemPtr) {
1568 	if (linePtr->outline.activeWidth > width) {
1569 	    width = linePtr->outline.activeWidth;
1570 	}
1571     } else if (state == TK_STATE_DISABLED) {
1572 	if (linePtr->outline.disabledWidth > 0) {
1573 	    width = linePtr->outline.disabledWidth;
1574 	}
1575     }
1576 
1577     radius = (width+1.0)/2.0;
1578 
1579     if ((state == TK_STATE_HIDDEN) || !linePtr->numPoints) {
1580 	return -1;
1581     } else if (linePtr->numPoints == 1) {
1582 	double oval[4];
1583 
1584 	oval[0] = linePtr->coordPtr[0]-radius;
1585 	oval[1] = linePtr->coordPtr[1]-radius;
1586 	oval[2] = linePtr->coordPtr[0]+radius;
1587 	oval[3] = linePtr->coordPtr[1]+radius;
1588 	return TkOvalToArea(oval, rectPtr);
1589     }
1590 
1591     /*
1592      * Handle smoothed lines by generating an expanded set of points against
1593      * which to do the check.
1594      */
1595 
1596     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
1597 	numPoints = linePtr->smooth->coordProc(canvas, NULL,
1598 		linePtr->numPoints, linePtr->splineSteps, NULL, NULL);
1599 	if (numPoints <= MAX_STATIC_POINTS) {
1600 	    linePoints = staticSpace;
1601 	} else {
1602 	    linePoints = (double *)ckalloc(2 * numPoints * sizeof(double));
1603 	}
1604 	numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
1605 		linePtr->numPoints, linePtr->splineSteps, NULL, linePoints);
1606     } else {
1607 	numPoints = linePtr->numPoints;
1608 	linePoints = linePtr->coordPtr;
1609     }
1610 
1611     /*
1612      * Check the segments of the line.
1613      */
1614 
1615     if (width < 1.0) {
1616 	width = 1.0;
1617     }
1618 
1619     result = TkThickPolyLineToArea(linePoints, numPoints, width,
1620 	    linePtr->capStyle, linePtr->joinStyle, rectPtr);
1621     if (result == 0) {
1622 	goto done;
1623     }
1624 
1625     /*
1626      * Check arrowheads, if any.
1627      */
1628 
1629     if (linePtr->arrow != ARROWS_NONE) {
1630 	if (linePtr->arrow != ARROWS_LAST) {
1631 	    if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
1632 		    rectPtr) != result) {
1633 		result = 0;
1634 		goto done;
1635 	    }
1636 	}
1637 	if (linePtr->arrow != ARROWS_FIRST) {
1638 	    if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
1639 		    rectPtr) != result) {
1640 		result = 0;
1641 		goto done;
1642 	    }
1643 	}
1644     }
1645 
1646   done:
1647     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
1648 	ckfree(linePoints);
1649     }
1650     return result;
1651 }
1652 
1653 /*
1654  *--------------------------------------------------------------
1655  *
1656  * ScaleLine --
1657  *
1658  *	This function is invoked to rescale a line item.
1659  *
1660  * Results:
1661  *	None.
1662  *
1663  * Side effects:
1664  *	The line referred to by itemPtr is rescaled so that the following
1665  *	transformation is applied to all point coordinates:
1666  *		x' = originX + scaleX*(x-originX)
1667  *		y' = originY + scaleY*(y-originY)
1668  *
1669  *--------------------------------------------------------------
1670  */
1671 
1672 static void
ScaleLine(Tk_Canvas canvas,Tk_Item * itemPtr,double originX,double originY,double scaleX,double scaleY)1673 ScaleLine(
1674     Tk_Canvas canvas,		/* Canvas containing line. */
1675     Tk_Item *itemPtr,		/* Line to be scaled. */
1676     double originX, double originY,
1677 				/* Origin about which to scale rect. */
1678     double scaleX,		/* Amount to scale in X direction. */
1679     double scaleY)		/* Amount to scale in Y direction. */
1680 {
1681     LineItem *linePtr = (LineItem *) itemPtr;
1682     double *coordPtr;
1683     int i;
1684 
1685     /*
1686      * Delete any arrowheads before scaling all the points (so that the
1687      * end-points of the line get restored).
1688      */
1689 
1690     if (linePtr->firstArrowPtr != NULL) {
1691 	linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
1692 	linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
1693 	ckfree(linePtr->firstArrowPtr);
1694 	linePtr->firstArrowPtr = NULL;
1695     }
1696     if (linePtr->lastArrowPtr != NULL) {
1697 	i = 2*(linePtr->numPoints-1);
1698 	linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
1699 	linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
1700 	ckfree(linePtr->lastArrowPtr);
1701 	linePtr->lastArrowPtr = NULL;
1702     }
1703     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
1704 	    i++, coordPtr += 2) {
1705 	coordPtr[0] = originX + scaleX*(*coordPtr - originX);
1706 	coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
1707     }
1708     if (linePtr->arrow != ARROWS_NONE) {
1709 	ConfigureArrows(canvas, linePtr);
1710     }
1711     ComputeLineBbox(canvas, linePtr);
1712 }
1713 
1714 /*
1715  *--------------------------------------------------------------
1716  *
1717  * GetLineIndex --
1718  *
1719  *	Parse an index into a line item and return either its value or an
1720  *	error.
1721  *
1722  * Results:
1723  *	A standard Tcl result. If all went well, then *indexPtr is filled in
1724  *	with the index (into itemPtr) corresponding to string. Otherwise an
1725  *	error message is left in interp->result.
1726  *
1727  * Side effects:
1728  *	None.
1729  *
1730  *--------------------------------------------------------------
1731  */
1732 
1733 static int
GetLineIndex(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,Tcl_Obj * obj,int * indexPtr)1734 GetLineIndex(
1735     Tcl_Interp *interp,		/* Used for error reporting. */
1736     Tk_Canvas canvas,		/* Canvas containing item. */
1737     Tk_Item *itemPtr,		/* Item for which the index is being
1738 				 * specified. */
1739     Tcl_Obj *obj,		/* Specification of a particular coord in
1740 				 * itemPtr's line. */
1741     int *indexPtr)		/* Where to store converted index. */
1742 {
1743     LineItem *linePtr = (LineItem *) itemPtr;
1744     const char *string = Tcl_GetString(obj);
1745 
1746     if (string[0] == 'e') {
1747 	if (strncmp(string, "end", obj->length) == 0) {
1748 	    *indexPtr = 2*linePtr->numPoints;
1749 	} else {
1750 	    goto badIndex;
1751 	}
1752     } else if (string[0] == '@') {
1753 	int i;
1754 	double x, y, bestDist, dist, *coordPtr;
1755 	char *end;
1756 	const char *p;
1757 
1758 	p = string+1;
1759 	x = strtod(p, &end);
1760 	if ((end == p) || (*end != ',')) {
1761 	    goto badIndex;
1762 	}
1763 	p = end+1;
1764 	y = strtod(p, &end);
1765 	if ((end == p) || (*end != 0)) {
1766 	    goto badIndex;
1767 	}
1768 	bestDist = 1.0e36;
1769 	coordPtr = linePtr->coordPtr;
1770 	*indexPtr = 0;
1771 	for (i=0; i<linePtr->numPoints; i++) {
1772 	    dist = hypot(coordPtr[0] - x, coordPtr[1] - y);
1773 	    if (dist < bestDist) {
1774 		bestDist = dist;
1775 		*indexPtr = 2*i;
1776 	    }
1777 	    coordPtr += 2;
1778 	}
1779     } else {
1780 	if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) {
1781 	    goto badIndex;
1782 	}
1783 	*indexPtr &= -2;	/* If index is odd, make it even. */
1784 	if (*indexPtr < 0){
1785 	    *indexPtr = 0;
1786 	} else if (*indexPtr > (2*linePtr->numPoints)) {
1787 	    *indexPtr = (2*linePtr->numPoints);
1788 	}
1789     }
1790     return TCL_OK;
1791 
1792     /*
1793      * Some of the paths here leave messages in interp->result, so we have to
1794      * clear it out before storing our own message.
1795      */
1796 
1797   badIndex:
1798     Tcl_ResetResult(interp);
1799     Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad index \"%s\"", string));
1800     Tcl_SetErrorCode(interp, "TK", "CANVAS", "ITEM_INDEX", "LINE", NULL);
1801     return TCL_ERROR;
1802 }
1803 
1804 /*
1805  *--------------------------------------------------------------
1806  *
1807  * TranslateLine --
1808  *
1809  *	This function is called to move a line by a given amount.
1810  *
1811  * Results:
1812  *	None.
1813  *
1814  * Side effects:
1815  *	The position of the line is offset by (xDelta, yDelta), and the
1816  *	bounding box is updated in the generic part of the item structure.
1817  *
1818  *--------------------------------------------------------------
1819  */
1820 
1821 static void
TranslateLine(Tk_Canvas canvas,Tk_Item * itemPtr,double deltaX,double deltaY)1822 TranslateLine(
1823     Tk_Canvas canvas,		/* Canvas containing item. */
1824     Tk_Item *itemPtr,		/* Item that is being moved. */
1825     double deltaX, double deltaY)
1826 				/* Amount by which item is to be moved. */
1827 {
1828     LineItem *linePtr = (LineItem *) itemPtr;
1829     double *coordPtr;
1830     int i;
1831 
1832     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
1833 	    i++, coordPtr += 2) {
1834 	coordPtr[0] += deltaX;
1835 	coordPtr[1] += deltaY;
1836     }
1837     if (linePtr->firstArrowPtr != NULL) {
1838 	for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
1839 		i++, coordPtr += 2) {
1840 	    coordPtr[0] += deltaX;
1841 	    coordPtr[1] += deltaY;
1842 	}
1843     }
1844     if (linePtr->lastArrowPtr != NULL) {
1845 	for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
1846 		i++, coordPtr += 2) {
1847 	    coordPtr[0] += deltaX;
1848 	    coordPtr[1] += deltaY;
1849 	}
1850     }
1851     ComputeLineBbox(canvas, linePtr);
1852 }
1853 
1854 /*
1855  *--------------------------------------------------------------
1856  *
1857  * ParseArrowShape --
1858  *
1859  *	This function is called back during option parsing to parse arrow
1860  *	shape information.
1861  *
1862  * Results:
1863  *	The return value is a standard Tcl result: TCL_OK means that the arrow
1864  *	shape information was parsed ok, and TCL_ERROR means it couldn't be
1865  *	parsed.
1866  *
1867  * Side effects:
1868  *	Arrow information in recordPtr is updated.
1869  *
1870  *--------------------------------------------------------------
1871  */
1872 
1873 static int
ParseArrowShape(TCL_UNUSED (void *),Tcl_Interp * interp,TCL_UNUSED (Tk_Window),const char * value,char * recordPtr,int offset)1874 ParseArrowShape(
1875     TCL_UNUSED(void *),	/* Not used. */
1876     Tcl_Interp *interp,		/* Used for error reporting. */
1877     TCL_UNUSED(Tk_Window),		/* Not used. */
1878     const char *value,		/* Textual specification of arrow shape. */
1879     char *recordPtr,		/* Pointer to item record in which to store
1880 				 * arrow information. */
1881     int offset)			/* Offset of shape information in widget
1882 				 * record. */
1883 {
1884     LineItem *linePtr = (LineItem *) recordPtr;
1885     double a, b, c;
1886     int argc;
1887     const char **argv = NULL;
1888 
1889     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
1890 	Tcl_Panic("ParseArrowShape received bogus offset");
1891     }
1892 
1893     if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) {
1894 	goto syntaxError;
1895     } else if (argc != 3) {
1896 	goto syntaxError;
1897     }
1898     if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)
1899 	    || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b)
1900 		!= TCL_OK)
1901 	    || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c)
1902 		!= TCL_OK)) {
1903 	goto syntaxError;
1904     }
1905 
1906     linePtr->arrowShapeA = (float) a;
1907     linePtr->arrowShapeB = (float) b;
1908     linePtr->arrowShapeC = (float) c;
1909     ckfree(argv);
1910     return TCL_OK;
1911 
1912   syntaxError:
1913     Tcl_ResetResult(interp);
1914     Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1915 	    "bad arrow shape \"%s\": must be list with three numbers",
1916 	    value));
1917     Tcl_SetErrorCode(interp, "TK", "CANVAS", "ARROW_SHAPE", NULL);
1918     if (argv != NULL) {
1919 	ckfree(argv);
1920     }
1921     return TCL_ERROR;
1922 }
1923 
1924 /*
1925  *--------------------------------------------------------------
1926  *
1927  * PrintArrowShape --
1928  *
1929  *	This function is a callback invoked by the configuration code to
1930  *	return a printable value describing an arrow shape.
1931  *
1932  * Results:
1933  *	None.
1934  *
1935  * Side effects:
1936  *	None.
1937  *
1938  *--------------------------------------------------------------
1939  */
1940 
1941 static const char *
PrintArrowShape(TCL_UNUSED (void *),TCL_UNUSED (Tk_Window),char * recordPtr,TCL_UNUSED (int),Tcl_FreeProc ** freeProcPtr)1942 PrintArrowShape(
1943     TCL_UNUSED(void *),	/* Not used. */
1944     TCL_UNUSED(Tk_Window),		/* Window associated with linePtr's widget. */
1945     char *recordPtr,		/* Pointer to item record containing current
1946 				 * shape information. */
1947     TCL_UNUSED(int),			/* Offset of arrow information in record. */
1948     Tcl_FreeProc **freeProcPtr)	/* Store address of function to call to free
1949 				 * string here. */
1950 {
1951     LineItem *linePtr = (LineItem *) recordPtr;
1952     char *buffer = (char *)ckalloc(120);
1953 
1954     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
1955 	    linePtr->arrowShapeB, linePtr->arrowShapeC);
1956     *freeProcPtr = TCL_DYNAMIC;
1957     return buffer;
1958 }
1959 
1960 /*
1961  *--------------------------------------------------------------
1962  *
1963  * ArrowParseProc --
1964  *
1965  *	This function is invoked during option processing to handle the
1966  *	"-arrow" option.
1967  *
1968  * Results:
1969  *	A standard Tcl return value.
1970  *
1971  * Side effects:
1972  *	The arrow for a given item gets replaced by the arrow indicated in the
1973  *	value argument.
1974  *
1975  *--------------------------------------------------------------
1976  */
1977 
1978 static int
ArrowParseProc(TCL_UNUSED (void *),Tcl_Interp * interp,TCL_UNUSED (Tk_Window),const char * value,char * widgRec,int offset)1979 ArrowParseProc(
1980     TCL_UNUSED(void *),
1981     Tcl_Interp *interp,		/* Used for reporting errors. */
1982     TCL_UNUSED(Tk_Window),		/* Window containing canvas widget. */
1983     const char *value,		/* Value of option. */
1984     char *widgRec,		/* Pointer to record for item. */
1985     int offset)			/* Offset into item. */
1986 {
1987     int c;
1988     size_t length;
1989     Arrows *arrowPtr = (Arrows *) (widgRec + offset);
1990 
1991     if (value == NULL || *value == 0) {
1992 	*arrowPtr = ARROWS_NONE;
1993 	return TCL_OK;
1994     }
1995 
1996     c = value[0];
1997     length = strlen(value);
1998 
1999     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
2000 	*arrowPtr = ARROWS_NONE;
2001 	return TCL_OK;
2002     }
2003     if ((c == 'f') && (strncmp(value, "first", length) == 0)) {
2004 	*arrowPtr = ARROWS_FIRST;
2005 	return TCL_OK;
2006     }
2007     if ((c == 'l') && (strncmp(value, "last", length) == 0)) {
2008 	*arrowPtr = ARROWS_LAST;
2009 	return TCL_OK;
2010     }
2011     if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
2012 	*arrowPtr = ARROWS_BOTH;
2013 	return TCL_OK;
2014     }
2015 
2016     Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2017 	    "bad arrow spec \"%s\": must be none, first, last, or both",
2018 	    value));
2019     Tcl_SetErrorCode(interp, "TK", "CANVAS", "ARROW", NULL);
2020     *arrowPtr = ARROWS_NONE;
2021     return TCL_ERROR;
2022 }
2023 
2024 /*
2025  *--------------------------------------------------------------
2026  *
2027  * ArrowPrintProc --
2028  *
2029  *	This function is invoked by the Tk configuration code to produce a
2030  *	printable string for the "-arrow" configuration option.
2031  *
2032  * Results:
2033  *	The return value is a string describing the arrows for the item
2034  *	referred to by "widgRec". In addition, *freeProcPtr is filled in with
2035  *	the address of a function to call to free the result string when it's
2036  *	no longer needed (or NULL to indicate that the string doesn't need to
2037  *	be freed).
2038  *
2039  * Side effects:
2040  *	None.
2041  *
2042  *--------------------------------------------------------------
2043  */
2044 
2045 static const char *
ArrowPrintProc(TCL_UNUSED (void *),TCL_UNUSED (Tk_Window),char * widgRec,int offset,TCL_UNUSED (Tcl_FreeProc **))2046 ArrowPrintProc(
2047     TCL_UNUSED(void *),	/* Ignored. */
2048     TCL_UNUSED(Tk_Window),		/* Window containing canvas widget. */
2049     char *widgRec,		/* Pointer to record for item. */
2050     int offset,			/* Offset into item. */
2051     TCL_UNUSED(Tcl_FreeProc **))	/* Pointer to variable to fill in with
2052 				 * information about how to reclaim storage
2053 				 * for return string. */
2054 {
2055     Arrows *arrowPtr = (Arrows *) (widgRec + offset);
2056 
2057     switch (*arrowPtr) {
2058     case ARROWS_FIRST:
2059 	return "first";
2060     case ARROWS_LAST:
2061 	return "last";
2062     case ARROWS_BOTH:
2063 	return "both";
2064     default:
2065 	return "none";
2066     }
2067 }
2068 
2069 /*
2070  *--------------------------------------------------------------
2071  *
2072  * ConfigureArrows --
2073  *
2074  *	If arrowheads have been requested for a line, this function makes
2075  *	arrangements for the arrowheads.
2076  *
2077  * Results:
2078  *	Always returns TCL_OK.
2079  *
2080  * Side effects:
2081  *	Information in linePtr is set up for one or two arrowheads. The
2082  *	firstArrowPtr and lastArrowPtr polygons are allocated and initialized,
2083  *	if need be, and the end points of the line are adjusted so that a
2084  *	thick line doesn't stick out past the arrowheads.
2085  *
2086  *--------------------------------------------------------------
2087  */
2088 
2089 static int
ConfigureArrows(Tk_Canvas canvas,LineItem * linePtr)2090 ConfigureArrows(
2091     Tk_Canvas canvas,		/* Canvas in which arrows will be displayed
2092 				 * (interp and tkwin fields are needed). */
2093     LineItem *linePtr)		/* Item to configure for arrows. */
2094 {
2095     double *poly, *coordPtr;
2096     double dx, dy, length, sinTheta, cosTheta, temp;
2097     double fracHeight;		/* Line width as fraction of arrowhead
2098 				 * width. */
2099     double backup;		/* Distance to backup end points so the line
2100 				 * ends in the middle of the arrowhead. */
2101     double vertX, vertY;	/* Position of arrowhead vertex. */
2102     double shapeA, shapeB, shapeC;
2103 				/* Adjusted coordinates (see explanation
2104 				 * below). */
2105     double width;
2106     Tk_State state = linePtr->header.state;
2107 
2108     if (linePtr->numPoints < 2) {
2109 	return TCL_OK;
2110     }
2111 
2112     if (state == TK_STATE_NULL) {
2113 	state = Canvas(canvas)->canvas_state;
2114     }
2115 
2116     width = linePtr->outline.width;
2117     if (Canvas(canvas)->currentItemPtr == (Tk_Item *)linePtr) {
2118 	if (linePtr->outline.activeWidth > width) {
2119 	    width = linePtr->outline.activeWidth;
2120 	}
2121     } else if (state == TK_STATE_DISABLED) {
2122 	if (linePtr->outline.disabledWidth > 0) {
2123 	    width = linePtr->outline.disabledWidth;
2124 	}
2125     }
2126 
2127     /*
2128      * The code below makes a tiny increase in the shape parameters for the
2129      * line. This is a bit of a hack, but it seems to result in displays that
2130      * more closely approximate the specified parameters. Without the
2131      * adjustment, the arrows come out smaller than expected.
2132      */
2133 
2134     shapeA = linePtr->arrowShapeA + 0.001;
2135     shapeB = linePtr->arrowShapeB + 0.001;
2136     shapeC = linePtr->arrowShapeC + width/2.0 + 0.001;
2137 
2138     /*
2139      * If there's an arrowhead on the first point of the line, compute its
2140      * polygon and adjust the first point of the line so that the line doesn't
2141      * stick out past the leading edge of the arrowhead.
2142      */
2143 
2144     fracHeight = (width/2.0)/shapeC;
2145     backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;
2146     if (linePtr->arrow != ARROWS_LAST) {
2147 	poly = linePtr->firstArrowPtr;
2148 	if (poly == NULL) {
2149 	    poly = (double *)ckalloc(2 * PTS_IN_ARROW * sizeof(double));
2150 	    poly[0] = poly[10] = linePtr->coordPtr[0];
2151 	    poly[1] = poly[11] = linePtr->coordPtr[1];
2152 	    linePtr->firstArrowPtr = poly;
2153 	}
2154 	dx = poly[0] - linePtr->coordPtr[2];
2155 	dy = poly[1] - linePtr->coordPtr[3];
2156 	length = hypot(dx, dy);
2157 	if (length == 0) {
2158 	    sinTheta = cosTheta = 0.0;
2159 	} else {
2160 	    sinTheta = dy/length;
2161 	    cosTheta = dx/length;
2162 	}
2163 	vertX = poly[0] - shapeA*cosTheta;
2164 	vertY = poly[1] - shapeA*sinTheta;
2165 	temp = shapeC*sinTheta;
2166 	poly[2] = poly[0] - shapeB*cosTheta + temp;
2167 	poly[8] = poly[2] - 2*temp;
2168 	temp = shapeC*cosTheta;
2169 	poly[3] = poly[1] - shapeB*sinTheta - temp;
2170 	poly[9] = poly[3] + 2*temp;
2171 	poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
2172 	poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
2173 	poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
2174 	poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
2175 
2176 	/*
2177 	 * Polygon done. Now move the first point towards the second so that
2178 	 * the corners at the end of the line are inside the arrowhead.
2179 	 */
2180 
2181 	linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
2182 	linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
2183     }
2184 
2185     /*
2186      * Similar arrowhead calculation for the last point of the line.
2187      */
2188 
2189     if (linePtr->arrow != ARROWS_FIRST) {
2190 	coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
2191 	poly = linePtr->lastArrowPtr;
2192 	if (poly == NULL) {
2193 	    poly = (double *)ckalloc(2 * PTS_IN_ARROW * sizeof(double));
2194 	    poly[0] = poly[10] = coordPtr[2];
2195 	    poly[1] = poly[11] = coordPtr[3];
2196 	    linePtr->lastArrowPtr = poly;
2197 	}
2198 	dx = poly[0] - coordPtr[0];
2199 	dy = poly[1] - coordPtr[1];
2200 	length = hypot(dx, dy);
2201 	if (length == 0) {
2202 	    sinTheta = cosTheta = 0.0;
2203 	} else {
2204 	    sinTheta = dy/length;
2205 	    cosTheta = dx/length;
2206 	}
2207 	vertX = poly[0] - shapeA*cosTheta;
2208 	vertY = poly[1] - shapeA*sinTheta;
2209 	temp = shapeC * sinTheta;
2210 	poly[2] = poly[0] - shapeB*cosTheta + temp;
2211 	poly[8] = poly[2] - 2*temp;
2212 	temp = shapeC * cosTheta;
2213 	poly[3] = poly[1] - shapeB*sinTheta - temp;
2214 	poly[9] = poly[3] + 2*temp;
2215 	poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
2216 	poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
2217 	poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
2218 	poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
2219 	coordPtr[2] = poly[0] - backup*cosTheta;
2220 	coordPtr[3] = poly[1] - backup*sinTheta;
2221     }
2222 
2223     return TCL_OK;
2224 }
2225 
2226 /*
2227  *--------------------------------------------------------------
2228  *
2229  * LineToPostscript --
2230  *
2231  *	This function is called to generate Postscript for line items.
2232  *
2233  * Results:
2234  *	The return value is a standard Tcl result. If an error occurs in
2235  *	generating Postscript then an error message is left in the interp's
2236  *	result, replacing whatever used to be there. If no error occurs, then
2237  *	Postscript for the item is appended to the result.
2238  *
2239  * Side effects:
2240  *	None.
2241  *
2242  *--------------------------------------------------------------
2243  */
2244 
2245 static int
LineToPostscript(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,TCL_UNUSED (int))2246 LineToPostscript(
2247     Tcl_Interp *interp,		/* Leave Postscript or error message here. */
2248     Tk_Canvas canvas,		/* Information about overall canvas. */
2249     Tk_Item *itemPtr,		/* Item for which Postscript is wanted. */
2250     TCL_UNUSED(int))		/* 1 means this is a prepass to collect font
2251 				 * information; 0 means final Postscript is
2252 				 * being created. */
2253 {
2254     LineItem *linePtr = (LineItem *) itemPtr;
2255     int style;
2256     double width;
2257     XColor *color;
2258     Pixmap stipple;
2259     Tk_State state = itemPtr->state;
2260     Tcl_Obj *psObj;
2261     Tcl_InterpState interpState;
2262 
2263     if (state == TK_STATE_NULL) {
2264 	state = Canvas(canvas)->canvas_state;
2265     }
2266 
2267     width = linePtr->outline.width;
2268     color = linePtr->outline.color;
2269     stipple = linePtr->outline.stipple;
2270     if (Canvas(canvas)->currentItemPtr == itemPtr) {
2271 	if (linePtr->outline.activeWidth > width) {
2272 	    width = linePtr->outline.activeWidth;
2273 	}
2274 	if (linePtr->outline.activeColor != NULL) {
2275 	    color = linePtr->outline.activeColor;
2276 	}
2277 	if (linePtr->outline.activeStipple != None) {
2278 	    stipple = linePtr->outline.activeStipple;
2279 	}
2280     } else if (state == TK_STATE_DISABLED) {
2281 	if (linePtr->outline.disabledWidth > 0) {
2282 	    width = linePtr->outline.disabledWidth;
2283 	}
2284 	if (linePtr->outline.disabledColor != NULL) {
2285 	    color = linePtr->outline.disabledColor;
2286 	}
2287 	if (linePtr->outline.disabledStipple != None) {
2288 	    stipple = linePtr->outline.disabledStipple;
2289 	}
2290     }
2291 
2292     if (color == NULL || linePtr->numPoints < 1 || linePtr->coordPtr == NULL){
2293 	return TCL_OK;
2294     }
2295 
2296     /*
2297      * Make our working space.
2298      */
2299 
2300     psObj = Tcl_NewObj();
2301     interpState = Tcl_SaveInterpState(interp, TCL_OK);
2302 
2303     /*
2304      * Check if we're just doing a "pixel".
2305      */
2306 
2307     if (linePtr->numPoints == 1) {
2308 	Tcl_AppendToObj(psObj, "matrix currentmatrix\n", -1);
2309 	Tcl_AppendPrintfToObj(psObj, "%.15g %.15g translate %.15g %.15g",
2310 		linePtr->coordPtr[0], Tk_CanvasPsY(canvas, linePtr->coordPtr[1]),
2311 		width/2.0, width/2.0);
2312 	Tcl_AppendToObj(psObj,
2313 		" scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", -1);
2314 
2315 	Tcl_ResetResult(interp);
2316 	if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
2317 	    goto error;
2318 	}
2319 	Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2320 
2321 	if (stipple != None) {
2322 	    Tcl_AppendToObj(psObj, "clip ", -1);
2323 	    Tcl_ResetResult(interp);
2324 	    if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
2325 		goto error;
2326 	    }
2327 	    Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2328 	} else {
2329 	    Tcl_AppendToObj(psObj, "fill\n", -1);
2330 	}
2331 	goto done;
2332     }
2333 
2334     /*
2335      * Generate a path for the line's center-line (do this differently for
2336      * straight lines and smoothed lines).
2337      */
2338 
2339     Tcl_ResetResult(interp);
2340     if ((!linePtr->smooth) || (linePtr->numPoints < 3)) {
2341 	Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints);
2342     } else if ((stipple == None) && linePtr->smooth->postscriptProc) {
2343 	linePtr->smooth->postscriptProc(interp, canvas, linePtr->coordPtr,
2344 		linePtr->numPoints, linePtr->splineSteps);
2345     } else {
2346 	/*
2347 	 * Special hack: Postscript printers don't appear to be able to turn a
2348 	 * path drawn with "curveto"s into a clipping path without exceeding
2349 	 * resource limits, so TkMakeBezierPostscript won't work for stippled
2350 	 * curves. Instead, generate all of the intermediate points here and
2351 	 * output them into the Postscript file with "lineto"s instead.
2352 	 */
2353 
2354 	double staticPoints[2*MAX_STATIC_POINTS];
2355 	double *pointPtr;
2356 	int numPoints;
2357 
2358 	numPoints = linePtr->smooth->coordProc(canvas, NULL,
2359 		linePtr->numPoints, linePtr->splineSteps, NULL, NULL);
2360 	pointPtr = staticPoints;
2361 	if (numPoints > MAX_STATIC_POINTS) {
2362 	    pointPtr = (double *)ckalloc(numPoints * 2 * sizeof(double));
2363 	}
2364 	numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr,
2365 		linePtr->numPoints, linePtr->splineSteps, NULL, pointPtr);
2366 	Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints);
2367 	if (pointPtr != staticPoints) {
2368 	    ckfree(pointPtr);
2369 	}
2370     }
2371     Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2372 
2373     /*
2374      * Set other line-drawing parameters and stroke out the line.
2375      */
2376 
2377     if (linePtr->capStyle == CapRound) {
2378 	style = 1;
2379     } else if (linePtr->capStyle == CapProjecting) {
2380 	style = 2;
2381     } else {
2382 	style = 0;
2383     }
2384     Tcl_AppendPrintfToObj(psObj, "%d setlinecap\n", style);
2385     if (linePtr->joinStyle == JoinRound) {
2386 	style = 1;
2387     } else if (linePtr->joinStyle == JoinBevel) {
2388 	style = 2;
2389     } else {
2390 	style = 0;
2391     }
2392     Tcl_AppendPrintfToObj(psObj, "%d setlinejoin\n", style);
2393 
2394     Tcl_ResetResult(interp);
2395     if (Tk_CanvasPsOutline(canvas, itemPtr, &linePtr->outline) != TCL_OK) {
2396 	goto error;
2397     }
2398     Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2399 
2400     /*
2401      * Output polygons for the arrowheads, if there are any.
2402      */
2403 
2404     if (linePtr->firstArrowPtr != NULL) {
2405 	if (stipple != None) {
2406 	    Tcl_AppendToObj(psObj, "grestore gsave\n", -1);
2407 	}
2408 	if (ArrowheadPostscript(interp, canvas, linePtr,
2409 		linePtr->firstArrowPtr, psObj) != TCL_OK) {
2410 	    goto error;
2411 	}
2412     }
2413     if (linePtr->lastArrowPtr != NULL) {
2414 	if (stipple != None) {
2415 	    Tcl_AppendToObj(psObj, "grestore gsave\n", -1);
2416 	}
2417 	if (ArrowheadPostscript(interp, canvas, linePtr,
2418 		linePtr->lastArrowPtr, psObj) != TCL_OK) {
2419 	    goto error;
2420 	}
2421     }
2422 
2423     /*
2424      * Plug the accumulated postscript back into the result.
2425      */
2426 
2427   done:
2428     (void) Tcl_RestoreInterpState(interp, interpState);
2429     Tcl_AppendObjToObj(Tcl_GetObjResult(interp), psObj);
2430     Tcl_DecrRefCount(psObj);
2431     return TCL_OK;
2432 
2433   error:
2434     Tcl_DiscardInterpState(interpState);
2435     Tcl_DecrRefCount(psObj);
2436     return TCL_ERROR;
2437 }
2438 
2439 /*
2440  *--------------------------------------------------------------
2441  *
2442  * ArrowheadPostscript --
2443  *
2444  *	This function is called to generate Postscript for an arrowhead for a
2445  *	line item.
2446  *
2447  * Results:
2448  *	The return value is a standard Tcl result. If an error occurs in
2449  *	generating Postscript then an error message is left in the interp's
2450  *	result, replacing whatever used to be there. If no error occurs, then
2451  *	Postscript for the arrowhead is appended to the given object.
2452  *
2453  * Side effects:
2454  *	None.
2455  *
2456  *--------------------------------------------------------------
2457  */
2458 
2459 static int
ArrowheadPostscript(Tcl_Interp * interp,Tk_Canvas canvas,LineItem * linePtr,double * arrowPtr,Tcl_Obj * psObj)2460 ArrowheadPostscript(
2461     Tcl_Interp *interp,		/* Leave error message here; non-error results
2462 				 * will be discarded by caller. */
2463     Tk_Canvas canvas,		/* Information about overall canvas. */
2464     LineItem *linePtr,		/* Line item for which Postscript is being
2465 				 * generated. */
2466     double *arrowPtr,		/* Pointer to first of five points describing
2467 				 * arrowhead polygon. */
2468     Tcl_Obj *psObj)		/* Append postscript to this object. */
2469 {
2470     Pixmap stipple;
2471     Tk_State state = linePtr->header.state;
2472 
2473     if (state == TK_STATE_NULL) {
2474 	state = Canvas(canvas)->canvas_state;
2475     }
2476 
2477     stipple = linePtr->outline.stipple;
2478     if (Canvas(canvas)->currentItemPtr == (Tk_Item *) linePtr) {
2479 	if (linePtr->outline.activeStipple!=None) {
2480 	    stipple = linePtr->outline.activeStipple;
2481 	}
2482     } else if (state == TK_STATE_DISABLED) {
2483 	if (linePtr->outline.activeStipple!=None) {
2484 	    stipple = linePtr->outline.disabledStipple;
2485 	}
2486     }
2487 
2488     Tcl_ResetResult(interp);
2489     Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW);
2490     Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2491 
2492     if (stipple != None) {
2493 	Tcl_AppendToObj(psObj, "clip ", -1);
2494 
2495 	Tcl_ResetResult(interp);
2496 	if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
2497 	    return TCL_ERROR;
2498 	}
2499 	Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
2500     } else {
2501 	Tcl_AppendToObj(psObj, "fill\n", -1);
2502     }
2503     return TCL_OK;
2504 }
2505 
2506 /*
2507  * Local Variables:
2508  * mode: c
2509  * c-basic-offset: 4
2510  * fill-column: 78
2511  * End:
2512  */
2513