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