4 * This file implements line items for canvas widgets.
6 * Copyright 1991-1992 Regents of the University of California.
7 * Permission to use, copy, modify, and distribute this
8 * software and its documentation for any purpose and without
9 * fee is hereby granted, provided that the above copyright
10 * notice appear in all copies. The University of California
11 * makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without
13 * express or implied warranty.
17 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkCanvLine.c,v 1.7 92/07/28 15:40:08 ouster Exp $ SPRITE (Berkeley)";
27 * The structure below defines the record for each line item.
30 typedef struct LineItem
{
31 Tk_Item header
; /* Generic stuff that's the same for all
32 * types. MUST BE FIRST IN STRUCTURE. */
33 Tk_Canvas
*canvasPtr
; /* Canvas containing item. Needed for
34 * parsing arrow shapes. */
35 int numPoints
; /* Number of points in line (always >= 2). */
36 double *coordPtr
; /* Pointer to malloc-ed array containing
37 * x- and y-coords of all points in line.
38 * X-coords are even-valued indices, y-coords
39 * are corresponding odd-valued indices. */
40 int width
; /* Width of line. */
41 XColor
*fg
; /* Foreground color for line. */
42 Pixmap fillStipple
; /* Stipple bitmap for filling line. */
43 int capStyle
; /* Cap style for line. */
44 int joinStyle
; /* Join style for line. */
45 GC gc
; /* Graphics context for filling line. */
46 Tk_Uid 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
53 double *firstArrowPtr
; /* Points to array of 5 points describing
54 * polygon for arrowhead at first point in
55 * line. First point of arrowhead is tip.
56 * Malloc'ed. NULL means no arrowhead at
58 double *lastArrowPtr
; /* Points to polygon for arrowhead at last
59 * point in line (5 points, first of which
60 * is tip). Malloc'ed. NULL means no
61 * arrowhead at last point. */
62 int smooth
; /* Non-zero means draw line smoothed (i.e.
63 * with Bezier splines). */
64 int splineSteps
; /* Number of steps in each spline segment. */
68 * Number of points in an arrowHead:
71 #define PTS_IN_ARROW 6
74 * Prototypes for procedures defined in this file:
77 static void ComputeLineBbox
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
79 static int ConfigureLine
_ANSI_ARGS_((
80 Tk_Canvas
*canvasPtr
, Tk_Item
*itemPtr
, int argc
,
81 char **argv
, int flags
));
82 static int ConfigureArrows
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
84 static int CreateLine
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
85 struct Tk_Item
*itemPtr
, int argc
, char **argv
));
86 static void DeleteLine
_ANSI_ARGS_((Tk_Item
*itemPtr
));
87 static void DisplayLine
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
88 Tk_Item
*itemPtr
, Drawable dst
));
89 static int LineCoords
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
90 Tk_Item
*itemPtr
, int argc
, char **argv
));
91 static int LineToArea
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
92 Tk_Item
*itemPtr
, double *rectPtr
));
93 static double LineToPoint
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
94 Tk_Item
*itemPtr
, double *coordPtr
));
95 static int ParseArrowShape
_ANSI_ARGS_((ClientData clientData
,
96 Tcl_Interp
*interp
, Tk_Window tkwin
, char *value
,
97 char *recordPtr
, int offset
));
98 static char * PrintArrowShape
_ANSI_ARGS_((ClientData clientData
,
99 Tk_Window tkwin
, char *recordPtr
, int offset
,
100 Tcl_FreeProc
**freeProcPtr
));
101 static void ScaleLine
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
102 Tk_Item
*itemPtr
, double originX
, double originY
,
103 double scaleX
, double scaleY
));
104 static void TranslateLine
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
105 Tk_Item
*itemPtr
, double deltaX
, double deltaY
));
108 * Information used for parsing configuration specs. If you change any
109 * of the default strings, be sure to change the corresponding default
110 * values in CreateLine.
113 static Tk_CustomOption arrowShapeOption
= {ParseArrowShape
,
114 PrintArrowShape
, (ClientData
) NULL
};
116 static Tk_ConfigSpec configSpecs
[] = {
117 {TK_CONFIG_UID
, "-arrow", (char *) NULL
, (char *) NULL
,
118 "none", Tk_Offset(LineItem
, arrow
), TK_CONFIG_DONT_SET_DEFAULT
},
119 {TK_CONFIG_CUSTOM
, "-arrowshape", (char *) NULL
, (char *) NULL
,
120 "8 10 3", Tk_Offset(LineItem
, arrowShapeA
),
121 TK_CONFIG_DONT_SET_DEFAULT
, &arrowShapeOption
},
122 {TK_CONFIG_CAP_STYLE
, "-capstyle", (char *) NULL
, (char *) NULL
,
123 "butt", Tk_Offset(LineItem
, capStyle
), TK_CONFIG_DONT_SET_DEFAULT
},
124 {TK_CONFIG_COLOR
, "-fill", (char *) NULL
, (char *) NULL
,
125 "black", Tk_Offset(LineItem
, fg
), 0},
126 {TK_CONFIG_JOIN_STYLE
, "-joinstyle", (char *) NULL
, (char *) NULL
,
127 "round", Tk_Offset(LineItem
, joinStyle
), TK_CONFIG_DONT_SET_DEFAULT
},
128 {TK_CONFIG_BOOLEAN
, "-smooth", (char *) NULL
, (char *) NULL
,
129 "no", Tk_Offset(LineItem
, smooth
), TK_CONFIG_DONT_SET_DEFAULT
},
130 {TK_CONFIG_INT
, "-splinesteps", (char *) NULL
, (char *) NULL
,
131 "12", Tk_Offset(LineItem
, splineSteps
), TK_CONFIG_DONT_SET_DEFAULT
},
132 {TK_CONFIG_BITMAP
, "-stipple", (char *) NULL
, (char *) NULL
,
133 (char *) NULL
, Tk_Offset(LineItem
, fillStipple
), TK_CONFIG_NULL_OK
},
134 {TK_CONFIG_CUSTOM
, "-tags", (char *) NULL
, (char *) NULL
,
135 (char *) NULL
, 0, TK_CONFIG_NULL_OK
, &tkCanvasTagsOption
},
136 {TK_CONFIG_PIXELS
, "-width", (char *) NULL
, (char *) NULL
,
137 "1", Tk_Offset(LineItem
, width
), TK_CONFIG_DONT_SET_DEFAULT
},
138 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
143 * The structures below defines the line item type by means
144 * of procedures that can be invoked by generic item code.
147 Tk_ItemType TkLineType
= {
149 sizeof(LineItem
), /* itemSize */
150 CreateLine
, /* createProc */
151 configSpecs
, /* configSpecs */
152 ConfigureLine
, /* configureProc */
153 LineCoords
, /* coordProc */
154 DeleteLine
, /* deleteProc */
155 DisplayLine
, /* displayProc */
156 0, /* alwaysRedraw */
157 LineToPoint
, /* pointProc */
158 LineToArea
, /* areaProc */
159 (Tk_ItemPostscriptProc
*) NULL
, /* postscriptProc */
160 ScaleLine
, /* scaleProc */
161 TranslateLine
, /* translateProc */
162 (Tk_ItemIndexProc
*) NULL
, /* indexProc */
163 (Tk_ItemCursorProc
*) NULL
, /* cursorProc */
164 (Tk_ItemSelectionProc
*) NULL
, /* selectionProc */
165 (Tk_ItemInsertProc
*) NULL
, /* insertProc */
166 (Tk_ItemDCharsProc
*) NULL
, /* dTextProc */
167 (Tk_ItemType
*) NULL
/* nextPtr */
171 * The Tk_Uid's below refer to uids for the various arrow types:
174 static Tk_Uid noneUid
= NULL
;
175 static Tk_Uid firstUid
= NULL
;
176 static Tk_Uid lastUid
= NULL
;
177 static Tk_Uid bothUid
= NULL
;
180 * The definition below determines how large are static arrays
181 * used to hold spline points (splines larger than this have to
182 * have their arrays malloc-ed).
185 #define MAX_STATIC_POINTS 200
188 *--------------------------------------------------------------
192 * This procedure is invoked to create a new line item in
196 * A standard Tcl return value. If an error occurred in
197 * creating the item, then an error message is left in
198 * canvasPtr->interp->result; in this case itemPtr is
199 * left uninitialized, so it can be safely freed by the
203 * A new line item is created.
205 *--------------------------------------------------------------
209 CreateLine(canvasPtr
, itemPtr
, argc
, argv
)
210 register Tk_Canvas
*canvasPtr
; /* Canvas to hold new item. */
211 Tk_Item
*itemPtr
; /* Record to hold new item; header
212 * has been initialized by caller. */
213 int argc
; /* Number of arguments in argv. */
214 char **argv
; /* Arguments describing line. */
216 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
220 Tcl_AppendResult(canvasPtr
->interp
, "wrong # args: should be \"",
221 Tk_PathName(canvasPtr
->tkwin
),
222 "\" create x1 y1 x2 y2 ?x3 y3 ...? ?options?",
228 * Carry out initialization that is needed to set defaults and to
229 * allow proper cleanup after errors during the the remainder of
233 linePtr
->canvasPtr
= canvasPtr
;
234 linePtr
->numPoints
= 0;
235 linePtr
->coordPtr
= NULL
;
238 linePtr
->fillStipple
= None
;
239 linePtr
->capStyle
= CapButt
;
240 linePtr
->joinStyle
= JoinRound
;
242 if (noneUid
== NULL
) {
243 noneUid
= Tk_GetUid("none");
244 firstUid
= Tk_GetUid("first");
245 lastUid
= Tk_GetUid("last");
246 bothUid
= Tk_GetUid("both");
248 linePtr
->arrow
= noneUid
;
249 linePtr
->arrowShapeA
= 8.0;
250 linePtr
->arrowShapeB
= 10.0;
251 linePtr
->arrowShapeC
= 3.0;
252 linePtr
->firstArrowPtr
= NULL
;
253 linePtr
->lastArrowPtr
= NULL
;
255 linePtr
->splineSteps
= 12;
258 * Count the number of points and then parse them into a point
259 * array. Leading arguments are assumed to be points if they
260 * start with a digit or a minus sign followed by a digit.
263 for (i
= 4; i
< (argc
-1); i
+=2) {
264 if ((!isdigit(argv
[i
][0])) &&
265 ((argv
[i
][0] != '-') || (!isdigit(argv
[i
][1])))) {
269 if (LineCoords(canvasPtr
, itemPtr
, i
, argv
) != TCL_OK
) {
272 if (ConfigureLine(canvasPtr
, itemPtr
, argc
-i
, argv
+i
, 0) == TCL_OK
) {
282 *--------------------------------------------------------------
286 * This procedure is invoked to process the "coords" widget
287 * command on lines. See the user documentation for details
291 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
294 * The coordinates for the given item may be changed.
296 *--------------------------------------------------------------
300 LineCoords(canvasPtr
, itemPtr
, argc
, argv
)
301 register Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
302 Tk_Item
*itemPtr
; /* Item whose coordinates are to be
303 * read or modified. */
304 int argc
; /* Number of coordinates supplied in
306 char **argv
; /* Array of coordinates: x1, y1,
309 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
314 for (i
= 0; i
< 2*linePtr
->numPoints
; i
++) {
315 sprintf(buffer
, "%g", linePtr
->coordPtr
[i
]);
316 Tcl_AppendElement(canvasPtr
->interp
, buffer
, 0);
318 } else if (argc
< 4) {
319 Tcl_AppendResult(canvasPtr
->interp
,
320 "too few coordinates for line: must have at least 4",
323 } else if (argc
& 1) {
324 Tcl_AppendResult(canvasPtr
->interp
,
325 "odd number of coordinates specified for line",
330 if (linePtr
->numPoints
!= numPoints
) {
331 if (linePtr
->coordPtr
!= NULL
) {
332 ckfree((char *) linePtr
->coordPtr
);
334 linePtr
->coordPtr
= (double *) ckalloc((unsigned)
335 (sizeof(double) * argc
));
336 linePtr
->numPoints
= numPoints
;
338 for (i
= argc
-1; i
>= 0; i
--) {
339 if (TkGetCanvasCoord(canvasPtr
, argv
[i
], &linePtr
->coordPtr
[i
])
344 ComputeLineBbox(canvasPtr
, linePtr
);
350 *--------------------------------------------------------------
354 * This procedure is invoked to configure various aspects
355 * of a line item such as its background color.
358 * A standard Tcl result code. If an error occurs, then
359 * an error message is left in canvasPtr->interp->result.
362 * Configuration information, such as colors and stipple
363 * patterns, may be set for itemPtr.
365 *--------------------------------------------------------------
369 ConfigureLine(canvasPtr
, itemPtr
, argc
, argv
, flags
)
370 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
371 Tk_Item
*itemPtr
; /* Line item to reconfigure. */
372 int argc
; /* Number of elements in argv. */
373 char **argv
; /* Arguments describing things to configure. */
374 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
376 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
381 if (Tk_ConfigureWidget(canvasPtr
->interp
, canvasPtr
->tkwin
,
382 configSpecs
, argc
, argv
, (char *) linePtr
, flags
) != TCL_OK
) {
387 * A few of the options require additional processing, such as
391 if (linePtr
->fg
== NULL
) {
394 gcValues
.foreground
= linePtr
->fg
->pixel
;
395 gcValues
.join_style
= linePtr
->joinStyle
;
396 if (linePtr
->width
< 0) {
399 gcValues
.line_width
= linePtr
->width
;
400 mask
= GCForeground
|GCJoinStyle
|GCLineWidth
;
401 if (linePtr
->fillStipple
!= None
) {
402 gcValues
.stipple
= linePtr
->fillStipple
;
403 gcValues
.fill_style
= FillStippled
;
404 mask
|= GCStipple
|GCFillStyle
;
406 if (linePtr
->arrow
== noneUid
) {
407 gcValues
.cap_style
= linePtr
->capStyle
;
410 newGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
412 if (linePtr
->gc
!= None
) {
413 Tk_FreeGC(linePtr
->gc
);
418 * Keep spline parameters within reasonable limits.
421 if (linePtr
->splineSteps
< 1) {
422 linePtr
->splineSteps
= 1;
423 } else if (linePtr
->splineSteps
> 100) {
424 linePtr
->splineSteps
= 100;
428 * Setup arrowheads, if needed. If arrowheads are turned off,
429 * restore the line's endpoints (they were shortened when the
430 * arrowheads were added).
433 if ((linePtr
->firstArrowPtr
!= NULL
) && (linePtr
->arrow
!= firstUid
)
434 && (linePtr
->arrow
!= bothUid
)) {
435 linePtr
->coordPtr
[0] = linePtr
->firstArrowPtr
[0];
436 linePtr
->coordPtr
[1] = linePtr
->firstArrowPtr
[1];
437 ckfree((char *) linePtr
->firstArrowPtr
);
438 linePtr
->firstArrowPtr
= NULL
;
440 if ((linePtr
->lastArrowPtr
!= NULL
) && (linePtr
->arrow
!= lastUid
)
441 && (linePtr
->arrow
!= bothUid
)) {
444 index
= 2*(linePtr
->numPoints
-1);
445 linePtr
->coordPtr
[index
] = linePtr
->lastArrowPtr
[0];
446 linePtr
->coordPtr
[index
+1] = linePtr
->lastArrowPtr
[1];
447 ckfree((char *) linePtr
->lastArrowPtr
);
448 linePtr
->lastArrowPtr
= NULL
;
450 if (linePtr
->arrow
!= noneUid
) {
451 if ((linePtr
->arrow
!= firstUid
) && (linePtr
->arrow
!= lastUid
)
452 && (linePtr
->arrow
!= bothUid
)) {
453 Tcl_AppendResult(canvasPtr
->interp
, "bad arrow spec \"",
454 linePtr
->arrow
, "\": must be none, first, last, or both",
456 linePtr
->arrow
= noneUid
;
459 ConfigureArrows(canvasPtr
, linePtr
);
463 * Recompute bounding box for line.
466 ComputeLineBbox(canvasPtr
, linePtr
);
472 *--------------------------------------------------------------
476 * This procedure is called to clean up the data structure
477 * associated with a line item.
483 * Resources associated with itemPtr are released.
485 *--------------------------------------------------------------
490 Tk_Item
*itemPtr
; /* Item that is being deleted. */
492 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
494 if (linePtr
->coordPtr
!= NULL
) {
495 ckfree((char *) linePtr
->coordPtr
);
497 if (linePtr
->fg
!= NULL
) {
498 Tk_FreeColor(linePtr
->fg
);
500 if (linePtr
->fillStipple
!= None
) {
501 Tk_FreeBitmap(linePtr
->fillStipple
);
503 if (linePtr
->gc
!= None
) {
504 Tk_FreeGC(linePtr
->gc
);
506 if (linePtr
->firstArrowPtr
!= NULL
) {
507 ckfree((char *) linePtr
->firstArrowPtr
);
509 if (linePtr
->lastArrowPtr
!= NULL
) {
510 ckfree((char *) linePtr
->lastArrowPtr
);
515 *--------------------------------------------------------------
519 * This procedure is invoked to compute the bounding box of
520 * all the pixels that may be drawn as part of a line.
526 * The fields x1, y1, x2, and y2 are updated in the header
529 *--------------------------------------------------------------
533 ComputeLineBbox(canvasPtr
, linePtr
)
534 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
535 LineItem
*linePtr
; /* Item whose bbos is to be
538 register double *coordPtr
;
541 coordPtr
= linePtr
->coordPtr
;
542 linePtr
->header
.x1
= linePtr
->header
.x2
= *coordPtr
;
543 linePtr
->header
.y1
= linePtr
->header
.y2
= coordPtr
[1];
546 * Compute the bounding box of all the points in the line,
547 * then expand in all directions by the line's width to take
548 * care of butting or rounded corners and projecting or
549 * rounded caps. This expansion is an overestimate (worst-case
550 * is square root of two over two) but it's simple. Don't do
551 * anything special for curves. This causes an additional
552 * overestimate in the bounding box, but is faster.
555 for (i
= 1, coordPtr
= linePtr
->coordPtr
+2; i
< linePtr
->numPoints
;
556 i
++, coordPtr
+= 2) {
557 TkIncludePoint(canvasPtr
, (Tk_Item
*) linePtr
, coordPtr
);
559 linePtr
->header
.x1
-= linePtr
->width
;
560 linePtr
->header
.x2
+= linePtr
->width
;
561 linePtr
->header
.y1
-= linePtr
->width
;
562 linePtr
->header
.y2
+= linePtr
->width
;
565 * For mitered lines, make a second pass through all the points.
566 * Compute the locations of the two miter vertex points and add
567 * those into the bounding box.
570 if (linePtr
->joinStyle
== JoinMiter
) {
571 for (i
= linePtr
->numPoints
, coordPtr
= linePtr
->coordPtr
; i
>= 3;
572 i
--, coordPtr
+= 2) {
576 if (TkGetMiterPoints(coordPtr
, coordPtr
+2, coordPtr
+4,
577 (double) linePtr
->width
, miter
, miter
+2)) {
578 for (j
= 0; j
< 4; j
+= 2) {
579 TkIncludePoint(canvasPtr
, (Tk_Item
*) linePtr
, miter
+j
);
586 * Add in the sizes of arrowheads, if any.
589 if (linePtr
->arrow
!= noneUid
) {
590 if (linePtr
->arrow
!= lastUid
) {
591 for (i
= 0, coordPtr
= linePtr
->firstArrowPtr
; i
< PTS_IN_ARROW
;
592 i
++, coordPtr
+= 2) {
593 TkIncludePoint(canvasPtr
, (Tk_Item
*) linePtr
, coordPtr
);
596 if (linePtr
->arrow
!= firstUid
) {
597 for (i
= 0, coordPtr
= linePtr
->lastArrowPtr
; i
< PTS_IN_ARROW
;
598 i
++, coordPtr
+= 2) {
599 TkIncludePoint(canvasPtr
, (Tk_Item
*) linePtr
, coordPtr
);
605 * Add one more pixel of fudge factor just to be safe (e.g.
606 * X may round differently than we do).
609 linePtr
->header
.x1
-= 1;
610 linePtr
->header
.x2
+= 1;
611 linePtr
->header
.y1
-= 1;
612 linePtr
->header
.y2
+= 1;
616 *--------------------------------------------------------------
620 * This procedure is invoked to draw a line item in a given
627 * ItemPtr is drawn in drawable using the transformation
628 * information in canvasPtr.
630 *--------------------------------------------------------------
634 DisplayLine(canvasPtr
, itemPtr
, drawable
)
635 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
636 Tk_Item
*itemPtr
; /* Item to be displayed. */
637 Drawable drawable
; /* Pixmap or window in which to draw
640 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
641 XPoint staticPoints
[MAX_STATIC_POINTS
];
643 register XPoint
*pPtr
;
644 register double *coordPtr
;
647 if (linePtr
->gc
== None
) {
652 * Build up an array of points in screen coordinates. Use a
653 * static array unless the line has an enormous number of points;
654 * in this case, dynamically allocate an array. For smoothed lines,
655 * generate the curve points on each redisplay.
658 if ((linePtr
->smooth
) && (linePtr
->numPoints
> 2)) {
659 numPoints
= 1 + linePtr
->numPoints
*linePtr
->splineSteps
;
661 numPoints
= linePtr
->numPoints
;
664 if (numPoints
<= MAX_STATIC_POINTS
) {
665 pointPtr
= staticPoints
;
667 pointPtr
= (XPoint
*) ckalloc((unsigned) (numPoints
* sizeof(XPoint
)));
670 if (linePtr
->smooth
) {
671 numPoints
= TkMakeBezierCurve(canvasPtr
, linePtr
->coordPtr
,
672 linePtr
->numPoints
, linePtr
->splineSteps
, pointPtr
,
675 for (i
= 0, coordPtr
= linePtr
->coordPtr
, pPtr
= pointPtr
;
676 i
< linePtr
->numPoints
; i
+= 1, coordPtr
+= 2, pPtr
++) {
677 pPtr
->x
= SCREEN_X(canvasPtr
, *coordPtr
);
678 pPtr
->y
= SCREEN_Y(canvasPtr
, coordPtr
[1]);
683 * Display line, the free up line storage if it was dynamically
687 XDrawLines(Tk_Display(canvasPtr
->tkwin
), drawable
, linePtr
->gc
,
688 pointPtr
, numPoints
, CoordModeOrigin
);
689 if (pointPtr
!= staticPoints
) {
690 ckfree((char *) pointPtr
);
694 * Display arrowheads, if they are wanted.
697 if (linePtr
->arrow
!= noneUid
) {
698 if (linePtr
->arrow
!= lastUid
) {
699 TkFillPolygon(canvasPtr
, linePtr
->firstArrowPtr
, PTS_IN_ARROW
,
700 drawable
, linePtr
->gc
);
702 if (linePtr
->arrow
!= firstUid
) {
703 TkFillPolygon(canvasPtr
, linePtr
->lastArrowPtr
, PTS_IN_ARROW
,
704 drawable
, linePtr
->gc
);
710 *--------------------------------------------------------------
714 * Computes the distance from a given point to a given
715 * line, in canvas units.
718 * The return value is 0 if the point whose x and y coordinates
719 * are pointPtr[0] and pointPtr[1] is inside the line. If the
720 * point isn't inside the line then the return value is the
721 * distance from the point to the line.
726 *--------------------------------------------------------------
731 LineToPoint(canvasPtr
, itemPtr
, pointPtr
)
732 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
733 Tk_Item
*itemPtr
; /* Item to check against point. */
734 double *pointPtr
; /* Pointer to x and y coordinates. */
736 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
737 register double *coordPtr
, *linePoints
;
738 double staticSpace
[2*MAX_STATIC_POINTS
];
740 double bestDist
, dist
;
741 int numPoints
, count
;
742 int changedMiterToBevel
; /* Non-zero means that a mitered corner
743 * had to be treated as beveled after all
744 * because the angle was < 11 degrees. */
749 * Handle smoothed lines by generating an expanded set of points
750 * against which to do the check.
753 if ((linePtr
->smooth
) && (linePtr
->numPoints
> 2)) {
754 numPoints
= 1 + linePtr
->numPoints
*linePtr
->splineSteps
;
755 if (numPoints
<= MAX_STATIC_POINTS
) {
756 linePoints
= staticSpace
;
758 linePoints
= (double *) ckalloc((unsigned)
759 (2*numPoints
*sizeof(double)));
761 numPoints
= TkMakeBezierCurve(canvasPtr
, linePtr
->coordPtr
,
762 linePtr
->numPoints
, linePtr
->splineSteps
, (XPoint
*) NULL
,
765 numPoints
= linePtr
->numPoints
;
766 linePoints
= linePtr
->coordPtr
;
770 * The overall idea is to iterate through all of the edges of
771 * the line, computing a polygon for each edge and testing the
772 * point against that polygon. In addition, there are additional
773 * tests to deal with rounded joints and caps.
776 changedMiterToBevel
= 0;
777 for (count
= numPoints
, coordPtr
= linePoints
; count
>= 2;
778 count
--, coordPtr
+= 2) {
781 * If rounding is done around the first point then compute
782 * the distance between the point and the point.
785 if (((linePtr
->capStyle
== CapRound
) && (count
== numPoints
))
786 || ((linePtr
->joinStyle
== JoinRound
)
787 && (count
!= numPoints
))) {
788 dist
= hypot(coordPtr
[0] - pointPtr
[0], coordPtr
[1] - pointPtr
[1])
789 - linePtr
->width
/2.0;
793 } else if (dist
< bestDist
) {
799 * Compute the polygonal shape corresponding to this edge,
800 * consisting of two points for the first point of the edge
801 * and two points for the last point of the edge.
804 if (count
== numPoints
) {
805 TkGetButtPoints(coordPtr
+2, coordPtr
, (double) linePtr
->width
,
806 linePtr
->capStyle
== CapProjecting
, poly
, poly
+2);
807 } else if ((linePtr
->joinStyle
== JoinMiter
) && !changedMiterToBevel
) {
813 TkGetButtPoints(coordPtr
+2, coordPtr
, (double) linePtr
->width
, 0,
817 * If this line uses beveled joints, then check the distance
818 * to a polygon comprising the last two points of the previous
819 * polygon and the first two from this polygon; this checks
820 * the wedges that fill the mitered joint.
823 if ((linePtr
->joinStyle
== JoinBevel
) || changedMiterToBevel
) {
826 dist
= TkPolygonToPoint(poly
, 5, pointPtr
);
830 } else if (dist
< bestDist
) {
833 changedMiterToBevel
= 0;
837 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
,
838 linePtr
->capStyle
== CapProjecting
, poly
+4, poly
+6);
839 } else if (linePtr
->joinStyle
== JoinMiter
) {
840 if (TkGetMiterPoints(coordPtr
, coordPtr
+2, coordPtr
+4,
841 (double) linePtr
->width
, poly
+4, poly
+6) == 0) {
842 changedMiterToBevel
= 1;
843 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
,
847 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
, 0,
852 dist
= TkPolygonToPoint(poly
, 5, pointPtr
);
856 } else if (dist
< bestDist
) {
862 * If caps are rounded, check the distance to the cap around the
863 * final end point of the line.
866 if (linePtr
->capStyle
== CapRound
) {
867 dist
= hypot(coordPtr
[0] - pointPtr
[0], coordPtr
[1] - pointPtr
[1])
868 - linePtr
->width
/2.0;
872 } else if (dist
< bestDist
) {
878 * If there are arrowheads, check the distance to the arrowheads.
881 if (linePtr
->arrow
!= noneUid
) {
882 if (linePtr
->arrow
!= lastUid
) {
883 dist
= TkPolygonToPoint(linePtr
->firstArrowPtr
, PTS_IN_ARROW
,
888 } else if (dist
< bestDist
) {
892 if (linePtr
->arrow
!= firstUid
) {
893 dist
= TkPolygonToPoint(linePtr
->lastArrowPtr
, PTS_IN_ARROW
,
898 } else if (dist
< bestDist
) {
905 if ((linePoints
!= staticSpace
) && (linePoints
!= linePtr
->coordPtr
)) {
906 ckfree((char *) linePoints
);
912 *--------------------------------------------------------------
916 * This procedure is called to determine whether an item
917 * lies entirely inside, entirely outside, or overlapping
918 * a given rectangular area.
921 * -1 is returned if the item is entirely outside the
922 * area, 0 if it overlaps, and 1 if it is entirely
923 * inside the given area.
928 *--------------------------------------------------------------
933 LineToArea(canvasPtr
, itemPtr
, rectPtr
)
934 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
935 Tk_Item
*itemPtr
; /* Item to check against line. */
938 register LineItem
*linePtr
= (LineItem
*) itemPtr
;
939 register double *coordPtr
;
940 double staticSpace
[2*MAX_STATIC_POINTS
];
941 double *linePoints
, poly
[10];
943 int numPoints
, count
;
944 int changedMiterToBevel
; /* Non-zero means that a mitered corner
945 * had to be treated as beveled after all
946 * because the angle was < 11 degrees. */
947 int inside
; /* Tentative guess about what to return,
948 * based on all points seen so far: one
949 * means everything seen so far was
950 * inside the area; -1 means everything
951 * was outside the area. 0 means overlap
954 radius
= linePtr
->width
/2.0;
958 * Handle smoothed lines by generating an expanded set of points
959 * against which to do the check.
962 if ((linePtr
->smooth
) && (linePtr
->numPoints
> 2)) {
963 numPoints
= 1 + linePtr
->numPoints
*linePtr
->splineSteps
;
964 if (numPoints
<= MAX_STATIC_POINTS
) {
965 linePoints
= staticSpace
;
967 linePoints
= (double *) ckalloc((unsigned)
968 (2*numPoints
*sizeof(double)));
970 numPoints
= TkMakeBezierCurve(canvasPtr
, linePtr
->coordPtr
,
971 linePtr
->numPoints
, linePtr
->splineSteps
, (XPoint
*) NULL
,
974 numPoints
= linePtr
->numPoints
;
975 linePoints
= linePtr
->coordPtr
;
978 coordPtr
= linePoints
;
979 if ((coordPtr
[0] >= rectPtr
[0]) && (coordPtr
[0] <= rectPtr
[2])
980 && (coordPtr
[1] >= rectPtr
[1]) && (coordPtr
[1] <= rectPtr
[3])) {
985 * Iterate through all of the edges of the line, computing a polygon
986 * for each edge and testing the area against that polygon. In
987 * addition, there are additional tests to deal with rounded joints
991 changedMiterToBevel
= 0;
992 for (count
= numPoints
; count
>= 2; count
--, coordPtr
+= 2) {
995 * If rounding is done around the first point of the edge
996 * then test a circular region around the point with the
1000 if (((linePtr
->capStyle
== CapRound
) && (count
== numPoints
))
1001 || ((linePtr
->joinStyle
== JoinRound
)
1002 && (count
!= numPoints
))) {
1003 poly
[0] = coordPtr
[0] - radius
;
1004 poly
[1] = coordPtr
[1] - radius
;
1005 poly
[2] = coordPtr
[0] + radius
;
1006 poly
[3] = coordPtr
[1] + radius
;
1007 if (TkOvalToArea(poly
, rectPtr
) != inside
) {
1014 * Compute the polygonal shape corresponding to this edge,
1015 * consisting of two points for the first point of the edge
1016 * and two points for the last point of the edge.
1019 if (count
== numPoints
) {
1020 TkGetButtPoints(coordPtr
+2, coordPtr
, (double) linePtr
->width
,
1021 linePtr
->capStyle
== CapProjecting
, poly
, poly
+2);
1022 } else if ((linePtr
->joinStyle
== JoinMiter
) && !changedMiterToBevel
) {
1028 TkGetButtPoints(coordPtr
+2, coordPtr
, (double) linePtr
->width
, 0,
1032 * If the last joint was beveled, then also check a
1033 * polygon comprising the last two points of the previous
1034 * polygon and the first two from this polygon; this checks
1035 * the wedges that fill the beveled joint.
1038 if ((linePtr
->joinStyle
== JoinBevel
) || changedMiterToBevel
) {
1041 if (TkPolygonToArea(poly
, 5, rectPtr
) != inside
) {
1045 changedMiterToBevel
= 0;
1049 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
,
1050 linePtr
->capStyle
== CapProjecting
, poly
+4, poly
+6);
1051 } else if (linePtr
->joinStyle
== JoinMiter
) {
1052 if (TkGetMiterPoints(coordPtr
, coordPtr
+2, coordPtr
+4,
1053 (double) linePtr
->width
, poly
+4, poly
+6) == 0) {
1054 changedMiterToBevel
= 1;
1055 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
,
1059 TkGetButtPoints(coordPtr
, coordPtr
+2, (double) linePtr
->width
, 0,
1064 if (TkPolygonToArea(poly
, 5, rectPtr
) != inside
) {
1071 * If caps are rounded, check the cap around the final point
1075 if (linePtr
->capStyle
== CapRound
) {
1076 poly
[0] = coordPtr
[0] - radius
;
1077 poly
[1] = coordPtr
[1] - radius
;
1078 poly
[2] = coordPtr
[0] + radius
;
1079 poly
[3] = coordPtr
[1] + radius
;
1080 if (TkOvalToArea(poly
, rectPtr
) != inside
) {
1087 * Check arrowheads, if any.
1090 if (linePtr
->arrow
!= noneUid
) {
1091 if (linePtr
->arrow
!= lastUid
) {
1092 if (TkPolygonToArea(linePtr
->firstArrowPtr
, PTS_IN_ARROW
,
1093 rectPtr
) != inside
) {
1098 if (linePtr
->arrow
!= firstUid
) {
1099 if (TkPolygonToArea(linePtr
->lastArrowPtr
, PTS_IN_ARROW
,
1100 rectPtr
) != inside
) {
1108 if ((linePoints
!= staticSpace
) && (linePoints
!= linePtr
->coordPtr
)) {
1109 ckfree((char *) linePoints
);
1115 *--------------------------------------------------------------
1119 * This procedure is invoked to rescale a line item.
1125 * The line referred to by itemPtr is rescaled so that the
1126 * following transformation is applied to all point
1128 * x' = originX + scaleX*(x-originX)
1129 * y' = originY + scaleY*(y-originY)
1131 *--------------------------------------------------------------
1135 ScaleLine(canvasPtr
, itemPtr
, originX
, originY
, scaleX
, scaleY
)
1136 Tk_Canvas
*canvasPtr
; /* Canvas containing line. */
1137 Tk_Item
*itemPtr
; /* Line to be scaled. */
1138 double originX
, originY
; /* Origin about which to scale rect. */
1139 double scaleX
; /* Amount to scale in X direction. */
1140 double scaleY
; /* Amount to scale in Y direction. */
1142 LineItem
*linePtr
= (LineItem
*) itemPtr
;
1143 register double *coordPtr
;
1146 for (i
= 0, coordPtr
= linePtr
->coordPtr
; i
< linePtr
->numPoints
;
1147 i
++, coordPtr
+= 2) {
1148 coordPtr
[0] = originX
+ scaleX
*(*coordPtr
- originX
);
1149 coordPtr
[1] = originY
+ scaleY
*(coordPtr
[1] - originY
);
1151 if (linePtr
->firstArrowPtr
!= NULL
) {
1152 for (i
= 0, coordPtr
= linePtr
->firstArrowPtr
; i
< PTS_IN_ARROW
;
1153 i
++, coordPtr
+= 2) {
1154 coordPtr
[0] = originX
+ scaleX
*(coordPtr
[0] - originX
);
1155 coordPtr
[1] = originY
+ scaleY
*(coordPtr
[1] - originY
);
1158 if (linePtr
->lastArrowPtr
!= NULL
) {
1159 for (i
= 0, coordPtr
= linePtr
->lastArrowPtr
; i
< PTS_IN_ARROW
;
1160 i
++, coordPtr
+= 2) {
1161 coordPtr
[0] = originX
+ scaleX
*(coordPtr
[0] - originX
);
1162 coordPtr
[1] = originY
+ scaleY
*(coordPtr
[1] - originY
);
1165 ComputeLineBbox(canvasPtr
, linePtr
);
1169 *--------------------------------------------------------------
1173 * This procedure is called to move a line by a given amount.
1179 * The position of the line is offset by (xDelta, yDelta), and
1180 * the bounding box is updated in the generic part of the item
1183 *--------------------------------------------------------------
1187 TranslateLine(canvasPtr
, itemPtr
, deltaX
, deltaY
)
1188 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
1189 Tk_Item
*itemPtr
; /* Item that is being moved. */
1190 double deltaX
, deltaY
; /* Amount by which item is to be
1193 LineItem
*linePtr
= (LineItem
*) itemPtr
;
1194 register double *coordPtr
;
1197 for (i
= 0, coordPtr
= linePtr
->coordPtr
; i
< linePtr
->numPoints
;
1198 i
++, coordPtr
+= 2) {
1199 coordPtr
[0] += deltaX
;
1200 coordPtr
[1] += deltaY
;
1202 if (linePtr
->firstArrowPtr
!= NULL
) {
1203 for (i
= 0, coordPtr
= linePtr
->firstArrowPtr
; i
< PTS_IN_ARROW
;
1204 i
++, coordPtr
+= 2) {
1205 coordPtr
[0] += deltaX
;
1206 coordPtr
[1] += deltaY
;
1209 if (linePtr
->lastArrowPtr
!= NULL
) {
1210 for (i
= 0, coordPtr
= linePtr
->lastArrowPtr
; i
< PTS_IN_ARROW
;
1211 i
++, coordPtr
+= 2) {
1212 coordPtr
[0] += deltaX
;
1213 coordPtr
[1] += deltaY
;
1216 ComputeLineBbox(canvasPtr
, linePtr
);
1220 *--------------------------------------------------------------
1222 * ParseArrowShape --
1224 * This procedure is called back during option parsing to
1225 * parse arrow shape information.
1228 * The return value is a standard Tcl result: TCL_OK means
1229 * that the arrow shape information was parsed ok, and
1230 * TCL_ERROR means it couldn't be parsed.
1233 * Arrow information in recordPtr is updated.
1235 *--------------------------------------------------------------
1240 ParseArrowShape(clientData
, interp
, tkwin
, value
, recordPtr
, offset
)
1241 ClientData clientData
; /* Not used. */
1242 Tcl_Interp
*interp
; /* Used for error reporting. */
1243 Tk_Window tkwin
; /* Not used. */
1244 char *value
; /* Textual specification of arrow shape. */
1245 char *recordPtr
; /* Pointer to item record in which to
1246 * store arrow information. */
1247 int offset
; /* Offset of shape information in widget
1250 LineItem
*linePtr
= (LineItem
*) recordPtr
;
1255 if (offset
!= Tk_Offset(LineItem
, arrowShapeA
)) {
1256 panic("ParseArrowShape received bogus offset");
1259 if (Tcl_SplitList(interp
, value
, &argc
, &argv
) != TCL_OK
) {
1261 Tcl_ResetResult(interp
);
1262 Tcl_AppendResult(interp
, "bad arrow shape \"", value
,
1263 "\": must be list with three numbers", (char *) NULL
);
1265 ckfree((char *) argv
);
1272 if ((TkGetCanvasCoord(linePtr
->canvasPtr
, argv
[0], &a
) != TCL_OK
)
1273 || (TkGetCanvasCoord(linePtr
->canvasPtr
, argv
[1], &b
) != TCL_OK
)
1274 || (TkGetCanvasCoord(linePtr
->canvasPtr
, argv
[2], &c
) != TCL_OK
)) {
1277 linePtr
->arrowShapeA
= a
;
1278 linePtr
->arrowShapeB
= b
;
1279 linePtr
->arrowShapeC
= c
;
1280 ckfree((char *) argv
);
1285 *--------------------------------------------------------------
1287 * PrintArrowShape --
1289 * This procedure is a callback invoked by the configuration
1290 * code to return a printable value describing an arrow shape.
1298 *--------------------------------------------------------------
1303 PrintArrowShape(clientData
, tkwin
, recordPtr
, offset
, freeProcPtr
)
1304 ClientData clientData
; /* Not used. */
1305 Tk_Window tkwin
; /* Window associated with linePtr's widget. */
1306 char *recordPtr
; /* Pointer to item record containing current
1307 * shape information. */
1308 int offset
; /* Offset of arrow information in record. */
1309 Tcl_FreeProc
**freeProcPtr
; /* Store address of procedure to call to
1310 * free string here. */
1312 LineItem
*linePtr
= (LineItem
*) recordPtr
;
1315 buffer
= ckalloc(120);
1316 sprintf(buffer
, "%.5g %.5g %.5g", linePtr
->arrowShapeA
,
1317 linePtr
->arrowShapeB
, linePtr
->arrowShapeC
);
1318 *freeProcPtr
= (Tcl_FreeProc
*) free
;
1323 *--------------------------------------------------------------
1325 * ConfigureArrows --
1327 * If arrowheads have been requested for a line, this
1328 * procedure makes arrangements for the arrowheads.
1331 * A standard Tcl return value. If an error occurs, then
1332 * an error message is left in canvasPtr->interp->result.
1335 * Information in linePtr is set up for one or two arrowheads.
1336 * the firstArrowPtr and lastArrowPtr polygons are allocated
1337 * and initialized, if need be, and the end points of the line
1338 * are adjusted so that a thick line doesn't stick out past
1341 *--------------------------------------------------------------
1346 ConfigureArrows(canvasPtr
, linePtr
)
1347 Tk_Canvas
*canvasPtr
; /* Canvas in which arrows will be
1348 * displayed (interp and tkwin
1349 * fields are needed). */
1350 register LineItem
*linePtr
; /* Item to configure for arrows. */
1352 double *poly
, *coordPtr
;
1353 double dx
, dy
, length
, sinTheta
, cosTheta
, temp
, shapeC
;
1354 double fracHeight
; /* Line width as fraction of
1355 * arrowhead width. */
1356 double backup
; /* Distance to backup end points
1357 * so the line ends in the middle
1358 * of the arrowhead. */
1359 double vertX
, vertY
; /* Position of arrowhead vertex. */
1362 * If there's an arrowhead on the first point of the line, compute
1363 * its polygon and adjust the first point of the line so that the
1364 * line doesn't stick out past the leading edge of the arrowhead.
1367 shapeC
= linePtr
->arrowShapeC
+ linePtr
->width
/2.0;
1368 fracHeight
= (linePtr
->width
/2.0)/shapeC
;
1369 backup
= fracHeight
*linePtr
->arrowShapeB
1370 + linePtr
->arrowShapeA
*(1.0 - fracHeight
)/2.0;
1371 if (linePtr
->arrow
!= lastUid
) {
1372 poly
= linePtr
->firstArrowPtr
;
1374 poly
= (double *) ckalloc((unsigned)
1375 (2*PTS_IN_ARROW
*sizeof(double)));
1376 poly
[0] = poly
[10] = linePtr
->coordPtr
[0];
1377 poly
[1] = poly
[11] = linePtr
->coordPtr
[1];
1378 linePtr
->firstArrowPtr
= poly
;
1380 dx
= poly
[0] - linePtr
->coordPtr
[2];
1381 dy
= poly
[1] - linePtr
->coordPtr
[3];
1382 length
= hypot(dx
, dy
);
1384 sinTheta
= cosTheta
= 0.0;
1386 sinTheta
= dy
/length
;
1387 cosTheta
= dx
/length
;
1389 vertX
= poly
[0] - linePtr
->arrowShapeA
*cosTheta
;
1390 vertY
= poly
[1] - linePtr
->arrowShapeA
*sinTheta
;
1391 temp
= shapeC
*sinTheta
;
1392 poly
[2] = poly
[0] - linePtr
->arrowShapeB
*cosTheta
+ temp
;
1393 poly
[8] = poly
[2] - 2*temp
;
1394 temp
= shapeC
*cosTheta
;
1395 poly
[3] = poly
[1] - linePtr
->arrowShapeB
*sinTheta
- temp
;
1396 poly
[9] = poly
[3] + 2*temp
;
1397 poly
[4] = poly
[2]*fracHeight
+ vertX
*(1.0-fracHeight
);
1398 poly
[5] = poly
[3]*fracHeight
+ vertY
*(1.0-fracHeight
);
1399 poly
[6] = poly
[8]*fracHeight
+ vertX
*(1.0-fracHeight
);
1400 poly
[7] = poly
[9]*fracHeight
+ vertY
*(1.0-fracHeight
);
1403 * Polygon done. Now move the first point towards the second so
1404 * that the corners at the end of the line are inside the
1408 linePtr
->coordPtr
[0] = poly
[0] - backup
*cosTheta
;
1409 linePtr
->coordPtr
[1] = poly
[1] - backup
*sinTheta
;
1413 * Similar arrowhead calculation for the last point of the line.
1416 if (linePtr
->arrow
!= firstUid
) {
1417 coordPtr
= linePtr
->coordPtr
+ 2*(linePtr
->numPoints
-2);
1418 poly
= linePtr
->lastArrowPtr
;
1420 poly
= (double *) ckalloc((unsigned)
1421 (2*PTS_IN_ARROW
*sizeof(double)));
1422 poly
[0] = poly
[10] = coordPtr
[2];
1423 poly
[1] = poly
[11] = coordPtr
[3];
1424 linePtr
->lastArrowPtr
= poly
;
1426 dx
= poly
[0] - coordPtr
[0];
1427 dy
= poly
[1] - coordPtr
[1];
1428 length
= hypot(dx
, dy
);
1430 sinTheta
= cosTheta
= 0.0;
1432 sinTheta
= dy
/length
;
1433 cosTheta
= dx
/length
;
1435 vertX
= poly
[0] - linePtr
->arrowShapeA
*cosTheta
;
1436 vertY
= poly
[1] - linePtr
->arrowShapeA
*sinTheta
;
1437 temp
= shapeC
*sinTheta
;
1438 poly
[2] = poly
[0] - linePtr
->arrowShapeB
*cosTheta
+ temp
;
1439 poly
[8] = poly
[2] - 2*temp
;
1440 temp
= shapeC
*cosTheta
;
1441 poly
[3] = poly
[1] - linePtr
->arrowShapeB
*sinTheta
- temp
;
1442 poly
[9] = poly
[3] + 2*temp
;
1443 poly
[4] = poly
[2]*fracHeight
+ vertX
*(1.0-fracHeight
);
1444 poly
[5] = poly
[3]*fracHeight
+ vertY
*(1.0-fracHeight
);
1445 poly
[6] = poly
[8]*fracHeight
+ vertX
*(1.0-fracHeight
);
1446 poly
[7] = poly
[9]*fracHeight
+ vertY
*(1.0-fracHeight
);
1447 coordPtr
[2] = poly
[0] - backup
*cosTheta
;
1448 coordPtr
[3] = poly
[1] - backup
*sinTheta
;