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 *--------------------------------------------------------------
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. */
158 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
162 Tcl_AppendResult(canvasPtr
->interp
, "wrong # args: should be \"",
163 Tk_PathName(canvasPtr
->tkwin
),
164 "\" create x1 y1 x2 y2 x3 y3 ?x4 y4 ...? ?options?",
170 * Carry out initialization that is needed in order to clean
171 * up after errors during the the remainder of this procedure.
174 polyPtr
->numPoints
= 0;
175 polyPtr
->coordPtr
= NULL
;
177 polyPtr
->fillStipple
= None
;
180 polyPtr
->splineSteps
= 12;
183 * Count the number of points and then parse them into a point
184 * array. Leading arguments are assumed to be points if they
185 * start with a digit or a minus sign followed by a digit.
188 for (i
= 4; i
< (argc
-1); i
+=2) {
189 if ((!isdigit(argv
[i
][0])) &&
190 ((argv
[i
][0] != '-') || (!isdigit(argv
[i
][1])))) {
194 if (PolygonCoords(canvasPtr
, itemPtr
, i
, argv
) != TCL_OK
) {
198 if (ConfigurePolygon(canvasPtr
, itemPtr
, argc
-i
, argv
+i
, 0) == TCL_OK
) {
203 DeletePolygon(itemPtr
);
208 *--------------------------------------------------------------
212 * This procedure is invoked to process the "coords" widget
213 * command on polygons. See the user documentation for details
217 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
220 * The coordinates for the given item may be changed.
222 *--------------------------------------------------------------
227 register Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
228 Tk_Item
*itemPtr
, /* Item whose coordinates are to be
229 * read or modified. */
230 int argc
, /* Number of coordinates supplied in
232 char **argv
/* Array of coordinates: x1, y1,
236 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
241 for (i
= 0; i
< 2*polyPtr
->numPoints
; i
++) {
242 sprintf(buffer
, "%g", polyPtr
->coordPtr
[i
]);
243 Tcl_AppendElement(canvasPtr
->interp
, buffer
, 0);
245 } else if (argc
< 6) {
246 Tcl_AppendResult(canvasPtr
->interp
,
247 "too few coordinates for polygon: must have at least 6",
250 } else if (argc
& 1) {
251 Tcl_AppendResult(canvasPtr
->interp
,
252 "odd number of coordinates specified for polygon",
257 if (polyPtr
->numPoints
!= numPoints
) {
258 if (polyPtr
->coordPtr
!= NULL
) {
259 ckfree((char *) polyPtr
->coordPtr
);
263 * One extra point gets allocated here, just in case we have
264 * to add another point to close the polygon.
267 polyPtr
->coordPtr
= (double *) ckalloc((unsigned)
268 (sizeof(double) * (argc
+2)));
269 polyPtr
->numPoints
= numPoints
;
271 for (i
= argc
-1; i
>= 0; i
--) {
272 if (TkGetCanvasCoord(canvasPtr
, argv
[i
], &polyPtr
->coordPtr
[i
])
279 * Close the polygon if it isn't already closed.
282 if ((polyPtr
->coordPtr
[argc
-2] != polyPtr
->coordPtr
[0])
283 || (polyPtr
->coordPtr
[argc
-1] != polyPtr
->coordPtr
[1])) {
284 polyPtr
->numPoints
++;
285 polyPtr
->coordPtr
[argc
] = polyPtr
->coordPtr
[0];
286 polyPtr
->coordPtr
[argc
+1] = polyPtr
->coordPtr
[1];
288 ComputePolygonBbox(canvasPtr
, polyPtr
);
294 *--------------------------------------------------------------
296 * ConfigurePolygon --
298 * This procedure is invoked to configure various aspects
299 * of a polygon item such as its background color.
302 * A standard Tcl result code. If an error occurs, then
303 * an error message is left in canvasPtr->interp->result.
306 * Configuration information, such as colors and stipple
307 * patterns, may be set for itemPtr.
309 *--------------------------------------------------------------
314 Tk_Canvas
*canvasPtr
, /* Canvas containing itemPtr. */
315 Tk_Item
*itemPtr
, /* Polygon item to reconfigure. */
316 int argc
, /* Number of elements in argv. */
317 char **argv
, /* Arguments describing things to configure. */
318 int flags
/* Flags to pass to Tk_ConfigureWidget. */
321 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
326 if (Tk_ConfigureWidget(canvasPtr
->interp
, canvasPtr
->tkwin
,
327 configSpecs
, argc
, argv
, (char *) polyPtr
, flags
) != TCL_OK
) {
332 * A few of the options require additional processing, such as
336 if (polyPtr
->fg
== NULL
) {
339 gcValues
.foreground
= polyPtr
->fg
->pixel
;
341 if (polyPtr
->fillStipple
!= None
) {
342 gcValues
.stipple
= polyPtr
->fillStipple
;
343 gcValues
.fill_style
= FillStippled
;
344 mask
|= GCStipple
|GCFillStyle
;
346 newGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
348 if (polyPtr
->gc
!= None
) {
349 Tk_FreeGC(polyPtr
->gc
);
354 * Keep spline parameters within reasonable limits.
357 if (polyPtr
->splineSteps
< 1) {
358 polyPtr
->splineSteps
= 1;
359 } else if (polyPtr
->splineSteps
> 100) {
360 polyPtr
->splineSteps
= 100;
363 ComputePolygonBbox(canvasPtr
, polyPtr
);
368 *--------------------------------------------------------------
372 * This procedure is called to clean up the data structure
373 * associated with a polygon item.
379 * Resources associated with itemPtr are released.
381 *--------------------------------------------------------------
386 Tk_Item
*itemPtr
/* Item that is being deleted. */
389 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
391 if (polyPtr
->coordPtr
!= NULL
) {
392 ckfree((char *) polyPtr
->coordPtr
);
394 if (polyPtr
->fg
!= NULL
) {
395 Tk_FreeColor(polyPtr
->fg
);
397 if (polyPtr
->fillStipple
!= None
) {
398 Tk_FreeBitmap(polyPtr
->fillStipple
);
400 if (polyPtr
->gc
!= None
) {
401 Tk_FreeGC(polyPtr
->gc
);
406 *--------------------------------------------------------------
408 * ComputePolygonBbox --
410 * This procedure is invoked to compute the bounding box of
411 * all the pixels that may be drawn as part of a polygon.
417 * The fields x1, y1, x2, and y2 are updated in the header
420 *--------------------------------------------------------------
425 register Tk_Canvas
*canvasPtr
, /* Canvas that contains item. */
426 PolygonItem
*polyPtr
/* Item whose bbox is to be
430 register double *coordPtr
;
433 coordPtr
= polyPtr
->coordPtr
;
434 polyPtr
->header
.x1
= polyPtr
->header
.x2
= *coordPtr
;
435 polyPtr
->header
.y1
= polyPtr
->header
.y2
= coordPtr
[1];
437 for (i
= 1, coordPtr
= polyPtr
->coordPtr
+2; i
< polyPtr
->numPoints
;
438 i
++, coordPtr
+= 2) {
439 TkIncludePoint(canvasPtr
, (Tk_Item
*) polyPtr
, coordPtr
);
443 * Add one more pixel of fudge factor just to be safe (e.g.
444 * X may round differently than we do).
447 polyPtr
->header
.x1
-= 1;
448 polyPtr
->header
.x2
+= 1;
449 polyPtr
->header
.y1
-= 1;
450 polyPtr
->header
.y2
+= 1;
454 *--------------------------------------------------------------
458 * This procedure is invoked to convert a polygon to screen
459 * coordinates and display it using a particular GC.
465 * ItemPtr is drawn in drawable using the transformation
466 * information in canvasPtr.
468 *--------------------------------------------------------------
473 register Tk_Canvas
*canvasPtr
, /* Canvas whose coordinate system
474 * is to be used for drawing. */
475 double *coordPtr
, /* Array of coordinates for polygon:
476 * x1, y1, x2, y2, .... */
477 int numPoints
, /* Twice this many coordinates are
478 * present at *coordPtr. */
479 Drawable drawable
, /* Pixmap or window in which to draw
481 GC gc
/* Graphics context for drawing. */
484 XPoint staticPoints
[MAX_STATIC_POINTS
];
486 register XPoint
*pPtr
;
490 * Build up an array of points in screen coordinates. Use a
491 * static array unless the polygon has an enormous number of points;
492 * in this case, dynamically allocate an array.
495 if (numPoints
<= MAX_STATIC_POINTS
) {
496 pointPtr
= staticPoints
;
498 pointPtr
= (XPoint
*) ckalloc((unsigned) (numPoints
* sizeof(XPoint
)));
501 for (i
= 0, pPtr
= pointPtr
; i
< numPoints
; i
+= 1, coordPtr
+= 2, pPtr
++) {
502 pPtr
->x
= SCREEN_X(canvasPtr
, coordPtr
[0]);
503 pPtr
->y
= SCREEN_Y(canvasPtr
, coordPtr
[1]);
507 * Display polygon, then free up polygon storage if it was dynamically
511 XFillPolygon(Tk_Display(canvasPtr
->tkwin
), drawable
, gc
, pointPtr
,
512 numPoints
, Complex
, CoordModeOrigin
);
513 if (pointPtr
!= staticPoints
) {
514 ckfree((char *) pointPtr
);
520 *--------------------------------------------------------------
524 * This procedure is invoked to draw a polygon item in a given
531 * ItemPtr is drawn in drawable using the transformation
532 * information in canvasPtr.
534 *--------------------------------------------------------------
539 register Tk_Canvas
*canvasPtr
, /* Canvas that contains item. */
540 Tk_Item
*itemPtr
, /* Item to be displayed. */
541 Drawable drawable
/* Pixmap or window in which to draw
545 register PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
547 if (polyPtr
->gc
== None
) {
551 if (!polyPtr
->smooth
) {
552 TkFillPolygon(canvasPtr
, polyPtr
->coordPtr
, polyPtr
->numPoints
,
553 drawable
, polyPtr
->gc
);
556 XPoint staticPoints
[MAX_STATIC_POINTS
];
560 * This is a smoothed polygon. Display using a set of generated
561 * spline points rather than the original points.
564 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
565 if (numPoints
<= MAX_STATIC_POINTS
) {
566 pointPtr
= staticPoints
;
568 pointPtr
= (XPoint
*) ckalloc((unsigned)
569 (numPoints
* sizeof(XPoint
)));
571 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
572 polyPtr
->numPoints
, polyPtr
->splineSteps
, pointPtr
,
574 XFillPolygon(Tk_Display(canvasPtr
->tkwin
), drawable
, polyPtr
->gc
,
575 pointPtr
, numPoints
, Complex
, CoordModeOrigin
);
576 if (pointPtr
!= staticPoints
) {
577 ckfree((char *) pointPtr
);
583 *--------------------------------------------------------------
587 * Computes the distance from a given point to a given
588 * polygon, in canvas units.
591 * The return value is 0 if the point whose x and y coordinates
592 * are pointPtr[0] and pointPtr[1] is inside the polygon. If the
593 * point isn't inside the polygon then the return value is the
594 * distance from the point to the polygon.
599 *--------------------------------------------------------------
605 Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
606 Tk_Item
*itemPtr
, /* Item to check against point. */
607 double *pointPtr
/* Pointer to x and y coordinates. */
610 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
611 double *coordPtr
, distance
;
612 double staticSpace
[2*MAX_STATIC_POINTS
];
615 if (!polyPtr
->smooth
) {
616 return TkPolygonToPoint(polyPtr
->coordPtr
, polyPtr
->numPoints
,
621 * Smoothed polygon. Generate a new set of points and use them
625 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
626 if (numPoints
<= MAX_STATIC_POINTS
) {
627 coordPtr
= staticSpace
;
629 coordPtr
= (double *) ckalloc((unsigned)
630 (2*numPoints
*sizeof(double)));
632 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
633 polyPtr
->numPoints
, polyPtr
->splineSteps
, (XPoint
*) NULL
,
635 distance
= TkPolygonToPoint(coordPtr
, numPoints
, pointPtr
);
636 if (coordPtr
!= staticSpace
) {
637 ckfree((char *) coordPtr
);
643 *--------------------------------------------------------------
647 * This procedure is called to determine whether an item
648 * lies entirely inside, entirely outside, or overlapping
649 * a given rectangular area.
652 * -1 is returned if the item is entirely outside the area
653 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
654 * inside the given area.
659 *--------------------------------------------------------------
665 Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
666 Tk_Item
*itemPtr
, /* Item to check against polygon. */
667 double *rectPtr
/* Pointer to array of four coordinates
668 * (x1, y1, x2, y2) describing rectangular
672 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
674 double staticSpace
[2*MAX_STATIC_POINTS
];
675 int numPoints
, result
;
677 if (!polyPtr
->smooth
) {
678 return TkPolygonToArea(polyPtr
->coordPtr
, polyPtr
->numPoints
, rectPtr
);
682 * Smoothed polygon. Generate a new set of points and use them
686 numPoints
= 1 + polyPtr
->numPoints
*polyPtr
->splineSteps
;
687 if (numPoints
<= MAX_STATIC_POINTS
) {
688 coordPtr
= staticSpace
;
690 coordPtr
= (double *) ckalloc((unsigned)
691 (2*numPoints
*sizeof(double)));
693 numPoints
= TkMakeBezierCurve(canvasPtr
, polyPtr
->coordPtr
,
694 polyPtr
->numPoints
, polyPtr
->splineSteps
, (XPoint
*) NULL
,
696 result
= TkPolygonToArea(coordPtr
, numPoints
, rectPtr
);
697 if (coordPtr
!= staticSpace
) {
698 ckfree((char *) coordPtr
);
704 *--------------------------------------------------------------
708 * This procedure is invoked to rescale a polygon item.
714 * The polygon referred to by itemPtr is rescaled so that the
715 * following transformation is applied to all point
717 * x' = originX + scaleX*(x-originX)
718 * y' = originY + scaleY*(y-originY)
720 *--------------------------------------------------------------
725 Tk_Canvas
*canvasPtr
, /* Canvas containing polygon. */
726 Tk_Item
*itemPtr
, /* Polygon to be scaled. */
728 double originY
, /* Origin about which to scale rect. */
729 double scaleX
, /* Amount to scale in X direction. */
730 double scaleY
/* Amount to scale in Y direction. */
733 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
734 register double *coordPtr
;
737 for (i
= 0, coordPtr
= polyPtr
->coordPtr
; i
< polyPtr
->numPoints
;
738 i
++, coordPtr
+= 2) {
739 *coordPtr
= originX
+ scaleX
*(*coordPtr
- originX
);
740 coordPtr
[1] = originY
+ scaleY
*(coordPtr
[1] - originY
);
742 ComputePolygonBbox(canvasPtr
, polyPtr
);
746 *--------------------------------------------------------------
748 * TranslatePolygon --
750 * This procedure is called to move a polygon by a given
757 * The position of the polygon is offset by (xDelta, yDelta),
758 * and the bounding box is updated in the generic part of the
761 *--------------------------------------------------------------
766 Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
767 Tk_Item
*itemPtr
, /* Item that is being moved. */
769 double deltaY
/* Amount by which item is to be
773 PolygonItem
*polyPtr
= (PolygonItem
*) itemPtr
;
774 register double *coordPtr
;
777 for (i
= 0, coordPtr
= polyPtr
->coordPtr
; i
< polyPtr
->numPoints
;
778 i
++, coordPtr
+= 2) {
780 coordPtr
[1] += deltaY
;
782 ComputePolygonBbox(canvasPtr
, polyPtr
);