1 /*
2 * tkCanvArc.c --
3 *
4 * This file implements arc items for canvas widgets.
5 *
6 * Copyright (c) 1992-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 */
12
13 #include "tkInt.h"
14 #include "tkCanvas.h"
15 #include "default.h"
16
17 /*
18 * The structure below defines the record for each arc item.
19 */
20
21 typedef enum {
22 PIESLICE_STYLE, CHORD_STYLE, ARC_STYLE
23 } Style;
24
25 typedef struct ArcItem {
26 Tk_Item header; /* Generic stuff that's the same for all
27 * types. MUST BE FIRST IN STRUCTURE. */
28 Tk_Outline outline; /* Outline structure */
29 double bbox[4]; /* Coordinates (x1, y1, x2, y2) of bounding
30 * box for oval of which arc is a piece. */
31 double start; /* Angle at which arc begins, in degrees
32 * between 0 and 360. */
33 double extent; /* Extent of arc (angular distance from start
34 * to end of arc) in degrees between -360 and
35 * 360. */
36 double *outlinePtr; /* Points to (x,y) coordinates for points that
37 * define one or two closed polygons
38 * representing the portion of the outline
39 * that isn't part of the arc (the V-shape for
40 * a pie slice or a line-like segment for a
41 * chord). Malloc'ed. */
42 int numOutlinePoints; /* Number of points at outlinePtr. Zero means
43 * no space allocated. */
44 Tk_TSOffset tsoffset;
45 XColor *fillColor; /* Color for filling arc (used for drawing
46 * outline too when style is "arc"). NULL
47 * means don't fill arc. */
48 XColor *activeFillColor; /* Color for filling arc (used for drawing
49 * outline too when style is "arc" and state
50 * is "active"). NULL means use fillColor. */
51 XColor *disabledFillColor; /* Color for filling arc (used for drawing
52 * outline too when style is "arc" and state
53 * is "disabled". NULL means use fillColor */
54 Pixmap fillStipple; /* Stipple bitmap for filling item. */
55 Pixmap activeFillStipple; /* Stipple bitmap for filling item if state is
56 * active. */
57 Pixmap disabledFillStipple; /* Stipple bitmap for filling item if state is
58 * disabled. */
59 Style style; /* How to draw arc: arc, chord, or
60 * pieslice. */
61 GC fillGC; /* Graphics context for filling item. */
62 double center1[2]; /* Coordinates of center of arc outline at
63 * start (see ComputeArcOutline). */
64 double center2[2]; /* Coordinates of center of arc outline at
65 * start+extent (see ComputeArcOutline). */
66 } ArcItem;
67
68 /*
69 * The definitions below define the sizes of the polygons used to display
70 * outline information for various styles of arcs:
71 */
72
73 #define CHORD_OUTLINE_PTS 7
74 #define PIE_OUTLINE1_PTS 6
75 #define PIE_OUTLINE2_PTS 7
76
77 /*
78 * Information used for parsing configuration specs:
79 */
80
81 static int StyleParseProc(ClientData clientData, Tcl_Interp *interp,
82 Tk_Window tkwin, const char *value,
83 char *widgRec, int offset);
84 static const char * StylePrintProc(ClientData clientData, Tk_Window tkwin,
85 char *widgRec, int offset, Tcl_FreeProc **freeProcPtr);
86
87 static const Tk_CustomOption stateOption = {
88 TkStateParseProc, TkStatePrintProc, INT2PTR(2)
89 };
90 static const Tk_CustomOption styleOption = {
91 StyleParseProc, StylePrintProc, NULL
92 };
93 static const Tk_CustomOption tagsOption = {
94 Tk_CanvasTagsParseProc, Tk_CanvasTagsPrintProc, NULL
95 };
96 static const Tk_CustomOption dashOption = {
97 TkCanvasDashParseProc, TkCanvasDashPrintProc, NULL
98 };
99 static const Tk_CustomOption offsetOption = {
100 TkOffsetParseProc, TkOffsetPrintProc, INT2PTR(TK_OFFSET_RELATIVE)
101 };
102 static const Tk_CustomOption pixelOption = {
103 TkPixelParseProc, TkPixelPrintProc, NULL
104 };
105
106 static const Tk_ConfigSpec configSpecs[] = {
107 {TK_CONFIG_CUSTOM, "-activedash", NULL, NULL,
108 NULL, Tk_Offset(ArcItem, outline.activeDash),
109 TK_CONFIG_NULL_OK, &dashOption},
110 {TK_CONFIG_COLOR, "-activefill", NULL, NULL,
111 NULL, Tk_Offset(ArcItem, activeFillColor), TK_CONFIG_NULL_OK, NULL},
112 {TK_CONFIG_COLOR, "-activeoutline", NULL, NULL,
113 NULL, Tk_Offset(ArcItem, outline.activeColor), TK_CONFIG_NULL_OK, NULL},
114 {TK_CONFIG_BITMAP, "-activeoutlinestipple", NULL, NULL,
115 NULL, Tk_Offset(ArcItem, outline.activeStipple), TK_CONFIG_NULL_OK, NULL},
116 {TK_CONFIG_BITMAP, "-activestipple", NULL, NULL,
117 NULL, Tk_Offset(ArcItem, activeFillStipple), TK_CONFIG_NULL_OK, NULL},
118 {TK_CONFIG_CUSTOM, "-activewidth", NULL, NULL,
119 "0.0", Tk_Offset(ArcItem, outline.activeWidth),
120 TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
121 {TK_CONFIG_CUSTOM, "-dash", NULL, NULL,
122 NULL, Tk_Offset(ArcItem, outline.dash),
123 TK_CONFIG_NULL_OK, &dashOption},
124 {TK_CONFIG_PIXELS, "-dashoffset", NULL, NULL,
125 "0", Tk_Offset(ArcItem, outline.offset), TK_CONFIG_DONT_SET_DEFAULT, NULL},
126 {TK_CONFIG_CUSTOM, "-disableddash", NULL, NULL,
127 NULL, Tk_Offset(ArcItem, outline.disabledDash),
128 TK_CONFIG_NULL_OK, &dashOption},
129 {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL,
130 NULL, Tk_Offset(ArcItem, disabledFillColor), TK_CONFIG_NULL_OK, NULL},
131 {TK_CONFIG_COLOR, "-disabledoutline", NULL, NULL,
132 NULL, Tk_Offset(ArcItem, outline.disabledColor), TK_CONFIG_NULL_OK, NULL},
133 {TK_CONFIG_BITMAP, "-disabledoutlinestipple", NULL, NULL,
134 NULL, Tk_Offset(ArcItem, outline.disabledStipple), TK_CONFIG_NULL_OK, NULL},
135 {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL,
136 NULL, Tk_Offset(ArcItem, disabledFillStipple), TK_CONFIG_NULL_OK, NULL},
137 {TK_CONFIG_CUSTOM, "-disabledwidth", NULL, NULL,
138 "0.0", Tk_Offset(ArcItem, outline.disabledWidth),
139 TK_CONFIG_DONT_SET_DEFAULT, &pixelOption},
140 {TK_CONFIG_DOUBLE, "-extent", NULL, NULL,
141 "90", Tk_Offset(ArcItem, extent), TK_CONFIG_DONT_SET_DEFAULT, NULL},
142 {TK_CONFIG_COLOR, "-fill", NULL, NULL,
143 NULL, Tk_Offset(ArcItem, fillColor), TK_CONFIG_NULL_OK, NULL},
144 {TK_CONFIG_CUSTOM, "-offset", NULL, NULL,
145 "0,0", Tk_Offset(ArcItem, tsoffset),
146 TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},
147 {TK_CONFIG_COLOR, "-outline", NULL, NULL,
148 DEF_CANVITEM_OUTLINE, Tk_Offset(ArcItem, outline.color), TK_CONFIG_NULL_OK, NULL},
149 {TK_CONFIG_CUSTOM, "-outlineoffset", NULL, NULL,
150 "0,0", Tk_Offset(ArcItem, outline.tsoffset),
151 TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},
152 {TK_CONFIG_BITMAP, "-outlinestipple", NULL, NULL,
153 NULL, Tk_Offset(ArcItem, outline.stipple), TK_CONFIG_NULL_OK, NULL},
154 {TK_CONFIG_DOUBLE, "-start", NULL, NULL,
155 "0", Tk_Offset(ArcItem, start), TK_CONFIG_DONT_SET_DEFAULT, NULL},
156 {TK_CONFIG_CUSTOM, "-state", NULL, NULL,
157 NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, &stateOption},
158 {TK_CONFIG_BITMAP, "-stipple", NULL, NULL,
159 NULL, Tk_Offset(ArcItem, fillStipple), TK_CONFIG_NULL_OK, NULL},
160 {TK_CONFIG_CUSTOM, "-style", NULL, NULL,
161 NULL, Tk_Offset(ArcItem, style), TK_CONFIG_DONT_SET_DEFAULT,
162 &styleOption},
163 {TK_CONFIG_CUSTOM, "-tags", NULL, NULL,
164 NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
165 {TK_CONFIG_CUSTOM, "-width", NULL, NULL,
166 "1.0", Tk_Offset(ArcItem, outline.width), TK_CONFIG_DONT_SET_DEFAULT,
167 &pixelOption},
168 {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0, NULL}
169 };
170
171 /*
172 * Prototypes for functions defined in this file:
173 */
174
175 static void ComputeArcBbox(Tk_Canvas canvas, ArcItem *arcPtr);
176 static int ConfigureArc(Tcl_Interp *interp,
177 Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
178 Tcl_Obj *const objv[], int flags);
179 static int CreateArc(Tcl_Interp *interp,
180 Tk_Canvas canvas, struct Tk_Item *itemPtr,
181 int objc, Tcl_Obj *const objv[]);
182 static void DeleteArc(Tk_Canvas canvas,
183 Tk_Item *itemPtr, Display *display);
184 static void DisplayArc(Tk_Canvas canvas,
185 Tk_Item *itemPtr, Display *display, Drawable dst,
186 int x, int y, int width, int height);
187 static int ArcCoords(Tcl_Interp *interp, Tk_Canvas canvas,
188 Tk_Item *itemPtr, int objc, Tcl_Obj *const objv[]);
189 static int ArcToArea(Tk_Canvas canvas,
190 Tk_Item *itemPtr, double *rectPtr);
191 static double ArcToPoint(Tk_Canvas canvas,
192 Tk_Item *itemPtr, double *coordPtr);
193 static int ArcToPostscript(Tcl_Interp *interp,
194 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass);
195 static void ScaleArc(Tk_Canvas canvas,
196 Tk_Item *itemPtr, double originX, double originY,
197 double scaleX, double scaleY);
198 static void TranslateArc(Tk_Canvas canvas,
199 Tk_Item *itemPtr, double deltaX, double deltaY);
200 static int AngleInRange(double x, double y,
201 double start, double extent);
202 static void ComputeArcOutline(Tk_Canvas canvas, ArcItem *arcPtr);
203 static int HorizLineToArc(double x1, double x2,
204 double y, double rx, double ry,
205 double start, double extent);
206 static int VertLineToArc(double x, double y1,
207 double y2, double rx, double ry,
208 double start, double extent);
209
210 /*
211 * The structures below defines the arc item types by means of functions that
212 * can be invoked by generic item code.
213 */
214
215 Tk_ItemType tkArcType = {
216 "arc", /* name */
217 sizeof(ArcItem), /* itemSize */
218 CreateArc, /* createProc */
219 configSpecs, /* configSpecs */
220 ConfigureArc, /* configureProc */
221 ArcCoords, /* coordProc */
222 DeleteArc, /* deleteProc */
223 DisplayArc, /* displayProc */
224 TK_CONFIG_OBJS, /* flags */
225 ArcToPoint, /* pointProc */
226 ArcToArea, /* areaProc */
227 ArcToPostscript, /* postscriptProc */
228 ScaleArc, /* scaleProc */
229 TranslateArc, /* translateProc */
230 NULL, /* indexProc */
231 NULL, /* icursorProc */
232 NULL, /* selectionProc */
233 NULL, /* insertProc */
234 NULL, /* dTextProc */
235 NULL, /* nextPtr */
236 NULL, 0, NULL, NULL
237 };
238
239 /*
240 *--------------------------------------------------------------
241 *
242 * CreateArc --
243 *
244 * This function is invoked to create a new arc item in a canvas.
245 *
246 * Results:
247 * A standard Tcl return value. If an error occurred in creating the
248 * item, then an error message is left in the interp's result; in this
249 * case itemPtr is left uninitialized, so it can be safely freed by the
250 * caller.
251 *
252 * Side effects:
253 * A new arc item is created.
254 *
255 *--------------------------------------------------------------
256 */
257
258 static int
CreateArc(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[])259 CreateArc(
260 Tcl_Interp *interp, /* Interpreter for error reporting. */
261 Tk_Canvas canvas, /* Canvas to hold new item. */
262 Tk_Item *itemPtr, /* Record to hold new item; header has been
263 * initialized by caller. */
264 int objc, /* Number of arguments in objv. */
265 Tcl_Obj *const objv[]) /* Arguments describing arc. */
266 {
267 ArcItem *arcPtr = (ArcItem *) itemPtr;
268 int i;
269
270 if (objc == 0) {
271 Tcl_Panic("canvas did not pass any coords");
272 }
273
274 /*
275 * Carry out initialization that is needed in order to clean up after
276 * errors during the the remainder of this function.
277 */
278
279 Tk_CreateOutline(&(arcPtr->outline));
280 arcPtr->start = 0;
281 arcPtr->extent = 90;
282 arcPtr->outlinePtr = NULL;
283 arcPtr->numOutlinePoints = 0;
284 arcPtr->tsoffset.flags = 0;
285 arcPtr->tsoffset.xoffset = 0;
286 arcPtr->tsoffset.yoffset = 0;
287 arcPtr->fillColor = NULL;
288 arcPtr->activeFillColor = NULL;
289 arcPtr->disabledFillColor = NULL;
290 arcPtr->fillStipple = None;
291 arcPtr->activeFillStipple = None;
292 arcPtr->disabledFillStipple = None;
293 arcPtr->style = PIESLICE_STYLE;
294 arcPtr->fillGC = NULL;
295
296 /*
297 * Process the arguments to fill in the item record.
298 */
299
300 for (i = 1; i < objc; i++) {
301 const char *arg = Tcl_GetString(objv[i]);
302
303 if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
304 break;
305 }
306 }
307 if (ArcCoords(interp, canvas, itemPtr, i, objv) != TCL_OK) {
308 goto error;
309 }
310 if (ConfigureArc(interp, canvas, itemPtr, objc-i, objv+i, 0) == TCL_OK) {
311 return TCL_OK;
312 }
313
314 error:
315 DeleteArc(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
316 return TCL_ERROR;
317 }
318
319 /*
320 *--------------------------------------------------------------
321 *
322 * ArcCoords --
323 *
324 * This function is invoked to process the "coords" widget command on
325 * arcs. See the user documentation for details on what it does.
326 *
327 * Results:
328 * Returns TCL_OK or TCL_ERROR, and sets the interp's result.
329 *
330 * Side effects:
331 * The coordinates for the given item may be changed.
332 *
333 *--------------------------------------------------------------
334 */
335
336 static int
ArcCoords(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[])337 ArcCoords(
338 Tcl_Interp *interp, /* Used for error reporting. */
339 Tk_Canvas canvas, /* Canvas containing item. */
340 Tk_Item *itemPtr, /* Item whose coordinates are to be read or
341 * modified. */
342 int objc, /* Number of coordinates supplied in objv. */
343 Tcl_Obj *const objv[]) /* Array of coordinates: x1, y1, x2, y2, ... */
344 {
345 ArcItem *arcPtr = (ArcItem *) itemPtr;
346
347 if (objc == 0) {
348 Tcl_Obj *objs[4];
349
350 objs[0] = Tcl_NewDoubleObj(arcPtr->bbox[0]);
351 objs[1] = Tcl_NewDoubleObj(arcPtr->bbox[1]);
352 objs[2] = Tcl_NewDoubleObj(arcPtr->bbox[2]);
353 objs[3] = Tcl_NewDoubleObj(arcPtr->bbox[3]);
354 Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs));
355 } else if ((objc == 1) || (objc == 4)) {
356 if (objc == 1) {
357 if (Tcl_ListObjGetElements(interp, objv[0], &objc,
358 (Tcl_Obj ***) &objv) != TCL_OK) {
359 return TCL_ERROR;
360 } else if (objc != 4) {
361 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
362 "wrong # coordinates: expected 4, got %d", objc));
363 Tcl_SetErrorCode(interp, "TK", "CANVAS", "COORDS", "ARC",
364 NULL);
365 return TCL_ERROR;
366 }
367 }
368 if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
369 &arcPtr->bbox[0]) != TCL_OK)
370 || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
371 &arcPtr->bbox[1]) != TCL_OK)
372 || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[2],
373 &arcPtr->bbox[2]) != TCL_OK)
374 || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[3],
375 &arcPtr->bbox[3]) != TCL_OK)) {
376 return TCL_ERROR;
377 }
378 ComputeArcBbox(canvas, arcPtr);
379 } else {
380 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
381 "wrong # coordinates: expected 0 or 4, got %d", objc));
382 Tcl_SetErrorCode(interp, "TK", "CANVAS", "COORDS", "ARC", NULL);
383 return TCL_ERROR;
384 }
385 return TCL_OK;
386 }
387
388 /*
389 *--------------------------------------------------------------
390 *
391 * ConfigureArc --
392 *
393 * This function is invoked to configure various aspects of a arc item,
394 * such as its outline and fill colors.
395 *
396 * Results:
397 * A standard Tcl result code. If an error occurs, then an error message
398 * is left in the interp's result.
399 *
400 * Side effects:
401 * Configuration information, such as colors and stipple patterns, may be
402 * set for itemPtr.
403 *
404 *--------------------------------------------------------------
405 */
406
407 static int
ConfigureArc(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int objc,Tcl_Obj * const objv[],int flags)408 ConfigureArc(
409 Tcl_Interp *interp, /* Used for error reporting. */
410 Tk_Canvas canvas, /* Canvas containing itemPtr. */
411 Tk_Item *itemPtr, /* Arc item to reconfigure. */
412 int objc, /* Number of elements in objv. */
413 Tcl_Obj *const objv[], /* Arguments describing things to configure. */
414 int flags) /* Flags to pass to Tk_ConfigureWidget. */
415 {
416 ArcItem *arcPtr = (ArcItem *) itemPtr;
417 XGCValues gcValues;
418 GC newGC;
419 unsigned long mask;
420 int i;
421 Tk_Window tkwin;
422 Tk_TSOffset *tsoffset;
423 XColor *color;
424 Pixmap stipple;
425 Tk_State state;
426
427 tkwin = Tk_CanvasTkwin(canvas);
428 if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
429 (const char **) objv, (char *) arcPtr, flags|TK_CONFIG_OBJS)) {
430 return TCL_ERROR;
431 }
432
433 state = itemPtr->state;
434
435 /*
436 * A few of the options require additional processing, such as style and
437 * graphics contexts.
438 */
439
440 if (arcPtr->outline.activeWidth > arcPtr->outline.width ||
441 arcPtr->outline.activeDash.number != 0 ||
442 arcPtr->outline.activeColor != NULL ||
443 arcPtr->outline.activeStipple != None ||
444 arcPtr->activeFillColor != NULL ||
445 arcPtr->activeFillStipple != None) {
446 itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
447 } else {
448 itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
449 }
450
451 tsoffset = &arcPtr->outline.tsoffset;
452 flags = tsoffset->flags;
453 if (flags & TK_OFFSET_LEFT) {
454 tsoffset->xoffset = (int) (arcPtr->bbox[0] + 0.5);
455 } else if (flags & TK_OFFSET_CENTER) {
456 tsoffset->xoffset = (int) ((arcPtr->bbox[0]+arcPtr->bbox[2]+1)/2);
457 } else if (flags & TK_OFFSET_RIGHT) {
458 tsoffset->xoffset = (int) (arcPtr->bbox[2] + 0.5);
459 }
460 if (flags & TK_OFFSET_TOP) {
461 tsoffset->yoffset = (int) (arcPtr->bbox[1] + 0.5);
462 } else if (flags & TK_OFFSET_MIDDLE) {
463 tsoffset->yoffset = (int) ((arcPtr->bbox[1]+arcPtr->bbox[3]+1)/2);
464 } else if (flags & TK_OFFSET_BOTTOM) {
465 tsoffset->yoffset = (int) (arcPtr->bbox[2] + 0.5);
466 }
467
468 i = (int) (arcPtr->start/360.0);
469 arcPtr->start -= i*360.0;
470 if (arcPtr->start < 0) {
471 arcPtr->start += 360.0;
472 }
473 i = (int) (arcPtr->extent/360.0);
474 arcPtr->extent -= i*360.0;
475
476 mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, &(arcPtr->outline));
477 if (mask) {
478 gcValues.cap_style = CapButt;
479 mask |= GCCapStyle;
480 newGC = Tk_GetGC(tkwin, mask, &gcValues);
481 } else {
482 newGC = NULL;
483 }
484 if (arcPtr->outline.gc != NULL) {
485 Tk_FreeGC(Tk_Display(tkwin), arcPtr->outline.gc);
486 }
487 arcPtr->outline.gc = newGC;
488
489 if(state == TK_STATE_NULL) {
490 state = Canvas(canvas)->canvas_state;
491 }
492 if (state==TK_STATE_HIDDEN) {
493 ComputeArcBbox(canvas, arcPtr);
494 return TCL_OK;
495 }
496
497 color = arcPtr->fillColor;
498 stipple = arcPtr->fillStipple;
499 if (Canvas(canvas)->currentItemPtr == itemPtr) {
500 if (arcPtr->activeFillColor!=NULL) {
501 color = arcPtr->activeFillColor;
502 }
503 if (arcPtr->activeFillStipple!=None) {
504 stipple = arcPtr->activeFillStipple;
505 }
506 } else if (state==TK_STATE_DISABLED) {
507 if (arcPtr->disabledFillColor!=NULL) {
508 color = arcPtr->disabledFillColor;
509 }
510 if (arcPtr->disabledFillStipple!=None) {
511 stipple = arcPtr->disabledFillStipple;
512 }
513 }
514
515 if (arcPtr->style == ARC_STYLE) {
516 newGC = NULL;
517 } else if (color == NULL) {
518 newGC = NULL;
519 } else {
520 gcValues.foreground = color->pixel;
521 if (arcPtr->style == CHORD_STYLE) {
522 gcValues.arc_mode = ArcChord;
523 } else {
524 gcValues.arc_mode = ArcPieSlice;
525 }
526 mask = GCForeground|GCArcMode;
527 if (stipple != None) {
528 gcValues.stipple = stipple;
529 gcValues.fill_style = FillStippled;
530 mask |= GCStipple|GCFillStyle;
531 }
532 newGC = Tk_GetGC(tkwin, mask, &gcValues);
533 }
534 if (arcPtr->fillGC != NULL) {
535 Tk_FreeGC(Tk_Display(tkwin), arcPtr->fillGC);
536 }
537 arcPtr->fillGC = newGC;
538
539 tsoffset = &arcPtr->tsoffset;
540 flags = tsoffset->flags;
541 if (flags & TK_OFFSET_LEFT) {
542 tsoffset->xoffset = (int) (arcPtr->bbox[0] + 0.5);
543 } else if (flags & TK_OFFSET_CENTER) {
544 tsoffset->xoffset = (int) ((arcPtr->bbox[0]+arcPtr->bbox[2]+1)/2);
545 } else if (flags & TK_OFFSET_RIGHT) {
546 tsoffset->xoffset = (int) (arcPtr->bbox[2] + 0.5);
547 }
548 if (flags & TK_OFFSET_TOP) {
549 tsoffset->yoffset = (int) (arcPtr->bbox[1] + 0.5);
550 } else if (flags & TK_OFFSET_MIDDLE) {
551 tsoffset->yoffset = (int) ((arcPtr->bbox[1]+arcPtr->bbox[3]+1)/2);
552 } else if (flags & TK_OFFSET_BOTTOM) {
553 tsoffset->yoffset = (int) (arcPtr->bbox[3] + 0.5);
554 }
555
556 ComputeArcBbox(canvas, arcPtr);
557 return TCL_OK;
558 }
559
560 /*
561 *--------------------------------------------------------------
562 *
563 * DeleteArc --
564 *
565 * This function is called to clean up the data structure associated with
566 * an arc item.
567 *
568 * Results:
569 * None.
570 *
571 * Side effects:
572 * Resources associated with itemPtr are released.
573 *
574 *--------------------------------------------------------------
575 */
576
577 static void
DeleteArc(TCL_UNUSED (Tk_Canvas),Tk_Item * itemPtr,Display * display)578 DeleteArc(
579 TCL_UNUSED(Tk_Canvas), /* Info about overall canvas. */
580 Tk_Item *itemPtr, /* Item that is being deleted. */
581 Display *display) /* Display containing window for canvas. */
582 {
583 ArcItem *arcPtr = (ArcItem *)itemPtr;
584
585 Tk_DeleteOutline(display, &(arcPtr->outline));
586 if (arcPtr->numOutlinePoints != 0) {
587 ckfree(arcPtr->outlinePtr);
588 }
589 if (arcPtr->fillColor != NULL) {
590 Tk_FreeColor(arcPtr->fillColor);
591 }
592 if (arcPtr->activeFillColor != NULL) {
593 Tk_FreeColor(arcPtr->activeFillColor);
594 }
595 if (arcPtr->disabledFillColor != NULL) {
596 Tk_FreeColor(arcPtr->disabledFillColor);
597 }
598 if (arcPtr->fillStipple != None) {
599 Tk_FreeBitmap(display, arcPtr->fillStipple);
600 }
601 if (arcPtr->activeFillStipple != None) {
602 Tk_FreeBitmap(display, arcPtr->activeFillStipple);
603 }
604 if (arcPtr->disabledFillStipple != None) {
605 Tk_FreeBitmap(display, arcPtr->disabledFillStipple);
606 }
607 if (arcPtr->fillGC != NULL) {
608 Tk_FreeGC(display, arcPtr->fillGC);
609 }
610 }
611
612 /*
613 *--------------------------------------------------------------
614 *
615 * ComputeArcBbox --
616 *
617 * This function is invoked to compute the bounding box of all the pixels
618 * that may be drawn as part of an arc.
619 *
620 * Results:
621 * None.
622 *
623 * Side effects:
624 * The fields x1, y1, x2, and y2 are updated in the header for itemPtr.
625 *
626 *--------------------------------------------------------------
627 */
628
629 static void
ComputeArcBbox(Tk_Canvas canvas,ArcItem * arcPtr)630 ComputeArcBbox(
631 Tk_Canvas canvas, /* Canvas that contains item. */
632 ArcItem *arcPtr) /* Item whose bbox is to be recomputed. */
633 {
634 double tmp, center[2], point[2];
635 double width;
636 Tk_State state = arcPtr->header.state;
637
638 if (state == TK_STATE_NULL) {
639 state = Canvas(canvas)->canvas_state;
640 }
641
642 width = arcPtr->outline.width;
643 if (width < 1.0) {
644 width = 1.0;
645 }
646 if (state==TK_STATE_HIDDEN) {
647 arcPtr->header.x1 = arcPtr->header.x2 =
648 arcPtr->header.y1 = arcPtr->header.y2 = -1;
649 return;
650 } else if (Canvas(canvas)->currentItemPtr == (Tk_Item *) arcPtr) {
651 if (arcPtr->outline.activeWidth>width) {
652 width = arcPtr->outline.activeWidth;
653 }
654 } else if (state==TK_STATE_DISABLED) {
655 if (arcPtr->outline.disabledWidth>0) {
656 width = arcPtr->outline.disabledWidth;
657 }
658 }
659
660 /*
661 * Make sure that the first coordinates are the lowest ones.
662 */
663
664 if (arcPtr->bbox[1] > arcPtr->bbox[3]) {
665 tmp = arcPtr->bbox[3];
666
667 arcPtr->bbox[3] = arcPtr->bbox[1];
668 arcPtr->bbox[1] = tmp;
669 }
670 if (arcPtr->bbox[0] > arcPtr->bbox[2]) {
671 tmp = arcPtr->bbox[2];
672
673 arcPtr->bbox[2] = arcPtr->bbox[0];
674 arcPtr->bbox[0] = tmp;
675 }
676
677 ComputeArcOutline(canvas,arcPtr);
678
679 /*
680 * To compute the bounding box, start with the bbox formed by the two
681 * endpoints of the arc. Then add in the center of the arc's oval (if
682 * relevant) and the 3-o'clock, 6-o'clock, 9-o'clock, and 12-o'clock
683 * positions, if they are relevant.
684 */
685
686 arcPtr->header.x1 = arcPtr->header.x2 = (int) arcPtr->center1[0];
687 arcPtr->header.y1 = arcPtr->header.y2 = (int) arcPtr->center1[1];
688 TkIncludePoint((Tk_Item *) arcPtr, arcPtr->center2);
689 center[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2;
690 center[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2;
691 if (arcPtr->style == PIESLICE_STYLE) {
692 TkIncludePoint((Tk_Item *) arcPtr, center);
693 }
694
695 tmp = -arcPtr->start;
696 if (tmp < 0) {
697 tmp += 360.0;
698 }
699 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
700 point[0] = arcPtr->bbox[2];
701 point[1] = center[1];
702 TkIncludePoint((Tk_Item *) arcPtr, point);
703 }
704 tmp = 90.0 - arcPtr->start;
705 if (tmp < 0) {
706 tmp += 360.0;
707 }
708 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
709 point[0] = center[0];
710 point[1] = arcPtr->bbox[1];
711 TkIncludePoint((Tk_Item *) arcPtr, point);
712 }
713 tmp = 180.0 - arcPtr->start;
714 if (tmp < 0) {
715 tmp += 360.0;
716 }
717 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
718 point[0] = arcPtr->bbox[0];
719 point[1] = center[1];
720 TkIncludePoint((Tk_Item *) arcPtr, point);
721 }
722 tmp = 270.0 - arcPtr->start;
723 if (tmp < 0) {
724 tmp += 360.0;
725 }
726 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
727 point[0] = center[0];
728 point[1] = arcPtr->bbox[3];
729 TkIncludePoint((Tk_Item *) arcPtr, point);
730 }
731
732 /*
733 * Lastly, expand by the width of the arc (if the arc's outline is being
734 * drawn) and add one extra pixel just for safety.
735 */
736
737 if (arcPtr->outline.gc == NULL) {
738 tmp = 1;
739 } else {
740 tmp = (int) ((width + 1.0)/2.0 + 1);
741 }
742 arcPtr->header.x1 -= (int) tmp;
743 arcPtr->header.y1 -= (int) tmp;
744 arcPtr->header.x2 += (int) tmp;
745 arcPtr->header.y2 += (int) tmp;
746 }
747
748 /*
749 *--------------------------------------------------------------
750 *
751 * DisplayArc --
752 *
753 * This function is invoked to draw an arc item in a given drawable.
754 *
755 * Results:
756 * None.
757 *
758 * Side effects:
759 * ItemPtr is drawn in drawable using the transformation information in
760 * canvas.
761 *
762 *--------------------------------------------------------------
763 */
764
765 static void
DisplayArc(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display,Drawable drawable,TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int),TCL_UNUSED (int))766 DisplayArc(
767 Tk_Canvas canvas, /* Canvas that contains item. */
768 Tk_Item *itemPtr, /* Item to be displayed. */
769 Display *display, /* Display on which to draw item. */
770 Drawable drawable, /* Pixmap or window in which to draw item. */
771 TCL_UNUSED(int), /* Describes region of canvas that must be */
772 TCL_UNUSED(int), /* redisplayed (not used). */
773 TCL_UNUSED(int),
774 TCL_UNUSED(int))
775 {
776 ArcItem *arcPtr = (ArcItem *) itemPtr;
777 short x1, y1, x2, y2;
778 int start, extent, dashnumber;
779 double lineWidth;
780 Tk_State state = itemPtr->state;
781 Pixmap stipple;
782
783 if (state == TK_STATE_NULL) {
784 state = Canvas(canvas)->canvas_state;
785 }
786 lineWidth = arcPtr->outline.width;
787 if (lineWidth < 1.0) {
788 lineWidth = 1.0;
789 }
790 dashnumber = arcPtr->outline.dash.number;
791 stipple = arcPtr->fillStipple;
792 if (Canvas(canvas)->currentItemPtr == itemPtr) {
793 if (arcPtr->outline.activeWidth>lineWidth) {
794 lineWidth = arcPtr->outline.activeWidth;
795 }
796 if (arcPtr->outline.activeDash.number != 0) {
797 dashnumber = arcPtr->outline.activeDash.number;
798 }
799 if (arcPtr->activeFillStipple != None) {
800 stipple = arcPtr->activeFillStipple;
801 }
802 } else if (state == TK_STATE_DISABLED) {
803 if (arcPtr->outline.disabledWidth > 0) {
804 lineWidth = arcPtr->outline.disabledWidth;
805 }
806 if (arcPtr->outline.disabledDash.number != 0) {
807 dashnumber = arcPtr->outline.disabledDash.number;
808 }
809 if (arcPtr->disabledFillStipple != None) {
810 stipple = arcPtr->disabledFillStipple;
811 }
812 }
813
814 /*
815 * Compute the screen coordinates of the bounding box for the item, plus
816 * integer values for the angles.
817 */
818
819 Tk_CanvasDrawableCoords(canvas, arcPtr->bbox[0], arcPtr->bbox[1],
820 &x1, &y1);
821 Tk_CanvasDrawableCoords(canvas, arcPtr->bbox[2], arcPtr->bbox[3],
822 &x2, &y2);
823 if (x2 <= x1) {
824 x2 = x1+1;
825 }
826 if (y2 <= y1) {
827 y2 = y1+1;
828 }
829 start = (int) ((64*arcPtr->start) + 0.5);
830 extent = (int) ((64*arcPtr->extent) + 0.5);
831
832 /*
833 * Display filled arc first (if wanted), then outline. If the extent is
834 * zero then don't invoke XFillArc or XDrawArc, since this causes some
835 * window servers to crash and should be a no-op anyway.
836 */
837
838 if ((arcPtr->fillGC != NULL) && (extent != 0)) {
839 if (stipple != None) {
840 int w = 0;
841 int h = 0;
842 Tk_TSOffset *tsoffset = &arcPtr->tsoffset;
843 int flags = tsoffset->flags;
844
845 if (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE)) {
846 Tk_SizeOfBitmap(display, stipple, &w, &h);
847 if (flags & TK_OFFSET_CENTER) {
848 w /= 2;
849 } else {
850 w = 0;
851 }
852 if (flags & TK_OFFSET_MIDDLE) {
853 h /= 2;
854 } else {
855 h = 0;
856 }
857 }
858 tsoffset->xoffset -= w;
859 tsoffset->yoffset -= h;
860 Tk_CanvasSetOffset(canvas, arcPtr->fillGC, tsoffset);
861 if (tsoffset) {
862 tsoffset->xoffset += w;
863 tsoffset->yoffset += h;
864 }
865 }
866 XFillArc(display, drawable, arcPtr->fillGC, x1, y1, (unsigned) (x2-x1),
867 (unsigned) (y2-y1), start, extent);
868 if (stipple != None) {
869 XSetTSOrigin(display, arcPtr->fillGC, 0, 0);
870 }
871 }
872 if (arcPtr->outline.gc != NULL) {
873 Tk_ChangeOutlineGC(canvas, itemPtr, &(arcPtr->outline));
874
875 if (extent != 0) {
876 XDrawArc(display, drawable, arcPtr->outline.gc, x1, y1,
877 (unsigned) (x2-x1), (unsigned) (y2-y1), start, extent);
878 }
879
880 /*
881 * If the outline width is very thin, don't use polygons to draw the
882 * linear parts of the outline (this often results in nothing being
883 * displayed); just draw lines instead. The same is done if the
884 * outline is dashed, because then polygons don't work.
885 */
886
887 if (lineWidth < 1.5 || dashnumber != 0) {
888 Tk_CanvasDrawableCoords(canvas, arcPtr->center1[0],
889 arcPtr->center1[1], &x1, &y1);
890 Tk_CanvasDrawableCoords(canvas, arcPtr->center2[0],
891 arcPtr->center2[1], &x2, &y2);
892
893 if (arcPtr->style == CHORD_STYLE) {
894 XDrawLine(display, drawable, arcPtr->outline.gc,
895 x1, y1, x2, y2);
896 } else if (arcPtr->style == PIESLICE_STYLE) {
897 short cx, cy;
898
899 Tk_CanvasDrawableCoords(canvas,
900 (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0,
901 (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0, &cx, &cy);
902 XDrawLine(display, drawable, arcPtr->outline.gc,
903 cx, cy, x1, y1);
904 XDrawLine(display, drawable, arcPtr->outline.gc,
905 cx, cy, x2, y2);
906 }
907 } else {
908 if (arcPtr->style == CHORD_STYLE) {
909 TkFillPolygon(canvas, arcPtr->outlinePtr, CHORD_OUTLINE_PTS,
910 display, drawable, arcPtr->outline.gc, NULL);
911 } else if (arcPtr->style == PIESLICE_STYLE) {
912 TkFillPolygon(canvas, arcPtr->outlinePtr, PIE_OUTLINE1_PTS,
913 display, drawable, arcPtr->outline.gc, NULL);
914 TkFillPolygon(canvas, arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
915 PIE_OUTLINE2_PTS, display, drawable,
916 arcPtr->outline.gc, NULL);
917 }
918 }
919
920 Tk_ResetOutlineGC(canvas, itemPtr, &(arcPtr->outline));
921 }
922 }
923
924 /*
925 *--------------------------------------------------------------
926 *
927 * ArcToPoint --
928 *
929 * Computes the distance from a given point to a given arc, in canvas
930 * units.
931 *
932 * Results:
933 * The return value is 0 if the point whose x and y coordinates are
934 * coordPtr[0] and coordPtr[1] is inside the arc. If the point isn't
935 * inside the arc then the return value is the distance from the point to
936 * the arc. If itemPtr is filled, then anywhere in the interior is
937 * considered "inside"; if itemPtr isn't filled, then "inside" means only
938 * the area occupied by the outline.
939 *
940 * Side effects:
941 * None.
942 *
943 *--------------------------------------------------------------
944 */
945
946 static double
ArcToPoint(Tk_Canvas canvas,Tk_Item * itemPtr,double * pointPtr)947 ArcToPoint(
948 Tk_Canvas canvas, /* Canvas containing item. */
949 Tk_Item *itemPtr, /* Item to check against point. */
950 double *pointPtr) /* Pointer to x and y coordinates. */
951 {
952 ArcItem *arcPtr = (ArcItem *) itemPtr;
953 double vertex[2], pointAngle, diff, dist, newDist;
954 double poly[8], polyDist, width, t1, t2;
955 int filled, angleInRange;
956 Tk_State state = itemPtr->state;
957
958 if (state == TK_STATE_NULL) {
959 state = Canvas(canvas)->canvas_state;
960 }
961
962 width = (double) arcPtr->outline.width;
963 if (Canvas(canvas)->currentItemPtr == itemPtr) {
964 if (arcPtr->outline.activeWidth>width) {
965 width = (double) arcPtr->outline.activeWidth;
966 }
967 } else if (state == TK_STATE_DISABLED) {
968 if (arcPtr->outline.disabledWidth>0) {
969 width = (double) arcPtr->outline.disabledWidth;
970 }
971 }
972
973 /*
974 * See if the point is within the angular range of the arc. Remember, X
975 * angles are backwards from the way we'd normally think of them. Also,
976 * compensate for any eccentricity of the oval.
977 */
978
979 vertex[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
980 vertex[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
981 t1 = arcPtr->bbox[3] - arcPtr->bbox[1];
982 if (t1 != 0.0) {
983 t1 = (pointPtr[1] - vertex[1]) / t1;
984 }
985 t2 = arcPtr->bbox[2] - arcPtr->bbox[0];
986 if (t2 != 0.0) {
987 t2 = (pointPtr[0] - vertex[0]) / t2;
988 }
989 if ((t1 == 0.0) && (t2 == 0.0)) {
990 pointAngle = 0;
991 } else {
992 pointAngle = -atan2(t1, t2)*180/PI;
993 }
994 diff = pointAngle - arcPtr->start;
995 diff -= ((int) (diff/360.0) * 360.0);
996 if (diff < 0) {
997 diff += 360.0;
998 }
999 angleInRange = (diff <= arcPtr->extent) ||
1000 ((arcPtr->extent < 0) && ((diff - 360.0) >= arcPtr->extent));
1001
1002 /*
1003 * Now perform different tests depending on what kind of arc we're dealing
1004 * with.
1005 */
1006
1007 if (arcPtr->style == ARC_STYLE) {
1008 if (angleInRange) {
1009 return TkOvalToPoint(arcPtr->bbox, width, 0, pointPtr);
1010 }
1011 dist = hypot(pointPtr[0] - arcPtr->center1[0],
1012 pointPtr[1] - arcPtr->center1[1]);
1013 newDist = hypot(pointPtr[0] - arcPtr->center2[0],
1014 pointPtr[1] - arcPtr->center2[1]);
1015 if (newDist < dist) {
1016 return newDist;
1017 }
1018 return dist;
1019 }
1020
1021 if ((arcPtr->fillGC != NULL) || (arcPtr->outline.gc == NULL)) {
1022 filled = 1;
1023 } else {
1024 filled = 0;
1025 }
1026 if (arcPtr->outline.gc == NULL) {
1027 width = 0.0;
1028 }
1029
1030 if (arcPtr->style == PIESLICE_STYLE) {
1031 if (width > 1.0) {
1032 dist = TkPolygonToPoint(arcPtr->outlinePtr, PIE_OUTLINE1_PTS,
1033 pointPtr);
1034 newDist = TkPolygonToPoint(arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
1035 PIE_OUTLINE2_PTS, pointPtr);
1036 } else {
1037 dist = TkLineToPoint(vertex, arcPtr->center1, pointPtr);
1038 newDist = TkLineToPoint(vertex, arcPtr->center2, pointPtr);
1039 }
1040 if (newDist < dist) {
1041 dist = newDist;
1042 }
1043 if (angleInRange) {
1044 newDist = TkOvalToPoint(arcPtr->bbox, width, filled, pointPtr);
1045 if (newDist < dist) {
1046 dist = newDist;
1047 }
1048 }
1049 return dist;
1050 }
1051
1052 /*
1053 * This is a chord-style arc. We have to deal specially with the
1054 * triangular piece that represents the difference between a chord-style
1055 * arc and a pie-slice arc (for small angles this piece is excluded here
1056 * where it would be included for pie slices; for large angles the piece
1057 * is included here but would be excluded for pie slices).
1058 */
1059
1060 if (width > 1.0) {
1061 dist = TkPolygonToPoint(arcPtr->outlinePtr, CHORD_OUTLINE_PTS,
1062 pointPtr);
1063 } else {
1064 dist = TkLineToPoint(arcPtr->center1, arcPtr->center2, pointPtr);
1065 }
1066 poly[0] = poly[6] = vertex[0];
1067 poly[1] = poly[7] = vertex[1];
1068 poly[2] = arcPtr->center1[0];
1069 poly[3] = arcPtr->center1[1];
1070 poly[4] = arcPtr->center2[0];
1071 poly[5] = arcPtr->center2[1];
1072 polyDist = TkPolygonToPoint(poly, 4, pointPtr);
1073 if (angleInRange) {
1074 if ((arcPtr->extent < -180.0) || (arcPtr->extent > 180.0)
1075 || (polyDist > 0.0)) {
1076 newDist = TkOvalToPoint(arcPtr->bbox, width, filled, pointPtr);
1077 if (newDist < dist) {
1078 dist = newDist;
1079 }
1080 }
1081 } else {
1082 if ((arcPtr->extent < -180.0) || (arcPtr->extent > 180.0)) {
1083 if (filled && (polyDist < dist)) {
1084 dist = polyDist;
1085 }
1086 }
1087 }
1088 return dist;
1089 }
1090
1091 /*
1092 *--------------------------------------------------------------
1093 *
1094 * ArcToArea --
1095 *
1096 * This function is called to determine whether an item lies entirely
1097 * inside, entirely outside, or overlapping a given area.
1098 *
1099 * Results:
1100 * -1 is returned if the item is entirely outside the area given by
1101 * rectPtr, 0 if it overlaps, and 1 if it is entirely inside the given
1102 * area.
1103 *
1104 * Side effects:
1105 * None.
1106 *
1107 *--------------------------------------------------------------
1108 */
1109
1110 static int
ArcToArea(Tk_Canvas canvas,Tk_Item * itemPtr,double * rectPtr)1111 ArcToArea(
1112 Tk_Canvas canvas, /* Canvas containing item. */
1113 Tk_Item *itemPtr, /* Item to check against arc. */
1114 double *rectPtr) /* Pointer to array of four coordinates (x1,
1115 * y1, x2, y2) describing rectangular area. */
1116 {
1117 ArcItem *arcPtr = (ArcItem *) itemPtr;
1118 double rx, ry; /* Radii for transformed oval: these define an
1119 * oval centered at the origin. */
1120 double tRect[4]; /* Transformed version of x1, y1, x2, y2, for
1121 * coord. system where arc is centered on the
1122 * origin. */
1123 double center[2], width, angle, tmp;
1124 double points[20], *pointPtr;
1125 int numPoints, filled;
1126 int inside; /* Non-zero means every test so far suggests
1127 * that arc is inside rectangle. 0 means every
1128 * test so far shows arc to be outside of
1129 * rectangle. */
1130 int newInside;
1131 Tk_State state = itemPtr->state;
1132
1133 if(state == TK_STATE_NULL) {
1134 state = Canvas(canvas)->canvas_state;
1135 }
1136 width = (double) arcPtr->outline.width;
1137 if (Canvas(canvas)->currentItemPtr == itemPtr) {
1138 if (arcPtr->outline.activeWidth>width) {
1139 width = (double) arcPtr->outline.activeWidth;
1140 }
1141 } else if (state == TK_STATE_DISABLED) {
1142 if (arcPtr->outline.disabledWidth>0) {
1143 width = (double) arcPtr->outline.disabledWidth;
1144 }
1145 }
1146
1147 if ((arcPtr->fillGC != NULL) || (arcPtr->outline.gc == NULL)) {
1148 filled = 1;
1149 } else {
1150 filled = 0;
1151 }
1152 if (arcPtr->outline.gc == NULL) {
1153 width = 0.0;
1154 }
1155
1156 /*
1157 * Transform both the arc and the rectangle so that the arc's oval is
1158 * centered on the origin.
1159 */
1160
1161 center[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
1162 center[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
1163 tRect[0] = rectPtr[0] - center[0];
1164 tRect[1] = rectPtr[1] - center[1];
1165 tRect[2] = rectPtr[2] - center[0];
1166 tRect[3] = rectPtr[3] - center[1];
1167 rx = arcPtr->bbox[2] - center[0] + width/2.0;
1168 ry = arcPtr->bbox[3] - center[1] + width/2.0;
1169
1170 /*
1171 * Find the extreme points of the arc and see whether these are all inside
1172 * the rectangle (in which case we're done), partly in and partly out (in
1173 * which case we're done), or all outside (in which case we have more work
1174 * to do). The extreme points include the following, which are checked in
1175 * order:
1176 *
1177 * 1. The outside points of the arc, corresponding to start and extent.
1178 * 2. The center of the arc (but only in pie-slice mode).
1179 * 3. The 12, 3, 6, and 9-o'clock positions (but only if the arc includes
1180 * those angles).
1181 */
1182
1183 pointPtr = points;
1184 angle = -arcPtr->start*(PI/180.0);
1185 pointPtr[0] = rx*cos(angle);
1186 pointPtr[1] = ry*sin(angle);
1187 angle += -arcPtr->extent*(PI/180.0);
1188 pointPtr[2] = rx*cos(angle);
1189 pointPtr[3] = ry*sin(angle);
1190 numPoints = 2;
1191 pointPtr += 4;
1192
1193 if ((arcPtr->style == PIESLICE_STYLE) && (arcPtr->extent < 180.0)) {
1194 pointPtr[0] = 0.0;
1195 pointPtr[1] = 0.0;
1196 numPoints++;
1197 pointPtr += 2;
1198 }
1199
1200 tmp = -arcPtr->start;
1201 if (tmp < 0) {
1202 tmp += 360.0;
1203 }
1204 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
1205 pointPtr[0] = rx;
1206 pointPtr[1] = 0.0;
1207 numPoints++;
1208 pointPtr += 2;
1209 }
1210 tmp = 90.0 - arcPtr->start;
1211 if (tmp < 0) {
1212 tmp += 360.0;
1213 }
1214 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
1215 pointPtr[0] = 0.0;
1216 pointPtr[1] = -ry;
1217 numPoints++;
1218 pointPtr += 2;
1219 }
1220 tmp = 180.0 - arcPtr->start;
1221 if (tmp < 0) {
1222 tmp += 360.0;
1223 }
1224 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
1225 pointPtr[0] = -rx;
1226 pointPtr[1] = 0.0;
1227 numPoints++;
1228 pointPtr += 2;
1229 }
1230 tmp = 270.0 - arcPtr->start;
1231 if (tmp < 0) {
1232 tmp += 360.0;
1233 }
1234 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
1235 pointPtr[0] = 0.0;
1236 pointPtr[1] = ry;
1237 numPoints++;
1238 }
1239
1240 /*
1241 * Now that we've located the extreme points, loop through them all to see
1242 * which are inside the rectangle.
1243 */
1244
1245 inside = (points[0] > tRect[0]) && (points[0] < tRect[2])
1246 && (points[1] > tRect[1]) && (points[1] < tRect[3]);
1247 for (pointPtr = points+2; numPoints > 1; pointPtr += 2, numPoints--) {
1248 newInside = (pointPtr[0] > tRect[0]) && (pointPtr[0] < tRect[2])
1249 && (pointPtr[1] > tRect[1]) && (pointPtr[1] < tRect[3]);
1250 if (newInside != inside) {
1251 return 0;
1252 }
1253 }
1254
1255 if (inside) {
1256 return 1;
1257 }
1258
1259 /*
1260 * So far, oval appears to be outside rectangle, but can't yet tell for
1261 * sure. Next, test each of the four sides of the rectangle against the
1262 * bounding region for the arc. If any intersections are found, then
1263 * return "overlapping". First, test against the polygon(s) forming the
1264 * sides of a chord or pie-slice.
1265 */
1266
1267 if (arcPtr->style == PIESLICE_STYLE) {
1268 if (width >= 1.0) {
1269 if (TkPolygonToArea(arcPtr->outlinePtr, PIE_OUTLINE1_PTS,
1270 rectPtr) != -1) {
1271 return 0;
1272 }
1273 if (TkPolygonToArea(arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
1274 PIE_OUTLINE2_PTS, rectPtr) != -1) {
1275 return 0;
1276 }
1277 } else {
1278 if ((TkLineToArea(center, arcPtr->center1, rectPtr) != -1) ||
1279 (TkLineToArea(center, arcPtr->center2, rectPtr) != -1)) {
1280 return 0;
1281 }
1282 }
1283 } else if (arcPtr->style == CHORD_STYLE) {
1284 if (width >= 1.0) {
1285 if (TkPolygonToArea(arcPtr->outlinePtr, CHORD_OUTLINE_PTS,
1286 rectPtr) != -1) {
1287 return 0;
1288 }
1289 } else {
1290 if (TkLineToArea(arcPtr->center1, arcPtr->center2,
1291 rectPtr) != -1) {
1292 return 0;
1293 }
1294 }
1295 }
1296
1297 /*
1298 * Next check for overlap between each of the four sides and the outer
1299 * perimiter of the arc. If the arc isn't filled, then also check the
1300 * inner perimeter of the arc.
1301 */
1302
1303 if (HorizLineToArc(tRect[0], tRect[2], tRect[1], rx, ry, arcPtr->start,
1304 arcPtr->extent)
1305 || HorizLineToArc(tRect[0], tRect[2], tRect[3], rx, ry,
1306 arcPtr->start, arcPtr->extent)
1307 || VertLineToArc(tRect[0], tRect[1], tRect[3], rx, ry,
1308 arcPtr->start, arcPtr->extent)
1309 || VertLineToArc(tRect[2], tRect[1], tRect[3], rx, ry,
1310 arcPtr->start, arcPtr->extent)) {
1311 return 0;
1312 }
1313 if ((width > 1.0) && !filled) {
1314 rx -= width;
1315 ry -= width;
1316 if (HorizLineToArc(tRect[0], tRect[2], tRect[1], rx, ry, arcPtr->start,
1317 arcPtr->extent)
1318 || HorizLineToArc(tRect[0], tRect[2], tRect[3], rx, ry,
1319 arcPtr->start, arcPtr->extent)
1320 || VertLineToArc(tRect[0], tRect[1], tRect[3], rx, ry,
1321 arcPtr->start, arcPtr->extent)
1322 || VertLineToArc(tRect[2], tRect[1], tRect[3], rx, ry,
1323 arcPtr->start, arcPtr->extent)) {
1324 return 0;
1325 }
1326 }
1327
1328 /*
1329 * The arc still appears to be totally disjoint from the rectangle, but
1330 * it's also possible that the rectangle is totally inside the arc. Do one
1331 * last check, which is to check one point of the rectangle to see if it's
1332 * inside the arc. If it is, we've got overlap. If it isn't, the arc's
1333 * really outside the rectangle.
1334 */
1335
1336 if (ArcToPoint(canvas, itemPtr, rectPtr) == 0.0) {
1337 return 0;
1338 }
1339 return -1;
1340 }
1341
1342 /*
1343 *--------------------------------------------------------------
1344 *
1345 * ScaleArc --
1346 *
1347 * This function is invoked to rescale an arc item.
1348 *
1349 * Results:
1350 * None.
1351 *
1352 * Side effects:
1353 * The arc referred to by itemPtr is rescaled so that the following
1354 * transformation is applied to all point coordinates:
1355 * x' = originX + scaleX*(x-originX)
1356 * y' = originY + scaleY*(y-originY)
1357 *
1358 *--------------------------------------------------------------
1359 */
1360
1361 static void
ScaleArc(Tk_Canvas canvas,Tk_Item * itemPtr,double originX,double originY,double scaleX,double scaleY)1362 ScaleArc(
1363 Tk_Canvas canvas, /* Canvas containing arc. */
1364 Tk_Item *itemPtr, /* Arc to be scaled. */
1365 double originX, /* Origin about which to scale rect. */
1366 double originY,
1367 double scaleX, /* Amount to scale in X direction. */
1368 double scaleY) /* Amount to scale in Y direction. */
1369 {
1370 ArcItem *arcPtr = (ArcItem *) itemPtr;
1371
1372 arcPtr->bbox[0] = originX + scaleX*(arcPtr->bbox[0] - originX);
1373 arcPtr->bbox[1] = originY + scaleY*(arcPtr->bbox[1] - originY);
1374 arcPtr->bbox[2] = originX + scaleX*(arcPtr->bbox[2] - originX);
1375 arcPtr->bbox[3] = originY + scaleY*(arcPtr->bbox[3] - originY);
1376 ComputeArcBbox(canvas, arcPtr);
1377 }
1378
1379 /*
1380 *--------------------------------------------------------------
1381 *
1382 * TranslateArc --
1383 *
1384 * This function is called to move an arc by a given amount.
1385 *
1386 * Results:
1387 * None.
1388 *
1389 * Side effects:
1390 * The position of the arc is offset by (xDelta, yDelta), and the
1391 * bounding box is updated in the generic part of the item structure.
1392 *
1393 *--------------------------------------------------------------
1394 */
1395
1396 static void
TranslateArc(Tk_Canvas canvas,Tk_Item * itemPtr,double deltaX,double deltaY)1397 TranslateArc(
1398 Tk_Canvas canvas, /* Canvas containing item. */
1399 Tk_Item *itemPtr, /* Item that is being moved. */
1400 double deltaX, /* Amount by which item is to be moved. */
1401 double deltaY)
1402 {
1403 ArcItem *arcPtr = (ArcItem *) itemPtr;
1404
1405 arcPtr->bbox[0] += deltaX;
1406 arcPtr->bbox[1] += deltaY;
1407 arcPtr->bbox[2] += deltaX;
1408 arcPtr->bbox[3] += deltaY;
1409 ComputeArcBbox(canvas, arcPtr);
1410 }
1411
1412 /*
1413 *--------------------------------------------------------------
1414 *
1415 * ComputeArcOutline --
1416 *
1417 * This function creates a polygon describing everything in the outline
1418 * for an arc except what's in the curved part. For a "pie slice" arc
1419 * this is a V-shaped chunk, and for a "chord" arc this is a linear chunk
1420 * (with cutaway corners). For "arc" arcs, this stuff isn't relevant.
1421 *
1422 * Results:
1423 * None.
1424 *
1425 * Side effects:
1426 * The information at arcPtr->outlinePtr gets modified, and storage for
1427 * arcPtr->outlinePtr may be allocated or freed.
1428 *
1429 *--------------------------------------------------------------
1430 */
1431
1432 static void
ComputeArcOutline(Tk_Canvas canvas,ArcItem * arcPtr)1433 ComputeArcOutline(
1434 Tk_Canvas canvas, /* Information about overall canvas. */
1435 ArcItem *arcPtr) /* Information about arc. */
1436 {
1437 double sin1, cos1, sin2, cos2, angle, width, halfWidth;
1438 double boxWidth, boxHeight;
1439 double vertex[2], corner1[2], corner2[2];
1440 double *outlinePtr;
1441 Tk_State state = arcPtr->header.state;
1442
1443 /*
1444 * Make sure that the outlinePtr array is large enough to hold either a
1445 * chord or pie-slice outline.
1446 */
1447
1448 if (arcPtr->numOutlinePoints == 0) {
1449 arcPtr->outlinePtr = (double *)ckalloc(26 * sizeof(double));
1450 arcPtr->numOutlinePoints = 22;
1451 }
1452 outlinePtr = arcPtr->outlinePtr;
1453
1454 if (state == TK_STATE_NULL) {
1455 state = Canvas(canvas)->canvas_state;
1456 }
1457
1458 /*
1459 * First compute the two points that lie at the centers of the ends of the
1460 * curved arc segment, which are marked with X's in the figure below:
1461 *
1462 *
1463 * * * *
1464 * * *
1465 * * * * *
1466 * * * * *
1467 * * * * *
1468 * X * * X
1469 *
1470 * The code is tricky because the arc can be ovular in shape. It computes
1471 * the position for a unit circle, and then scales to fit the shape of the
1472 * arc's bounding box.
1473 *
1474 * Also, watch out because angles go counter-clockwise like you might
1475 * expect, but the y-coordinate system is inverted. To handle this, just
1476 * negate the angles in all the computations.
1477 */
1478
1479 boxWidth = arcPtr->bbox[2] - arcPtr->bbox[0];
1480 boxHeight = arcPtr->bbox[3] - arcPtr->bbox[1];
1481 angle = -arcPtr->start*PI/180.0;
1482 sin1 = sin(angle);
1483 cos1 = cos(angle);
1484 angle -= arcPtr->extent*PI/180.0;
1485 sin2 = sin(angle);
1486 cos2 = cos(angle);
1487 vertex[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
1488 vertex[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
1489 arcPtr->center1[0] = vertex[0] + cos1*boxWidth/2.0;
1490 arcPtr->center1[1] = vertex[1] + sin1*boxHeight/2.0;
1491 arcPtr->center2[0] = vertex[0] + cos2*boxWidth/2.0;
1492 arcPtr->center2[1] = vertex[1] + sin2*boxHeight/2.0;
1493
1494 /*
1495 * Next compute the "outermost corners" of the arc, which are marked with
1496 * X's in the figure below:
1497 *
1498 * * * *
1499 * * *
1500 * * * * *
1501 * * * * *
1502 * X * * X
1503 * * *
1504 *
1505 * The code below is tricky because it has to handle eccentricity in the
1506 * shape of the oval. The key in the code below is to realize that the
1507 * slope of the line from arcPtr->center1 to corner1 is (boxWidth*sin1)
1508 * divided by (boxHeight*cos1), and similarly for arcPtr->center2 and
1509 * corner2. These formulas can be computed from the formula for the oval.
1510 */
1511
1512 width = arcPtr->outline.width;
1513 if (Canvas(canvas)->currentItemPtr == (Tk_Item *) arcPtr) {
1514 if (arcPtr->outline.activeWidth>arcPtr->outline.width) {
1515 width = arcPtr->outline.activeWidth;
1516 }
1517 } else if (state == TK_STATE_DISABLED) {
1518 if (arcPtr->outline.disabledWidth>arcPtr->outline.width) {
1519 width = arcPtr->outline.disabledWidth;
1520 }
1521 }
1522 halfWidth = width/2.0;
1523
1524 if (((boxWidth*sin1) == 0.0) && ((boxHeight*cos1) == 0.0)) {
1525 angle = 0.0;
1526 } else {
1527 angle = atan2(boxWidth*sin1, boxHeight*cos1);
1528 }
1529 corner1[0] = arcPtr->center1[0] + cos(angle)*halfWidth;
1530 corner1[1] = arcPtr->center1[1] + sin(angle)*halfWidth;
1531 if (((boxWidth*sin2) == 0.0) && ((boxHeight*cos2) == 0.0)) {
1532 angle = 0.0;
1533 } else {
1534 angle = atan2(boxWidth*sin2, boxHeight*cos2);
1535 }
1536 corner2[0] = arcPtr->center2[0] + cos(angle)*halfWidth;
1537 corner2[1] = arcPtr->center2[1] + sin(angle)*halfWidth;
1538
1539 /*
1540 * For a chord outline, generate a six-sided polygon with three points for
1541 * each end of the chord. The first and third points for each end are butt
1542 * points generated on either side of the center point. The second point
1543 * is the corner point.
1544 */
1545
1546 if (arcPtr->style == CHORD_STYLE) {
1547 outlinePtr[0] = outlinePtr[12] = corner1[0];
1548 outlinePtr[1] = outlinePtr[13] = corner1[1];
1549 TkGetButtPoints(arcPtr->center2, arcPtr->center1,
1550 width, 0, outlinePtr+10, outlinePtr+2);
1551 outlinePtr[4] = arcPtr->center2[0] + outlinePtr[2]
1552 - arcPtr->center1[0];
1553 outlinePtr[5] = arcPtr->center2[1] + outlinePtr[3]
1554 - arcPtr->center1[1];
1555 outlinePtr[6] = corner2[0];
1556 outlinePtr[7] = corner2[1];
1557 outlinePtr[8] = arcPtr->center2[0] + outlinePtr[10]
1558 - arcPtr->center1[0];
1559 outlinePtr[9] = arcPtr->center2[1] + outlinePtr[11]
1560 - arcPtr->center1[1];
1561 } else if (arcPtr->style == PIESLICE_STYLE) {
1562 /*
1563 * For pie slices, generate two polygons, one for each side of the pie
1564 * slice. The first arm has a shape like this, where the center of the
1565 * oval is X, arcPtr->center1 is at Y, and corner1 is at Z:
1566 *
1567 * _____________________
1568 * | \
1569 * | \
1570 * X Y Z
1571 * | /
1572 * |_____________________/
1573 */
1574
1575 TkGetButtPoints(arcPtr->center1, vertex, width, 0,
1576 outlinePtr, outlinePtr+2);
1577 outlinePtr[4] = arcPtr->center1[0] + outlinePtr[2] - vertex[0];
1578 outlinePtr[5] = arcPtr->center1[1] + outlinePtr[3] - vertex[1];
1579 outlinePtr[6] = corner1[0];
1580 outlinePtr[7] = corner1[1];
1581 outlinePtr[8] = arcPtr->center1[0] + outlinePtr[0] - vertex[0];
1582 outlinePtr[9] = arcPtr->center1[1] + outlinePtr[1] - vertex[1];
1583 outlinePtr[10] = outlinePtr[0];
1584 outlinePtr[11] = outlinePtr[1];
1585
1586 /*
1587 * The second arm has a shape like this:
1588 *
1589 * ______________________
1590 * / \
1591 * / \
1592 * Z Y X /
1593 * \ /
1594 * \______________________/
1595 *
1596 * Similar to above X is the center of the oval/circle, Y is
1597 * arcPtr->center2, and Z is corner2. The extra jog out to the left of
1598 * X is needed in or to produce a butted joint with the first arm; the
1599 * corner to the right of X is one of the first two points of the
1600 * first arm, depending on extent.
1601 */
1602
1603 TkGetButtPoints(arcPtr->center2, vertex, width, 0,
1604 outlinePtr+12, outlinePtr+16);
1605 if ((arcPtr->extent > 180) ||
1606 ((arcPtr->extent < 0) && (arcPtr->extent > -180))) {
1607 outlinePtr[14] = outlinePtr[0];
1608 outlinePtr[15] = outlinePtr[1];
1609 } else {
1610 outlinePtr[14] = outlinePtr[2];
1611 outlinePtr[15] = outlinePtr[3];
1612 }
1613 outlinePtr[18] = arcPtr->center2[0] + outlinePtr[16] - vertex[0];
1614 outlinePtr[19] = arcPtr->center2[1] + outlinePtr[17] - vertex[1];
1615 outlinePtr[20] = corner2[0];
1616 outlinePtr[21] = corner2[1];
1617 outlinePtr[22] = arcPtr->center2[0] + outlinePtr[12] - vertex[0];
1618 outlinePtr[23] = arcPtr->center2[1] + outlinePtr[13] - vertex[1];
1619 outlinePtr[24] = outlinePtr[12];
1620 outlinePtr[25] = outlinePtr[13];
1621 }
1622 }
1623
1624 /*
1625 *--------------------------------------------------------------
1626 *
1627 * HorizLineToArc --
1628 *
1629 * Determines whether a horizontal line segment intersects a given arc.
1630 *
1631 * Results:
1632 * The return value is 1 if the given line intersects the infinitely-thin
1633 * arc section defined by rx, ry, start, and extent, and 0 otherwise.
1634 * Only the perimeter of the arc is checked: interior areas (e.g. chord
1635 * or pie-slice) are not checked.
1636 *
1637 * Side effects:
1638 * None.
1639 *
1640 *--------------------------------------------------------------
1641 */
1642
1643 static int
HorizLineToArc(double x1,double x2,double y,double rx,double ry,double start,double extent)1644 HorizLineToArc(
1645 double x1, double x2, /* X-coords of endpoints of line segment. X1
1646 * must be <= x2. */
1647 double y, /* Y-coordinate of line segment. */
1648 double rx, double ry, /* These x- and y-radii define an oval
1649 * centered at the origin. */
1650 double start, double extent)/* Angles that define extent of arc, in the
1651 * standard fashion for this module. */
1652 {
1653 double tmp, x;
1654 double tx, ty; /* Coordinates of intersection point in
1655 * transformed coordinate system. */
1656
1657 /*
1658 * Compute the x-coordinate of one possible intersection point between the
1659 * arc and the line. Use a transformed coordinate system where the oval is
1660 * a unit circle centered at the origin. Then scale back to get actual
1661 * x-coordinate.
1662 */
1663
1664 ty = y/ry;
1665 tmp = 1 - ty*ty;
1666 if (tmp < 0) {
1667 return 0;
1668 }
1669 tx = sqrt(tmp);
1670 x = tx*rx;
1671
1672 /*
1673 * Test both intersection points.
1674 */
1675
1676 if ((x >= x1) && (x <= x2) && AngleInRange(tx, ty, start, extent)) {
1677 return 1;
1678 }
1679 if ((-x >= x1) && (-x <= x2) && AngleInRange(-tx, ty, start, extent)) {
1680 return 1;
1681 }
1682 return 0;
1683 }
1684
1685 /*
1686 *--------------------------------------------------------------
1687 *
1688 * VertLineToArc --
1689 *
1690 * Determines whether a vertical line segment intersects a given arc.
1691 *
1692 * Results:
1693 * The return value is 1 if the given line intersects the infinitely-thin
1694 * arc section defined by rx, ry, start, and extent, and 0 otherwise.
1695 * Only the perimeter of the arc is checked: interior areas (e.g. chord
1696 * or pie-slice) are not checked.
1697 *
1698 * Side effects:
1699 * None.
1700 *
1701 *--------------------------------------------------------------
1702 */
1703
1704 static int
VertLineToArc(double x,double y1,double y2,double rx,double ry,double start,double extent)1705 VertLineToArc(
1706 double x, /* X-coordinate of line segment. */
1707 double y1, double y2, /* Y-coords of endpoints of line segment. Y1
1708 * must be <= y2. */
1709 double rx, double ry, /* These x- and y-radii define an oval
1710 * centered at the origin. */
1711 double start, double extent)/* Angles that define extent of arc, in the
1712 * standard fashion for this module. */
1713 {
1714 double tmp, y;
1715 double tx, ty; /* Coordinates of intersection point in
1716 * transformed coordinate system. */
1717
1718 /*
1719 * Compute the y-coordinate of one possible intersection point between the
1720 * arc and the line. Use a transformed coordinate system where the oval is
1721 * a unit circle centered at the origin. Then scale back to get actual
1722 * y-coordinate.
1723 */
1724
1725 tx = x/rx;
1726 tmp = 1 - tx*tx;
1727 if (tmp < 0) {
1728 return 0;
1729 }
1730 ty = sqrt(tmp);
1731 y = ty*ry;
1732
1733 /*
1734 * Test both intersection points.
1735 */
1736
1737 if ((y > y1) && (y < y2) && AngleInRange(tx, ty, start, extent)) {
1738 return 1;
1739 }
1740 if ((-y > y1) && (-y < y2) && AngleInRange(tx, -ty, start, extent)) {
1741 return 1;
1742 }
1743 return 0;
1744 }
1745
1746 /*
1747 *--------------------------------------------------------------
1748 *
1749 * AngleInRange --
1750 *
1751 * Determine whether the angle from the origin to a given point is within
1752 * a given range.
1753 *
1754 * Results:
1755 * The return value is 1 if the angle from (0,0) to (x,y) is in the range
1756 * given by start and extent, where angles are interpreted in the
1757 * standard way for ovals (meaning backwards from normal interpretation).
1758 * Otherwise the return value is 0.
1759 *
1760 * Side effects:
1761 * None.
1762 *
1763 *--------------------------------------------------------------
1764 */
1765
1766 static int
AngleInRange(double x,double y,double start,double extent)1767 AngleInRange(
1768 double x, double y, /* Coordinate of point; angle measured from
1769 * origin to here, relative to x-axis. */
1770 double start, /* First angle, degrees, >=0, <=360. */
1771 double extent) /* Size of arc in degrees >=-360, <=360. */
1772 {
1773 double diff;
1774
1775 if ((x == 0.0) && (y == 0.0)) {
1776 return 1;
1777 }
1778 diff = -atan2(y, x);
1779 diff = diff*(180.0/PI) - start;
1780 while (diff > 360.0) {
1781 diff -= 360.0;
1782 }
1783 while (diff < 0.0) {
1784 diff += 360.0;
1785 }
1786 if (extent >= 0) {
1787 return diff <= extent;
1788 }
1789 return (diff-360.0) >= extent;
1790 }
1791
1792 /*
1793 *--------------------------------------------------------------
1794 *
1795 * ArcToPostscript --
1796 *
1797 * This function is called to generate Postscript for arc items.
1798 *
1799 * Results:
1800 * The return value is a standard Tcl result. If an error occurs in
1801 * generating Postscript then an error message is left in the interp's
1802 * result, replacing whatever used to be there. If no error occurs, then
1803 * Postscript for the item is appended to the result.
1804 *
1805 * Side effects:
1806 * None.
1807 *
1808 *--------------------------------------------------------------
1809 */
1810
1811 static int
ArcToPostscript(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,TCL_UNUSED (int))1812 ArcToPostscript(
1813 Tcl_Interp *interp, /* Leave Postscript or error message here. */
1814 Tk_Canvas canvas, /* Information about overall canvas. */
1815 Tk_Item *itemPtr, /* Item for which Postscript is wanted. */
1816 TCL_UNUSED(int)) /* 1 means this is a prepass to collect font
1817 * information; 0 means final Postscript is
1818 * being created. */
1819 {
1820 ArcItem *arcPtr = (ArcItem *) itemPtr;
1821 double y1, y2, ang1, ang2;
1822 XColor *color;
1823 Pixmap stipple;
1824 XColor *fillColor;
1825 Pixmap fillStipple;
1826 Tk_State state = itemPtr->state;
1827 Tcl_Obj *psObj;
1828 Tcl_InterpState interpState;
1829
1830 y1 = Tk_CanvasPsY(canvas, arcPtr->bbox[1]);
1831 y2 = Tk_CanvasPsY(canvas, arcPtr->bbox[3]);
1832 ang1 = arcPtr->start;
1833 ang2 = ang1 + arcPtr->extent;
1834 if (ang2 < ang1) {
1835 ang1 = ang2;
1836 ang2 = arcPtr->start;
1837 }
1838
1839 if (state == TK_STATE_NULL) {
1840 state = Canvas(canvas)->canvas_state;
1841 }
1842 color = arcPtr->outline.color;
1843 stipple = arcPtr->outline.stipple;
1844 fillColor = arcPtr->fillColor;
1845 fillStipple = arcPtr->fillStipple;
1846 if (Canvas(canvas)->currentItemPtr == itemPtr) {
1847 if (arcPtr->outline.activeColor!=NULL) {
1848 color = arcPtr->outline.activeColor;
1849 }
1850 if (arcPtr->outline.activeStipple!=None) {
1851 stipple = arcPtr->outline.activeStipple;
1852 }
1853 if (arcPtr->activeFillColor!=NULL) {
1854 fillColor = arcPtr->activeFillColor;
1855 }
1856 if (arcPtr->activeFillStipple!=None) {
1857 fillStipple = arcPtr->activeFillStipple;
1858 }
1859 } else if (state == TK_STATE_DISABLED) {
1860 if (arcPtr->outline.disabledColor!=NULL) {
1861 color = arcPtr->outline.disabledColor;
1862 }
1863 if (arcPtr->outline.disabledStipple!=None) {
1864 stipple = arcPtr->outline.disabledStipple;
1865 }
1866 if (arcPtr->disabledFillColor!=NULL) {
1867 fillColor = arcPtr->disabledFillColor;
1868 }
1869 if (arcPtr->disabledFillStipple!=None) {
1870 fillStipple = arcPtr->disabledFillStipple;
1871 }
1872 }
1873
1874 /*
1875 * Make our working space.
1876 */
1877
1878 psObj = Tcl_NewObj();
1879 interpState = Tcl_SaveInterpState(interp, TCL_OK);
1880
1881 /*
1882 * If the arc is filled, output Postscript for the interior region of the
1883 * arc.
1884 */
1885
1886 if (arcPtr->fillGC != NULL) {
1887 Tcl_AppendPrintfToObj(psObj,
1888 "matrix currentmatrix\n"
1889 "%.15g %.15g translate %.15g %.15g scale\n",
1890 (arcPtr->bbox[0] + arcPtr->bbox[2])/2, (y1 + y2)/2,
1891 (arcPtr->bbox[2] - arcPtr->bbox[0])/2, (y1 - y2)/2);
1892
1893 if (arcPtr->style != CHORD_STYLE) {
1894 Tcl_AppendToObj(psObj, "0 0 moveto ", -1);
1895 }
1896 Tcl_AppendPrintfToObj(psObj,
1897 "0 0 1 %.15g %.15g arc closepath\nsetmatrix\n",
1898 ang1, ang2);
1899
1900 Tcl_ResetResult(interp);
1901 if (Tk_CanvasPsColor(interp, canvas, fillColor) != TCL_OK) {
1902 goto error;
1903 }
1904 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1905
1906 if (fillStipple != None) {
1907 Tcl_AppendToObj(psObj, "clip ", -1);
1908
1909 Tcl_ResetResult(interp);
1910 if (Tk_CanvasPsStipple(interp, canvas, fillStipple) != TCL_OK) {
1911 goto error;
1912 }
1913 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1914
1915 if (arcPtr->outline.gc != NULL) {
1916 Tcl_AppendToObj(psObj, "grestore gsave\n", -1);
1917 }
1918 } else {
1919 Tcl_AppendToObj(psObj, "fill\n", -1);
1920 }
1921 }
1922
1923 /*
1924 * If there's an outline for the arc, draw it.
1925 */
1926
1927 if (arcPtr->outline.gc != NULL) {
1928 Tcl_AppendPrintfToObj(psObj,
1929 "matrix currentmatrix\n"
1930 "%.15g %.15g translate %.15g %.15g scale\n",
1931 (arcPtr->bbox[0] + arcPtr->bbox[2])/2, (y1 + y2)/2,
1932 (arcPtr->bbox[2] - arcPtr->bbox[0])/2, (y1 - y2)/2);
1933 Tcl_AppendPrintfToObj(psObj,
1934 "0 0 1 %.15g %.15g arc\nsetmatrix\n0 setlinecap\n",
1935 ang1, ang2);
1936
1937 Tcl_ResetResult(interp);
1938 if (Tk_CanvasPsOutline(canvas, itemPtr, &arcPtr->outline) != TCL_OK) {
1939 goto error;
1940 }
1941 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1942
1943 if (arcPtr->style != ARC_STYLE) {
1944 Tcl_AppendToObj(psObj, "grestore gsave\n", -1);
1945
1946 Tcl_ResetResult(interp);
1947 if (arcPtr->style == CHORD_STYLE) {
1948 Tk_CanvasPsPath(interp, canvas, arcPtr->outlinePtr,
1949 CHORD_OUTLINE_PTS);
1950 } else {
1951 Tk_CanvasPsPath(interp, canvas, arcPtr->outlinePtr,
1952 PIE_OUTLINE1_PTS);
1953 if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
1954 goto error;
1955 }
1956 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1957
1958 if (stipple != None) {
1959 Tcl_AppendToObj(psObj, "clip ", -1);
1960
1961 Tcl_ResetResult(interp);
1962 if (Tk_CanvasPsStipple(interp, canvas, stipple) !=TCL_OK){
1963 goto error;
1964 }
1965 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1966 } else {
1967 Tcl_AppendToObj(psObj, "fill\n", -1);
1968 }
1969 Tcl_AppendToObj(psObj, "grestore gsave\n", -1);
1970
1971 Tcl_ResetResult(interp);
1972 Tk_CanvasPsPath(interp, canvas,
1973 arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
1974 PIE_OUTLINE2_PTS);
1975 }
1976 if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
1977 goto error;
1978 }
1979 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1980
1981 if (stipple != None) {
1982 Tcl_AppendToObj(psObj, "clip ", -1);
1983
1984 Tcl_ResetResult(interp);
1985 if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
1986 goto error;
1987 }
1988 Tcl_AppendObjToObj(psObj, Tcl_GetObjResult(interp));
1989 } else {
1990 Tcl_AppendToObj(psObj, "fill\n", -1);
1991 }
1992 }
1993 }
1994
1995 /*
1996 * Plug the accumulated postscript back into the result.
1997 */
1998
1999 (void) Tcl_RestoreInterpState(interp, interpState);
2000 Tcl_AppendObjToObj(Tcl_GetObjResult(interp), psObj);
2001 Tcl_DecrRefCount(psObj);
2002 return TCL_OK;
2003
2004 error:
2005 Tcl_DiscardInterpState(interpState);
2006 Tcl_DecrRefCount(psObj);
2007 return TCL_ERROR;
2008 }
2009
2010 /*
2011 *--------------------------------------------------------------
2012 *
2013 * StyleParseProc --
2014 *
2015 * This function is invoked during option processing to handle the
2016 * "-style" option.
2017 *
2018 * Results:
2019 * A standard Tcl return value.
2020 *
2021 * Side effects:
2022 * The state for a given item gets replaced by the state indicated in the
2023 * value argument.
2024 *
2025 *--------------------------------------------------------------
2026 */
2027
2028 static int
StyleParseProc(TCL_UNUSED (void *),Tcl_Interp * interp,TCL_UNUSED (Tk_Window),const char * value,char * widgRec,int offset)2029 StyleParseProc(
2030 TCL_UNUSED(void *),
2031 Tcl_Interp *interp, /* Used for reporting errors. */
2032 TCL_UNUSED(Tk_Window), /* Window containing canvas widget. */
2033 const char *value, /* Value of option. */
2034 char *widgRec, /* Pointer to record for item. */
2035 int offset) /* Offset into item. */
2036 {
2037 int c;
2038 size_t length;
2039 Style *stylePtr = (Style *) (widgRec + offset);
2040
2041 if (value == NULL || *value == 0) {
2042 *stylePtr = PIESLICE_STYLE;
2043 return TCL_OK;
2044 }
2045
2046 c = value[0];
2047 length = strlen(value);
2048
2049 if ((c == 'a') && (strncmp(value, "arc", length) == 0)) {
2050 *stylePtr = ARC_STYLE;
2051 return TCL_OK;
2052 }
2053 if ((c == 'c') && (strncmp(value, "chord", length) == 0)) {
2054 *stylePtr = CHORD_STYLE;
2055 return TCL_OK;
2056 }
2057 if ((c == 'p') && (strncmp(value, "pieslice", length) == 0)) {
2058 *stylePtr = PIESLICE_STYLE;
2059 return TCL_OK;
2060 }
2061
2062 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2063 "bad -style option \"%s\": must be arc, chord, or pieslice",
2064 value));
2065 Tcl_SetErrorCode(interp, "TK", "CANVAS", "ARC_STYLE", NULL);
2066 *stylePtr = PIESLICE_STYLE;
2067 return TCL_ERROR;
2068 }
2069
2070 /*
2071 *--------------------------------------------------------------
2072 *
2073 * StylePrintProc --
2074 *
2075 * This function is invoked by the Tk configuration code to produce a
2076 * printable string for the "-style" configuration option.
2077 *
2078 * Results:
2079 * The return value is a string describing the state for the item
2080 * referred to by "widgRec". In addition, *freeProcPtr is filled in with
2081 * the address of a function to call to free the result string when it's
2082 * no longer needed (or NULL to indicate that the string doesn't need to
2083 * be freed).
2084 *
2085 * Side effects:
2086 * None.
2087 *
2088 *--------------------------------------------------------------
2089 */
2090
2091 static const char *
StylePrintProc(TCL_UNUSED (void *),TCL_UNUSED (Tk_Window),char * widgRec,int offset,TCL_UNUSED (Tcl_FreeProc **))2092 StylePrintProc(
2093 TCL_UNUSED(void *), /* Ignored. */
2094 TCL_UNUSED(Tk_Window), /* Ignored. */
2095 char *widgRec, /* Pointer to record for item. */
2096 int offset, /* Offset into item. */
2097 TCL_UNUSED(Tcl_FreeProc **)) /* Pointer to variable to fill in with
2098 * information about how to reclaim storage
2099 * for return string. */
2100 {
2101 Style *stylePtr = (Style *) (widgRec + offset);
2102
2103 if (*stylePtr == ARC_STYLE) {
2104 return "arc";
2105 } else if (*stylePtr == CHORD_STYLE) {
2106 return "chord";
2107 } else {
2108 return "pieslice";
2109 }
2110 }
2111
2112 /*
2113 * Local Variables:
2114 * mode: c
2115 * c-basic-offset: 4
2116 * fill-column: 78
2117 * End:
2118 */
2119