4 * This file implements polygon 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/tkCanvPoly.c,v 1.6 92/07/28 15:40:10 ouster Exp $ SPRITE (Berkeley)";
27 * The structure below defines the record for each polygon item.
30 typedef struct PolygonItem
{
31 Tk_Item header
; /* Generic stuff that's the same for all
32 * types. MUST BE FIRST IN STRUCTURE. */
33 int numPoints
; /* Number of points in polygon (always >= 3).
34 * Polygon is always closed. */
35 double *coordPtr
; /* Pointer to malloc-ed array containing
36 * x- and y-coords of all points in polygon.
37 * X-coords are even-valued indices, y-coords
38 * are corresponding odd-valued indices. */
39 XColor
*fg
; /* Foreground color for polygon. */
40 Pixmap fillStipple
; /* Stipple bitmap for filling polygon. */
41 GC gc
; /* Graphics context for filling polygon. */
42 int smooth
; /* Non-zero means draw shape smoothed (i.e.
43 * with Bezier splines). */
44 int splineSteps
; /* Number of steps in each spline segment. */
48 * Information used for parsing configuration specs:
51 static Tk_ConfigSpec configSpecs
[] = {
52 {TK_CONFIG_COLOR
, "-fill", (char *) NULL
, (char *) NULL
,
53 "black", Tk_Offset(PolygonItem
, fg
), TK_CONFIG_NULL_OK
},
54 {TK_CONFIG_BOOLEAN
, "-smooth", (char *) NULL
, (char *) NULL
,
55 "no", Tk_Offset(PolygonItem
, smooth
), TK_CONFIG_DONT_SET_DEFAULT
},
56 {TK_CONFIG_INT
, "-splinesteps", (char *) NULL
, (char *) NULL
,
57 "12", Tk_Offset(PolygonItem
, splineSteps
), TK_CONFIG_DONT_SET_DEFAULT
},
58 {TK_CONFIG_BITMAP
, "-stipple", (char *) NULL
, (char *) NULL
,
59 (char *) NULL
, Tk_Offset(PolygonItem
, fillStipple
), TK_CONFIG_NULL_OK
},
60 {TK_CONFIG_CUSTOM
, "-tags", (char *) NULL
, (char *) NULL
,
61 (char *) NULL
, 0, TK_CONFIG_NULL_OK
, &tkCanvasTagsOption
},
62 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
67 * Prototypes for procedures defined in this file:
70 static void ComputePolygonBbox
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
71 PolygonItem
*polyPtr
));
72 static int ConfigurePolygon
_ANSI_ARGS_((
73 Tk_Canvas
*canvasPtr
, Tk_Item
*itemPtr
, int argc
,
74 char **argv
, int flags
));
75 static int CreatePolygon
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
76 struct Tk_Item
*itemPtr
, int argc
, char **argv
));
77 static void DeletePolygon
_ANSI_ARGS_((Tk_Item
*itemPtr
));
78 static void DisplayPolygon
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
79 Tk_Item
*itemPtr
, Drawable dst
));
80 static int PolygonCoords
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
81 Tk_Item
*itemPtr
, int argc
, char **argv
));
82 static int PolygonToArea
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
83 Tk_Item
*itemPtr
, double *rectPtr
));
84 static double PolygonToPoint
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
85 Tk_Item
*itemPtr
, double *pointPtr
));
86 static void ScalePolygon
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
87 Tk_Item
*itemPtr
, double originX
, double originY
,
88 double scaleX
, double scaleY
));
89 static void TranslatePolygon
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
90 Tk_Item
*itemPtr
, double deltaX
, double deltaY
));
93 * The structures below defines the polygon item type by means
94 * of procedures that can be invoked by generic item code.
97 Tk_ItemType TkPolygonType
= {
99 sizeof(PolygonItem
), /* itemSize */
100 CreatePolygon
, /* createProc */
101 configSpecs
, /* configSpecs */
102 ConfigurePolygon
, /* configureProc */
103 PolygonCoords
, /* coordProc */
104 DeletePolygon
, /* deleteProc */
105 DisplayPolygon
, /* displayProc */
106 0, /* alwaysRedraw */
107 PolygonToPoint
, /* pointProc */
108 PolygonToArea
, /* areaProc */
109 (Tk_ItemPostscriptProc
*) NULL
, /* postscriptProc */
110 ScalePolygon
, /* scaleProc */
111 TranslatePolygon
, /* translateProc */
112 (Tk_ItemIndexProc
*) NULL
, /* indexProc */
113 (Tk_ItemCursorProc
*) NULL
, /* cursorProc */
114 (Tk_ItemSelectionProc
*) NULL
, /* selectionProc */
115 (Tk_ItemInsertProc
*) NULL
, /* insertProc */
116 (Tk_ItemDCharsProc
*) NULL
, /* dTextProc */
117 (Tk_ItemType
*) NULL
/* nextPtr */
121 * The definition below determines how large are static arrays
122 * used to hold spline points (splines larger than this have to
123 * have their arrays malloc-ed).
126 #define MAX_STATIC_POINTS 200
129 *--------------------------------------------------------------
133 * This procedure is invoked to create a new polygon item in
137 * A standard Tcl return value. If an error occurred in
138 * creating the item, then an error message is left in
139 * canvasPtr->interp->result; in this case itemPtr is
140 * left uninitialized, so it can be safely freed by the
144 * A new polygon item is created.
146 *--------------------------------------------------------------
150 CreatePolygon(canvasPtr
, itemPtr
, argc
, argv
)
151 register Tk_Canvas
*canvasPtr
; /* Canvas to hold new item. */
152 Tk_Item
*itemPtr
; /* Record to hold new item; header
153 * has been initialized by caller. */
154 int argc
; /* Number of arguments in argv. */
155 char **argv
; /* Arguments describing polygon. */
157 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
161 Tcl_AppendResult(canvasPtr
->interp
, "wrong # args: should be \"",
162 Tk_PathName(canvasPtr
->tkwin
),
163 "\" create x1 y1 x2 y2 x3 y3 ?x4 y4 ...? ?options?",
169 * Carry out initialization that is needed in order to clean
170 * up after errors during the the remainder of this procedure.
173 polyPtr
->numPoints
= 0;
174 polyPtr
->coordPtr
= NULL
;
176 polyPtr
->fillStipple
= None
;
179 polyPtr
->splineSteps
= 12;
182 * Count the number of points and then parse them into a point
183 * array. Leading arguments are assumed to be points if they
184 * start with a digit or a minus sign followed by a digit.
187 for (i
= 4; i
< (argc
-1); i
+=2) {
188 if ((!isdigit(argv
[i
][0])) &&
189 ((argv
[i
][0] != '-') || (!isdigit(argv
[i
][1])))) {
193 if (PolygonCoords(canvasPtr
, itemPtr
, i
, argv
) != TCL_OK
) {
197 if (ConfigurePolygon(canvasPtr
, itemPtr
, argc
-i
, argv
+i
, 0) == TCL_OK
) {
202 DeletePolygon(itemPtr
);
207 *--------------------------------------------------------------
211 * This procedure is invoked to process the "coords" widget
212 * command on polygons. See the user documentation for details
216 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
219 * The coordinates for the given item may be changed.
221 *--------------------------------------------------------------
225 PolygonCoords(canvasPtr
, itemPtr
, argc
, argv
)
226 register Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
227 Tk_Item
*itemPtr
; /* Item whose coordinates are to be
228 * read or modified. */
229 int argc
; /* Number of coordinates supplied in
231 char **argv
; /* Array of coordinates: x1, y1,
234 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
239 for (i
= 0; i
< 2*polyPtr
->numPoints
; i
++) {
240 sprintf(buffer
, "%g", polyPtr
->coordPtr
[i
]);
241 Tcl_AppendElement(canvasPtr
->interp
, buffer
, 0);
243 } else if (argc
< 6) {
244 Tcl_AppendResult(canvasPtr
->interp
,
245 "too few coordinates for polygon: must have at least 6",
248 } else if (argc
& 1) {
249 Tcl_AppendResult(canvasPtr
->interp
,
250 "odd number of coordinates specified for polygon",
255 if (polyPtr
->numPoints
!= numPoints
) {
256 if (polyPtr
->coordPtr
!= NULL
) {
257 ckfree((char *) polyPtr
->coordPtr
);
261 * One extra point gets allocated here, just in case we have
262 * to add another point to close the polygon.
265 polyPtr
->coordPtr
= (double *) ckalloc((unsigned)
266 (sizeof(double) * (argc
+2)));
267 polyPtr
->numPoints
= numPoints
;
269 for (i
= argc
-1; i
>= 0; i
--) {
270 if (TkGetCanvasCoord(canvasPtr
, argv
[i
], &polyPtr
->coordPtr
[i
])
277 * Close the polygon if it isn't already closed.
280 if ((polyPtr
->coordPtr
[argc
-2] != polyPtr
->coordPtr
[0])
281 || (polyPtr
->coordPtr
[argc
-1] != polyPtr
->coordPtr
[1])) {
282 polyPtr
->numPoints
++;
283 polyPtr
->coordPtr
[argc
] = polyPtr
->coordPtr
[0];
284 polyPtr
->coordPtr
[argc
+1] = polyPtr
->coordPtr
[1];
286 ComputePolygonBbox(canvasPtr
, polyPtr
);
292 *--------------------------------------------------------------
294 * ConfigurePolygon --
296 * This procedure is invoked to configure various aspects
297 * of a polygon item such as its background color.
300 * A standard Tcl result code. If an error occurs, then
301 * an error message is left in canvasPtr->interp->result.
304 * Configuration information, such as colors and stipple
305 * patterns, may be set for itemPtr.
307 *--------------------------------------------------------------
311 ConfigurePolygon(canvasPtr
, itemPtr
, argc
, argv
, flags
)
312 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
313 Tk_Item
*itemPtr
; /* Polygon item to reconfigure. */
314 int argc
; /* Number of elements in argv. */
315 char **argv
; /* Arguments describing things to configure. */
316 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
318 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
323 if (Tk_ConfigureWidget(canvasPtr
->interp
, canvasPtr
->tkwin
,
324 configSpecs
, argc
, argv
, (char *) polyPtr
, flags
) != TCL_OK
) {
329 * A few of the options require additional processing, such as
333 if (polyPtr
->fg
== NULL
) {
336 gcValues
.foreground
= polyPtr
->fg
->pixel
;
338 if (polyPtr
->fillStipple
!= None
) {
339 gcValues
.stipple
= polyPtr
->fillStipple
;
340 gcValues
.fill_style
= FillStippled
;
341 mask
|= GCStipple
|GCFillStyle
;
343 newGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
345 if (polyPtr
->gc
!= None
) {
346 Tk_FreeGC(polyPtr
->gc
);
351 * Keep spline parameters within reasonable limits.
354 if (polyPtr
->splineSteps
< 1) {
355 polyPtr
->splineSteps
= 1;
356 } else if (polyPtr
->splineSteps
> 100) {
357 polyPtr
->splineSteps
= 100;
360 ComputePolygonBbox(canvasPtr
, polyPtr
);
365 *--------------------------------------------------------------
369 * This procedure is called to clean up the data structure
370 * associated with a polygon item.
376 * Resources associated with itemPtr are released.
378 *--------------------------------------------------------------
382 DeletePolygon(itemPtr
)
383 Tk_Item
*itemPtr
; /* Item that is being deleted. */
385 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
387 if (polyPtr
->coordPtr
!= NULL
) {
388 ckfree((char *) polyPtr
->coordPtr
);
390 if (polyPtr
->fg
!= NULL
) {
391 Tk_FreeColor(polyPtr
->fg
);
393 if (polyPtr
->fillStipple
!= None
) {
394 Tk_FreeBitmap(polyPtr
->fillStipple
);
396 if (polyPtr
->gc
!= None
) {
397 Tk_FreeGC(polyPtr
->gc
);
402 *--------------------------------------------------------------
404 * ComputePolygonBbox --
406 * This procedure is invoked to compute the bounding box of
407 * all the pixels that may be drawn as part of a polygon.
413 * The fields x1, y1, x2, and y2 are updated in the header
416 *--------------------------------------------------------------
420 ComputePolygonBbox(canvasPtr
, polyPtr
)
421 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
422 PolygonItem
*polyPtr
; /* Item whose bbox is to be
425 register double *coordPtr
;
428 coordPtr
= polyPtr
->coordPtr
;
429 polyPtr
->header
.x1
= polyPtr
->header
.x2
= *coordPtr
;
430 polyPtr
->header
.y1
= polyPtr
->header
.y2
= coordPtr
[1];
432 for (i
= 1, coordPtr
= polyPtr
->coordPtr
+2; i
< polyPtr
->numPoints
;
433 i
++, coordPtr
+= 2) {
434 TkIncludePoint(canvasPtr
, (Tk_Item
*) polyPtr
, coordPtr
);
438 * Add one more pixel of fudge factor just to be safe (e.g.
439 * X may round differently than we do).
442 polyPtr
->header
.x1
-= 1;
443 polyPtr
->header
.x2
+= 1;
444 polyPtr
->header
.y1
-= 1;
445 polyPtr
->header
.y2
+= 1;
449 *--------------------------------------------------------------
453 * This procedure is invoked to convert a polygon to screen
454 * coordinates and display it using a particular GC.
460 * ItemPtr is drawn in drawable using the transformation
461 * information in canvasPtr.
463 *--------------------------------------------------------------
467 TkFillPolygon(canvasPtr
, coordPtr
, numPoints
, drawable
, gc
)
468 register Tk_Canvas
*canvasPtr
; /* Canvas whose coordinate system
469 * is to be used for drawing. */
470 double *coordPtr
; /* Array of coordinates for polygon:
471 * x1, y1, x2, y2, .... */
472 int numPoints
; /* Twice this many coordinates are
473 * present at *coordPtr. */
474 Drawable drawable
; /* Pixmap or window in which to draw
476 GC gc
; /* Graphics context for drawing. */
478 XPoint staticPoints
[MAX_STATIC_POINTS
];
480 register XPoint
*pPtr
;
484 * Build up an array of points in screen coordinates. Use a
485 * static array unless the polygon has an enormous number of points;
486 * in this case, dynamically allocate an array.
489 if (numPoints
<= MAX_STATIC_POINTS
) {
490 pointPtr
= staticPoints
;
492 pointPtr
= (XPoint
*) ckalloc((unsigned) (numPoints
* sizeof(XPoint
)));
495 for (i
= 0, pPtr
= pointPtr
; i
< numPoints
; i
+= 1, coordPtr
+= 2, pPtr
++) {
496 pPtr
->x
= SCREEN_X(canvasPtr
, coordPtr
[0]);
497 pPtr
->y
= SCREEN_Y(canvasPtr
, coordPtr
[1]);
501 * Display polygon, then free up polygon storage if it was dynamically
505 XFillPolygon(Tk_Display(canvasPtr
->tkwin
), drawable
, gc
, pointPtr
,
506 numPoints
, Complex
, CoordModeOrigin
);
507 if (pointPtr
!= staticPoints
) {
508 ckfree((char *) pointPtr
);
514 *--------------------------------------------------------------
518 * This procedure is invoked to draw a polygon item in a given
525 * ItemPtr is drawn in drawable using the transformation
526 * information in canvasPtr.
528 *--------------------------------------------------------------
532 DisplayPolygon(canvasPtr
, itemPtr
, drawable
)
533 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
534 Tk_Item
*itemPtr
; /* Item to be displayed. */
535 Drawable drawable
; /* Pixmap or window in which to draw
538 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
540 if (polyPtr
->gc
== None
) {
544 if (!polyPtr
->smooth
) {
545 TkFillPolygon(canvasPtr
, polyPtr
->coordPtr
, polyPtr
->numPoints
,
546 drawable
, polyPtr
->gc
);
549 XPoint staticPoints
[MAX_STATIC_POINTS
];
553 * This is a smoothed polygon. Display using a set of generated
554 * spline points rather than the original points.
557 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
558 if (numPoints
<= MAX_STATIC_POINTS
) {
559 pointPtr
= staticPoints
;
561 pointPtr
= (XPoint
*) ckalloc((unsigned)
562 (numPoints
* sizeof(XPoint
)));
564 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
565 polyPtr
->numPoints
, polyPtr
->splineSteps
, pointPtr
,
567 XFillPolygon(Tk_Display(canvasPtr
->tkwin
), drawable
, polyPtr
->gc
,
568 pointPtr
, numPoints
, Complex
, CoordModeOrigin
);
569 if (pointPtr
!= staticPoints
) {
570 ckfree((char *) pointPtr
);
576 *--------------------------------------------------------------
580 * Computes the distance from a given point to a given
581 * polygon, in canvas units.
584 * The return value is 0 if the point whose x and y coordinates
585 * are pointPtr[0] and pointPtr[1] is inside the polygon. If the
586 * point isn't inside the polygon then the return value is the
587 * distance from the point to the polygon.
592 *--------------------------------------------------------------
597 PolygonToPoint(canvasPtr
, itemPtr
, pointPtr
)
598 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
599 Tk_Item
*itemPtr
; /* Item to check against point. */
600 double *pointPtr
; /* Pointer to x and y coordinates. */
602 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
603 double *coordPtr
, distance
;
604 double staticSpace
[2*MAX_STATIC_POINTS
];
607 if (!polyPtr
->smooth
) {
608 return TkPolygonToPoint(polyPtr
->coordPtr
, polyPtr
->numPoints
,
613 * Smoothed polygon. Generate a new set of points and use them
617 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
618 if (numPoints
<= MAX_STATIC_POINTS
) {
619 coordPtr
= staticSpace
;
621 coordPtr
= (double *) ckalloc((unsigned)
622 (2*numPoints
*sizeof(double)));
624 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
625 polyPtr
->numPoints
, polyPtr
->splineSteps
, (XPoint
*) NULL
,
627 distance
= TkPolygonToPoint(coordPtr
, numPoints
, pointPtr
);
628 if (coordPtr
!= staticSpace
) {
629 ckfree((char *) coordPtr
);
635 *--------------------------------------------------------------
639 * This procedure is called to determine whether an item
640 * lies entirely inside, entirely outside, or overlapping
641 * a given rectangular area.
644 * -1 is returned if the item is entirely outside the area
645 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
646 * inside the given area.
651 *--------------------------------------------------------------
656 PolygonToArea(canvasPtr
, itemPtr
, rectPtr
)
657 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
658 Tk_Item
*itemPtr
; /* Item to check against polygon. */
659 double *rectPtr
; /* Pointer to array of four coordinates
660 * (x1, y1, x2, y2) describing rectangular
663 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
665 double staticSpace
[2*MAX_STATIC_POINTS
];
666 int numPoints
, result
;
668 if (!polyPtr
->smooth
) {
669 return TkPolygonToArea(polyPtr
->coordPtr
, polyPtr
->numPoints
, rectPtr
);
673 * Smoothed polygon. Generate a new set of points and use them
677 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
678 if (numPoints
<= MAX_STATIC_POINTS
) {
679 coordPtr
= staticSpace
;
681 coordPtr
= (double *) ckalloc((unsigned)
682 (2*numPoints
*sizeof(double)));
684 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
685 polyPtr
->numPoints
, polyPtr
->splineSteps
, (XPoint
*) NULL
,
687 result
= TkPolygonToArea(coordPtr
, numPoints
, rectPtr
);
688 if (coordPtr
!= staticSpace
) {
689 ckfree((char *) coordPtr
);
695 *--------------------------------------------------------------
699 * This procedure is invoked to rescale a polygon item.
705 * The polygon referred to by itemPtr is rescaled so that the
706 * following transformation is applied to all point
708 * x' = originX + scaleX*(x-originX)
709 * y' = originY + scaleY*(y-originY)
711 *--------------------------------------------------------------
715 ScalePolygon(canvasPtr
, itemPtr
, originX
, originY
, scaleX
, scaleY
)
716 Tk_Canvas
*canvasPtr
; /* Canvas containing polygon. */
717 Tk_Item
*itemPtr
; /* Polygon to be scaled. */
718 double originX
, originY
; /* Origin about which to scale rect. */
719 double scaleX
; /* Amount to scale in X direction. */
720 double scaleY
; /* Amount to scale in Y direction. */
722 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
723 register double *coordPtr
;
726 for (i
= 0, coordPtr
= polyPtr
->coordPtr
; i
< polyPtr
->numPoints
;
727 i
++, coordPtr
+= 2) {
728 *coordPtr
= originX
+ scaleX
*(*coordPtr
- originX
);
729 coordPtr
[1] = originY
+ scaleY
*(coordPtr
[1] - originY
);
731 ComputePolygonBbox(canvasPtr
, polyPtr
);
735 *--------------------------------------------------------------
737 * TranslatePolygon --
739 * This procedure is called to move a polygon by a given
746 * The position of the polygon is offset by (xDelta, yDelta),
747 * and the bounding box is updated in the generic part of the
750 *--------------------------------------------------------------
754 TranslatePolygon(canvasPtr
, itemPtr
, deltaX
, deltaY
)
755 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
756 Tk_Item
*itemPtr
; /* Item that is being moved. */
757 double deltaX
, deltaY
; /* Amount by which item is to be
760 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
761 register double *coordPtr
;
764 for (i
= 0, coordPtr
= polyPtr
->coordPtr
; i
< polyPtr
->numPoints
;
765 i
++, coordPtr
+= 2) {
767 coordPtr
[1] += deltaY
;
769 ComputePolygonBbox(canvasPtr
, polyPtr
);