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