]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkcvarc.c
show mini-map when hovering over the empty mini-map frame
[micropolis] / src / tk / tkcvarc.c
1 /*
2 * tkCanvArc.c --
3 *
4 * This file implements arc items for canvas widgets.
5 *
6 * Copyright 1992 Regents of the University of California.
7 * Permission to use, copy, modify, and distribute this
8 * software and its documentation for any purpose and without
9 * fee is hereby granted, provided that the above copyright
10 * notice appear in all copies. The University of California
11 * makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without
13 * express or implied warranty.
14 */
15
16 #ifndef lint
17 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvArc.c,v 1.5 92/08/16 15:42:20 ouster Exp $ SPRITE (Berkeley)";
18 #endif
19
20 #include <stdio.h>
21 #include <math.h>
22 #include "tkint.h"
23 #include "tkcanvas.h"
24
25 /*
26 * The structure below defines the record for each arc item.
27 */
28
29 typedef struct ArcItem {
30 Tk_Item header; /* Generic stuff that's the same for all
31 * types. MUST BE FIRST IN STRUCTURE. */
32 double bbox[4]; /* Coordinates (x1, y1, x2, y2) of bounding
33 * box for oval of which arc is a piece. */
34 double start; /* Angle at which arc begins, in degrees
35 * between 0 and 360. */
36 double extent; /* Extent of arc (angular distance from
37 * start to end of arc) in degrees between
38 * -360 and 360. */
39 double *outlinePtr; /* Points to (x,y) coordinates for points
40 * that define one or two closed polygons
41 * representing the portion of the outline
42 * that isn't part of the arc (the V-shape
43 * for a pie slice or a line-like segment
44 * for a chord). Malloc'ed. */
45 int numOutlinePoints; /* Number of points at outlinePtr. Zero
46 * means no space allocated. */
47 int width; /* Width of outline (in pixels). */
48 XColor *outlineColor; /* Color for outline. NULL means don't
49 * draw outline. */
50 XColor *fillColor; /* Color for filling arc (used for drawing
51 * outline too when style is "arc"). NULL
52 * means don't fill arc. */
53 Pixmap fillStipple; /* Stipple bitmap for filling item. */
54 Tk_Uid style; /* How to draw arc: arc, chord, or pieslice. */
55 GC outlineGC; /* Graphics context for outline. */
56 GC fillGC; /* Graphics context for filling item. */
57 double center1[2]; /* Coordinates of center of arc outline at
58 * start (see ComputeArcOutline). */
59 double center2[2]; /* Coordinates of center of arc outline at
60 * start+extent (see ComputeArcOutline). */
61 } ArcItem;
62
63 /*
64 * The definitions below define the sizes of the polygons used to
65 * display outline information for various styles of arcs:
66 */
67
68 #define CHORD_OUTLINE_PTS 7
69 #define PIE_OUTLINE1_PTS 6
70 #define PIE_OUTLINE2_PTS 7
71
72 /*
73 * Information used for parsing configuration specs:
74 */
75
76 static Tk_ConfigSpec configSpecs[] = {
77 {TK_CONFIG_DOUBLE, "-extent", (char *) NULL, (char *) NULL,
78 "90", Tk_Offset(ArcItem, extent), TK_CONFIG_DONT_SET_DEFAULT},
79 {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
80 (char *) NULL, Tk_Offset(ArcItem, fillColor), TK_CONFIG_NULL_OK},
81 {TK_CONFIG_COLOR, "-outline", (char *) NULL, (char *) NULL,
82 "black", Tk_Offset(ArcItem, outlineColor), TK_CONFIG_NULL_OK},
83 {TK_CONFIG_DOUBLE, "-start", (char *) NULL, (char *) NULL,
84 "0", Tk_Offset(ArcItem, start), TK_CONFIG_DONT_SET_DEFAULT},
85 {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
86 (char *) NULL, Tk_Offset(ArcItem, fillStipple), TK_CONFIG_NULL_OK},
87 {TK_CONFIG_UID, "-style", (char *) NULL, (char *) NULL,
88 "pieslice", Tk_Offset(ArcItem, style), TK_CONFIG_DONT_SET_DEFAULT},
89 {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
90 (char *) NULL, 0, TK_CONFIG_NULL_OK, &tkCanvasTagsOption},
91 {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
92 "1", Tk_Offset(ArcItem, width), TK_CONFIG_DONT_SET_DEFAULT},
93 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
94 (char *) NULL, 0, 0}
95 };
96
97 /*
98 * Prototypes for procedures defined in this file:
99 */
100
101 static int ArcCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr,
102 Tk_Item *itemPtr, int argc, char **argv));
103 static int AngleInRange _ANSI_ARGS_((double x, double y,
104 double start, double extent));
105 static int ArcToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
106 Tk_Item *itemPtr, double *rectPtr));
107 static double ArcToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr,
108 Tk_Item *itemPtr, double *coordPtr));
109 static void ComputeArcBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr,
110 ArcItem *arcPtr));
111 static void ComputeArcOutline _ANSI_ARGS_((ArcItem *arcPtr));
112 static int ConfigureArc _ANSI_ARGS_((
113 Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc,
114 char **argv, int flags));
115 static int CreateArc _ANSI_ARGS_((Tk_Canvas *canvasPtr,
116 struct Tk_Item *itemPtr, int argc, char **argv));
117 static void DeleteArc _ANSI_ARGS_((Tk_Item *itemPtr));
118 static void DisplayArc _ANSI_ARGS_((Tk_Canvas *canvasPtr,
119 Tk_Item *itemPtr, Drawable dst));
120 static int HorizLineToArc _ANSI_ARGS_((double x1, double x2,
121 double y, double rx, double ry,
122 double start, double extent));
123 static void ScaleArc _ANSI_ARGS_((Tk_Canvas *canvasPtr,
124 Tk_Item *itemPtr, double originX, double originY,
125 double scaleX, double scaleY));
126 static void TranslateArc _ANSI_ARGS_((Tk_Canvas *canvasPtr,
127 Tk_Item *itemPtr, double deltaX, double deltaY));
128 static int VertLineToArc _ANSI_ARGS_((double x, double y1,
129 double y2, double rx, double ry,
130 double start, double extent));
131
132 /*
133 * The structures below defines the arc item types by means of procedures
134 * that can be invoked by generic item code.
135 */
136
137 Tk_ItemType TkArcType = {
138 "arc", /* name */
139 sizeof(ArcItem), /* itemSize */
140 CreateArc, /* createProc */
141 configSpecs, /* configSpecs */
142 ConfigureArc, /* configureProc */
143 ArcCoords, /* coordProc */
144 DeleteArc, /* deleteProc */
145 DisplayArc, /* displayProc */
146 0, /* alwaysRedraw */
147 ArcToPoint, /* pointProc */
148 ArcToArea, /* areaProc */
149 (Tk_ItemPostscriptProc *) NULL, /* postscriptProc */
150 ScaleArc, /* scaleProc */
151 TranslateArc, /* translateProc */
152 (Tk_ItemIndexProc *) NULL, /* indexProc */
153 (Tk_ItemCursorProc *) NULL, /* cursorProc */
154 (Tk_ItemSelectionProc *) NULL, /* selectionProc */
155 (Tk_ItemInsertProc *) NULL, /* insertProc */
156 (Tk_ItemDCharsProc *) NULL, /* dTextProc */
157 (Tk_ItemType *) NULL /* nextPtr */
158 };
159
160 #define PI 3.14159265358979323846
161
162 /*
163 * The uid's below comprise the legal values for the "-style"
164 * option for arcs.
165 */
166
167 static Tk_Uid arcUid = NULL;
168 static Tk_Uid chordUid = NULL;
169 static Tk_Uid pieSliceUid = NULL;
170 \f
171 /*
172 *--------------------------------------------------------------
173 *
174 * CreateArc --
175 *
176 * This procedure is invoked to create a new arc item in
177 * a canvas.
178 *
179 * Results:
180 * A standard Tcl return value. If an error occurred in
181 * creating the item, then an error message is left in
182 * canvasPtr->interp->result; in this case itemPtr is
183 * left uninitialized, so it can be safely freed by the
184 * caller.
185 *
186 * Side effects:
187 * A new arc item is created.
188 *
189 *--------------------------------------------------------------
190 */
191
192 static int
193 CreateArc(canvasPtr, itemPtr, argc, argv)
194 register Tk_Canvas *canvasPtr; /* Canvas to hold new item. */
195 Tk_Item *itemPtr; /* Record to hold new item; header
196 * has been initialized by caller. */
197 int argc; /* Number of arguments in argv. */
198 char **argv; /* Arguments describing arc. */
199 {
200 register ArcItem *arcPtr = (ArcItem *) itemPtr;
201
202 if (argc < 4) {
203 Tcl_AppendResult(canvasPtr->interp, "wrong # args: should be \"",
204 Tk_PathName(canvasPtr->tkwin), "\" create ",
205 itemPtr->typePtr->name, " x1 y1 x2 y2 ?options?",
206 (char *) NULL);
207 return TCL_ERROR;
208 }
209
210 /*
211 * Carry out once-only initialization.
212 */
213
214 if (arcUid == NULL) {
215 arcUid = Tk_GetUid("arc");
216 chordUid = Tk_GetUid("chord");
217 pieSliceUid = Tk_GetUid("pieslice");
218 }
219
220 /*
221 * Carry out initialization that is needed in order to clean
222 * up after errors during the the remainder of this procedure.
223 */
224
225 arcPtr->start = 0;
226 arcPtr->extent = 90;
227 arcPtr->outlinePtr = NULL;
228 arcPtr->numOutlinePoints = 0;
229 arcPtr->width = 1;
230 arcPtr->outlineColor = NULL;
231 arcPtr->fillColor = NULL;
232 arcPtr->fillStipple = None;
233 arcPtr->style = pieSliceUid;
234 arcPtr->outlineGC = None;
235 arcPtr->fillGC = None;
236
237 /*
238 * Process the arguments to fill in the item record.
239 */
240
241 if ((TkGetCanvasCoord(canvasPtr, argv[0], &arcPtr->bbox[0]) != TCL_OK)
242 || (TkGetCanvasCoord(canvasPtr, argv[1],
243 &arcPtr->bbox[1]) != TCL_OK)
244 || (TkGetCanvasCoord(canvasPtr, argv[2],
245 &arcPtr->bbox[2]) != TCL_OK)
246 || (TkGetCanvasCoord(canvasPtr, argv[3],
247 &arcPtr->bbox[3]) != TCL_OK)) {
248 return TCL_ERROR;
249 }
250
251 if (ConfigureArc(canvasPtr, itemPtr, argc-4, argv+4, 0) != TCL_OK) {
252 DeleteArc(itemPtr);
253 return TCL_ERROR;
254 }
255 return TCL_OK;
256 }
257 \f
258 /*
259 *--------------------------------------------------------------
260 *
261 * ArcCoords --
262 *
263 * This procedure is invoked to process the "coords" widget
264 * command on arcs. See the user documentation for details
265 * on what it does.
266 *
267 * Results:
268 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
269 *
270 * Side effects:
271 * The coordinates for the given item may be changed.
272 *
273 *--------------------------------------------------------------
274 */
275
276 static int
277 ArcCoords(canvasPtr, itemPtr, argc, argv)
278 register Tk_Canvas *canvasPtr; /* Canvas containing item. */
279 Tk_Item *itemPtr; /* Item whose coordinates are to be
280 * read or modified. */
281 int argc; /* Number of coordinates supplied in
282 * argv. */
283 char **argv; /* Array of coordinates: x1, y1,
284 * x2, y2, ... */
285 {
286 register ArcItem *arcPtr = (ArcItem *) itemPtr;
287 char buffer[500];
288
289 if (argc == 0) {
290 sprintf(buffer, "%g %g %g %g", arcPtr->bbox[0],
291 arcPtr->bbox[1], arcPtr->bbox[2],
292 arcPtr->bbox[3]);
293 Tcl_SetResult(canvasPtr->interp, buffer, TCL_VOLATILE);
294 } else if (argc == 4) {
295 if ((TkGetCanvasCoord(canvasPtr, argv[0],
296 &arcPtr->bbox[0]) != TCL_OK)
297 || (TkGetCanvasCoord(canvasPtr, argv[1],
298 &arcPtr->bbox[1]) != TCL_OK)
299 || (TkGetCanvasCoord(canvasPtr, argv[2],
300 &arcPtr->bbox[2]) != TCL_OK)
301 || (TkGetCanvasCoord(canvasPtr, argv[3],
302 &arcPtr->bbox[3]) != TCL_OK)) {
303 return TCL_ERROR;
304 }
305 ComputeArcBbox(canvasPtr, arcPtr);
306 } else {
307 sprintf(canvasPtr->interp->result,
308 "wrong # coordinates: expected 0 or 4, got %d",
309 argc);
310 return TCL_ERROR;
311 }
312 return TCL_OK;
313 }
314 \f
315 /*
316 *--------------------------------------------------------------
317 *
318 * ConfigureArc --
319 *
320 * This procedure is invoked to configure various aspects
321 * of a arc item, such as its outline and fill colors.
322 *
323 * Results:
324 * A standard Tcl result code. If an error occurs, then
325 * an error message is left in canvasPtr->interp->result.
326 *
327 * Side effects:
328 * Configuration information, such as colors and stipple
329 * patterns, may be set for itemPtr.
330 *
331 *--------------------------------------------------------------
332 */
333
334 static int
335 ConfigureArc(canvasPtr, itemPtr, argc, argv, flags)
336 Tk_Canvas *canvasPtr; /* Canvas containing itemPtr. */
337 Tk_Item *itemPtr; /* Arc item to reconfigure. */
338 int argc; /* Number of elements in argv. */
339 char **argv; /* Arguments describing things to configure. */
340 int flags; /* Flags to pass to Tk_ConfigureWidget. */
341 {
342 register ArcItem *arcPtr = (ArcItem *) itemPtr;
343 XGCValues gcValues;
344 GC newGC;
345 unsigned long mask;
346 int i;
347
348 if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin,
349 configSpecs, argc, argv, (char *) arcPtr, flags) != TCL_OK) {
350 return TCL_ERROR;
351 }
352
353 /*
354 * A few of the options require additional processing, such as
355 * style and graphics contexts.
356 */
357
358 i = arcPtr->start/360.0;
359 arcPtr->start -= i*360.0;
360 if (arcPtr->start < 0) {
361 arcPtr->start += 360.0;
362 }
363 i = arcPtr->extent/360.0;
364 arcPtr->extent -= i*360.0;
365
366 if ((arcPtr->style != arcUid) && (arcPtr->style != chordUid)
367 && (arcPtr->style != pieSliceUid)) {
368 Tcl_AppendResult(canvasPtr->interp, "bad -style option \"",
369 arcPtr->style, "\": must be arc, chord, or pieslice",
370 (char *) NULL);
371 arcPtr->style = pieSliceUid;
372 return TCL_ERROR;
373 }
374
375 if (arcPtr->width < 0) {
376 arcPtr->width = 1;
377 }
378 if (arcPtr->style == arcUid) {
379 if (arcPtr->fillColor == NULL) {
380 newGC = None;
381 } else {
382 gcValues.foreground = arcPtr->fillColor->pixel;
383 gcValues.cap_style = CapButt;
384 gcValues.line_width = arcPtr->width;
385 mask = GCForeground|GCCapStyle|GCLineWidth;
386 if (arcPtr->fillStipple != None) {
387 gcValues.stipple = arcPtr->fillStipple;
388 gcValues.fill_style = FillStippled;
389 mask |= GCStipple|GCFillStyle;
390 }
391 newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
392 }
393 } else if (arcPtr->outlineColor == NULL) {
394 newGC = None;
395 } else {
396 gcValues.foreground = arcPtr->outlineColor->pixel;
397 gcValues.cap_style = CapButt;
398 gcValues.line_width = arcPtr->width;
399 mask = GCForeground|GCCapStyle|GCLineWidth;
400 newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
401 }
402 if (arcPtr->outlineGC != None) {
403 Tk_FreeGC(arcPtr->outlineGC);
404 }
405 arcPtr->outlineGC = newGC;
406
407 if ((arcPtr->fillColor == NULL) || (arcPtr->style == arcUid)) {
408 newGC = None;
409 } else {
410 gcValues.foreground = arcPtr->fillColor->pixel;
411 if (arcPtr->style == chordUid) {
412 gcValues.arc_mode = ArcChord;
413 } else {
414 gcValues.arc_mode = ArcPieSlice;
415 }
416 mask = GCForeground|GCArcMode;
417 if (arcPtr->fillStipple != None) {
418 gcValues.stipple = arcPtr->fillStipple;
419 gcValues.fill_style = FillStippled;
420 mask |= GCStipple|GCFillStyle;
421 }
422 newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
423 }
424 if (arcPtr->fillGC != None) {
425 Tk_FreeGC(arcPtr->fillGC);
426 }
427 arcPtr->fillGC = newGC;
428
429 ComputeArcBbox(canvasPtr, arcPtr);
430 return TCL_OK;
431 }
432 \f
433 /*
434 *--------------------------------------------------------------
435 *
436 * DeleteArc --
437 *
438 * This procedure is called to clean up the data structure
439 * associated with a arc item.
440 *
441 * Results:
442 * None.
443 *
444 * Side effects:
445 * Resources associated with itemPtr are released.
446 *
447 *--------------------------------------------------------------
448 */
449
450 static void
451 DeleteArc(itemPtr)
452 Tk_Item *itemPtr; /* Item that is being deleted. */
453 {
454 register ArcItem *arcPtr = (ArcItem *) itemPtr;
455
456 if (arcPtr->numOutlinePoints != 0) {
457 ckfree((char *) arcPtr->outlinePtr);
458 }
459 if (arcPtr->outlineColor != NULL) {
460 Tk_FreeColor(arcPtr->outlineColor);
461 }
462 if (arcPtr->fillColor != NULL) {
463 Tk_FreeColor(arcPtr->fillColor);
464 }
465 if (arcPtr->fillStipple != None) {
466 Tk_FreeBitmap(arcPtr->fillStipple);
467 }
468 if (arcPtr->outlineGC != None) {
469 Tk_FreeGC(arcPtr->outlineGC);
470 }
471 if (arcPtr->fillGC != None) {
472 Tk_FreeGC(arcPtr->fillGC);
473 }
474 }
475 \f
476 /*
477 *--------------------------------------------------------------
478 *
479 * ComputeArcBbox --
480 *
481 * This procedure is invoked to compute the bounding box of
482 * all the pixels that may be drawn as part of an arc.
483 *
484 * Results:
485 * None.
486 *
487 * Side effects:
488 * The fields x1, y1, x2, and y2 are updated in the header
489 * for itemPtr.
490 *
491 *--------------------------------------------------------------
492 */
493
494 /* ARGSUSED */
495 static void
496 ComputeArcBbox(canvasPtr, arcPtr)
497 register Tk_Canvas *canvasPtr; /* Canvas that contains item. */
498 register ArcItem *arcPtr; /* Item whose bbox is to be
499 * recomputed. */
500 {
501 double tmp, center[2], point[2];
502
503 /*
504 * Make sure that the first coordinates are the lowest ones.
505 */
506
507 if (arcPtr->bbox[1] > arcPtr->bbox[3]) {
508 double tmp;
509 tmp = arcPtr->bbox[3];
510 arcPtr->bbox[3] = arcPtr->bbox[1];
511 arcPtr->bbox[1] = tmp;
512 }
513 if (arcPtr->bbox[0] > arcPtr->bbox[2]) {
514 double tmp;
515 tmp = arcPtr->bbox[2];
516 arcPtr->bbox[2] = arcPtr->bbox[0];
517 arcPtr->bbox[0] = tmp;
518 }
519
520 ComputeArcOutline(arcPtr);
521
522 /*
523 * To compute the bounding box, start with the the bbox formed
524 * by the two endpoints of the arc. Then add in the center of
525 * the arc's oval (if relevant) and the 3-o'clock, 6-o'clock,
526 * 9-o'clock, and 12-o'clock positions, if they are relevant.
527 */
528
529 arcPtr->header.x1 = arcPtr->header.x2 = arcPtr->center1[0];
530 arcPtr->header.y1 = arcPtr->header.y2 = arcPtr->center1[1];
531 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, arcPtr->center2);
532 center[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2;
533 center[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2;
534 if (arcPtr->style != arcUid) {
535 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, center);
536 }
537
538 tmp = -arcPtr->start;
539 if (tmp < 0) {
540 tmp += 360.0;
541 }
542 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
543 point[0] = arcPtr->bbox[2];
544 point[1] = center[1];
545 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, point);
546 }
547 tmp = 90.0 - arcPtr->start;
548 if (tmp < 0) {
549 tmp += 360.0;
550 }
551 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
552 point[0] = center[0];
553 point[1] = arcPtr->bbox[1];
554 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, point);
555 }
556 tmp = 180.0 - arcPtr->start;
557 if (tmp < 0) {
558 tmp += 360.0;
559 }
560 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
561 point[0] = arcPtr->bbox[0];
562 point[1] = center[1];
563 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, point);
564 }
565 tmp = 270.0 - arcPtr->start;
566 if (tmp < 0) {
567 tmp += 360.0;
568 }
569 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
570 point[0] = center[0];
571 point[1] = arcPtr->bbox[3];
572 TkIncludePoint(canvasPtr, (Tk_Item *) arcPtr, point);
573 }
574
575 /*
576 * Lastly, expand by the width of the arc (if the arc's outline is
577 * being drawn) and add one extra pixel just for safety.
578 */
579
580 if (arcPtr->outlineColor == NULL) {
581 tmp = 1;
582 } else {
583 tmp = (arcPtr->width + 1)/2 + 1;
584 }
585 arcPtr->header.x1 -= tmp;
586 arcPtr->header.y1 -= tmp;
587 arcPtr->header.x2 += tmp;
588 arcPtr->header.y2 += tmp;
589 }
590 \f
591 /*
592 *--------------------------------------------------------------
593 *
594 * DisplayArc --
595 *
596 * This procedure is invoked to draw an arc item in a given
597 * drawable.
598 *
599 * Results:
600 * None.
601 *
602 * Side effects:
603 * ItemPtr is drawn in drawable using the transformation
604 * information in canvasPtr.
605 *
606 *--------------------------------------------------------------
607 */
608
609 static void
610 DisplayArc(canvasPtr, itemPtr, drawable)
611 register Tk_Canvas *canvasPtr; /* Canvas that contains item. */
612 Tk_Item *itemPtr; /* Item to be displayed. */
613 Drawable drawable; /* Pixmap or window in which to draw
614 * item. */
615 {
616 register ArcItem *arcPtr = (ArcItem *) itemPtr;
617 Display *display = Tk_Display(canvasPtr->tkwin);
618 int x1, y1, x2, y2, start, extent;
619
620 /*
621 * Compute the screen coordinates of the bounding box for the item,
622 * plus integer values for the angles.
623 */
624
625 x1 = SCREEN_X(canvasPtr, arcPtr->bbox[0]);
626 y1 = SCREEN_Y(canvasPtr, arcPtr->bbox[1]);
627 x2 = SCREEN_X(canvasPtr, arcPtr->bbox[2]);
628 y2 = SCREEN_Y(canvasPtr, arcPtr->bbox[3]);
629 if (x2 <= x1) {
630 x2 = x1+1;
631 }
632 if (y2 <= y1) {
633 y2 = y1+1;
634 }
635 start = (64*arcPtr->start) + 0.5;
636 extent = (64*arcPtr->extent) + 0.5;
637
638 /*
639 * Display filled arc first (if wanted), then outline.
640 */
641
642 if (arcPtr->fillGC != None) {
643 XFillArc(display, drawable, arcPtr->fillGC, x1, y1, (x2-x1),
644 (y2-y1), start, extent);
645 }
646 if (arcPtr->outlineGC != None) {
647 XDrawArc(display, drawable, arcPtr->outlineGC, x1, y1, (x2-x1),
648 (y2-y1), start, extent);
649
650 /*
651 * If the outline width is very thin, don't use polygons to draw
652 * the linear parts of the outline (this often results in nothing
653 * being displayed); just draw lines instead.
654 */
655
656 if (arcPtr->width <= 2) {
657 x1 = SCREEN_X(canvasPtr, arcPtr->center1[0]);
658 y1 = SCREEN_Y(canvasPtr, arcPtr->center1[1]);
659 x2 = SCREEN_X(canvasPtr, arcPtr->center2[0]);
660 y2 = SCREEN_Y(canvasPtr, arcPtr->center2[1]);
661
662 if (arcPtr->style == chordUid) {
663 XDrawLine(display, drawable, arcPtr->outlineGC,
664 x1, y1, x2, y2);
665 } else if (arcPtr->style == pieSliceUid) {
666 int cx, cy;
667
668 cx = SCREEN_X(canvasPtr, (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0);
669 cy = SCREEN_Y(canvasPtr, (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0);
670 XDrawLine(display, drawable, arcPtr->outlineGC,
671 cx, cy, x1, y1);
672 XDrawLine(display, drawable, arcPtr->outlineGC,
673 cx, cy, x2, y2);
674 }
675 } else {
676 if (arcPtr->style == chordUid) {
677 TkFillPolygon(canvasPtr, arcPtr->outlinePtr,
678 CHORD_OUTLINE_PTS, drawable, arcPtr->outlineGC);
679 } else if (arcPtr->style == pieSliceUid) {
680 TkFillPolygon(canvasPtr, arcPtr->outlinePtr,
681 PIE_OUTLINE1_PTS, drawable, arcPtr->outlineGC);
682 TkFillPolygon(canvasPtr,
683 arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
684 PIE_OUTLINE2_PTS, drawable, arcPtr->outlineGC);
685 }
686 }
687 }
688 }
689 \f
690 /*
691 *--------------------------------------------------------------
692 *
693 * ArcToPoint --
694 *
695 * Computes the distance from a given point to a given
696 * arc, in canvas units.
697 *
698 * Results:
699 * The return value is 0 if the point whose x and y coordinates
700 * are coordPtr[0] and coordPtr[1] is inside the arc. If the
701 * point isn't inside the arc then the return value is the
702 * distance from the point to the arc. If itemPtr is filled,
703 * then anywhere in the interior is considered "inside"; if
704 * itemPtr isn't filled, then "inside" means only the area
705 * occupied by the outline.
706 *
707 * Side effects:
708 * None.
709 *
710 *--------------------------------------------------------------
711 */
712
713 /* ARGSUSED */
714 static double
715 ArcToPoint(canvasPtr, itemPtr, pointPtr)
716 Tk_Canvas *canvasPtr; /* Canvas containing item. */
717 Tk_Item *itemPtr; /* Item to check against point. */
718 double *pointPtr; /* Pointer to x and y coordinates. */
719 {
720 register ArcItem *arcPtr = (ArcItem *) itemPtr;
721 double vertex[2], pointAngle, diff, dist, newDist;
722 double poly[8], polyDist, width;
723 int filled, angleInRange;
724
725 if ((arcPtr->fillGC != None) || (arcPtr->outlineGC == None)) {
726 filled = 1;
727 } else {
728 filled = 0;
729 }
730
731 /*
732 * See if the point is within the angular range of the arc.
733 * Remember, X angles are backwards from the way we'd normally
734 * think of them. Also, compensate for any eccentricity of
735 * the oval.
736 */
737
738 vertex[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
739 vertex[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
740 pointAngle = -atan2((pointPtr[1] - vertex[1])
741 /(arcPtr->bbox[3] - arcPtr->bbox[1]),
742 (pointPtr[0] - vertex[0])/(arcPtr->bbox[2] - arcPtr->bbox[0]));
743 pointAngle *= 180/PI;
744 diff = pointAngle - arcPtr->start;
745 diff -= ((int) (diff/360.0) * 360.0);
746 if (diff < 0) {
747 diff += 360.0;
748 }
749 angleInRange = (diff <= arcPtr->extent) ||
750 ((arcPtr->extent < 0) && ((diff - 360.0) >= arcPtr->extent));
751
752 /*
753 * Now perform different tests depending on what kind of arc
754 * we're dealing with.
755 */
756
757 if (arcPtr->style == arcUid) {
758 if (angleInRange) {
759 return TkOvalToPoint(arcPtr->bbox, (double) arcPtr->width,
760 0, pointPtr);
761 }
762 dist = hypot(pointPtr[0] - arcPtr->center1[0],
763 pointPtr[1] - arcPtr->center1[1]);
764 newDist = hypot(pointPtr[0] - arcPtr->center2[0],
765 pointPtr[1] - arcPtr->center2[1]);
766 if (newDist < dist) {
767 return newDist;
768 }
769 return dist;
770 }
771
772 if ((arcPtr->fillGC != None) || (arcPtr->outlineGC == None)) {
773 filled = 1;
774 } else {
775 filled = 0;
776 }
777 if (arcPtr->outlineGC == None) {
778 width = 0.0;
779 } else {
780 width = arcPtr->width;
781 }
782
783 if (arcPtr->style == pieSliceUid) {
784 if (width > 1.0) {
785 dist = TkPolygonToPoint(arcPtr->outlinePtr, PIE_OUTLINE1_PTS,
786 pointPtr);
787 newDist = TkPolygonToPoint(arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
788 PIE_OUTLINE2_PTS, pointPtr);
789 } else {
790 dist = TkLineToPoint(vertex, arcPtr->center1, pointPtr);
791 newDist = TkLineToPoint(vertex, arcPtr->center2, pointPtr);
792 }
793 if (newDist < dist) {
794 dist = newDist;
795 }
796 if (angleInRange) {
797 newDist = TkOvalToPoint(arcPtr->bbox, width, filled, pointPtr);
798 if (newDist < dist) {
799 dist = newDist;
800 }
801 }
802 return dist;
803 }
804
805 /*
806 * This is a chord-style arc. We have to deal specially with the
807 * triangular piece that represents the difference between a
808 * chord-style arc and a pie-slice arc (for small angles this piece
809 * is excluded here where it would be included for pie slices;
810 * for large angles the piece is included here but would be
811 * excluded for pie slices).
812 */
813
814 if (width > 1.0) {
815 dist = TkPolygonToPoint(arcPtr->outlinePtr, CHORD_OUTLINE_PTS,
816 pointPtr);
817 } else {
818 dist = TkLineToPoint(arcPtr->center1, arcPtr->center2, pointPtr);
819 }
820 poly[0] = poly[6] = vertex[0];
821 poly[1] = poly[7] = vertex[1];
822 poly[2] = arcPtr->center1[0];
823 poly[3] = arcPtr->center1[1];
824 poly[4] = arcPtr->center2[0];
825 poly[5] = arcPtr->center2[1];
826 polyDist = TkPolygonToPoint(poly, 4, pointPtr);
827 if (angleInRange) {
828 if ((arcPtr->extent < -180.0) || (arcPtr->extent > 180.0)
829 || (polyDist > 0.0)) {
830 newDist = TkOvalToPoint(arcPtr->bbox, width, filled, pointPtr);
831 if (newDist < dist) {
832 dist = newDist;
833 }
834 }
835 } else {
836 if ((arcPtr->extent < -180.0) || (arcPtr->extent > 180.0)) {
837 if (filled && (polyDist < dist)) {
838 dist = polyDist;
839 }
840 }
841 }
842 return dist;
843 }
844 \f
845 /*
846 *--------------------------------------------------------------
847 *
848 * ArcToArea --
849 *
850 * This procedure is called to determine whether an item
851 * lies entirely inside, entirely outside, or overlapping
852 * a given area.
853 *
854 * Results:
855 * -1 is returned if the item is entirely outside the area
856 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
857 * inside the given area.
858 *
859 * Side effects:
860 * None.
861 *
862 *--------------------------------------------------------------
863 */
864
865 /* ARGSUSED */
866 static int
867 ArcToArea(canvasPtr, itemPtr, rectPtr)
868 Tk_Canvas *canvasPtr; /* Canvas containing item. */
869 Tk_Item *itemPtr; /* Item to check against arc. */
870 double *rectPtr; /* Pointer to array of four coordinates
871 * (x1, y1, x2, y2) describing rectangular
872 * area. */
873 {
874 register ArcItem *arcPtr = (ArcItem *) itemPtr;
875 double rx, ry; /* Radii for transformed oval: these define
876 * an oval centered at the origin. */
877 double tRect[4]; /* Transformed version of x1, y1, x2, y2,
878 * for coord. system where arc is centered
879 * on the origin. */
880 double center[2], width, angle, tmp;
881 double points[20], *pointPtr;
882 int numPoints, filled;
883 int inside; /* Non-zero means every test so far suggests
884 * that arc is inside rectangle. 0 means
885 * every test so far shows arc to be outside
886 * of rectangle. */
887 int newInside;
888
889 if ((arcPtr->fillGC != None) || (arcPtr->outlineGC == None)) {
890 filled = 1;
891 } else {
892 filled = 0;
893 }
894 if (arcPtr->outlineGC == None) {
895 width = 0.0;
896 } else {
897 width = arcPtr->width;
898 }
899
900 /*
901 * Transform both the arc and the rectangle so that the arc's oval
902 * is centered on the origin.
903 */
904
905 center[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
906 center[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
907 tRect[0] = rectPtr[0] - center[0];
908 tRect[1] = rectPtr[1] - center[1];
909 tRect[2] = rectPtr[2] - center[0];
910 tRect[3] = rectPtr[3] - center[1];
911 rx = arcPtr->bbox[2] - center[0] + width/2.0;
912 ry = arcPtr->bbox[3] - center[1] + width/2.0;
913
914 /*
915 * Find the extreme points of the arc and see whether these are all
916 * inside the rectangle (in which case we're done), partly in and
917 * partly out (in which case we're done), or all outside (in which
918 * case we have more work to do). The extreme points include the
919 * following, which are checked in order:
920 *
921 * 1. The outside points of the arc, corresponding to start and
922 * extent.
923 * 2. The center of the arc (but only in pie-slice mode).
924 * 3. The 12, 3, 6, and 9-o'clock positions (but only if the arc
925 * includes those angles).
926 */
927
928 pointPtr = points;
929 numPoints = 0;
930 angle = -arcPtr->start*(PI/180.0);
931 pointPtr[0] = rx*cos(angle);
932 pointPtr[1] = ry*sin(angle);
933 angle += -arcPtr->extent*(PI/180.0);
934 pointPtr[2] = rx*cos(angle);
935 pointPtr[3] = ry*sin(angle);
936 numPoints = 2;
937 pointPtr += 4;
938
939 if ((arcPtr->style == pieSliceUid) && (arcPtr->extent < 180.0)) {
940 pointPtr[0] = 0.0;
941 pointPtr[1] = 0.0;
942 numPoints++;
943 pointPtr += 2;
944 }
945
946 tmp = -arcPtr->start;
947 if (tmp < 0) {
948 tmp += 360.0;
949 }
950 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
951 pointPtr[0] = rx;
952 pointPtr[1] = 0.0;
953 numPoints++;
954 pointPtr += 2;
955 }
956 tmp = 90.0 - arcPtr->start;
957 if (tmp < 0) {
958 tmp += 360.0;
959 }
960 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
961 pointPtr[0] = 0.0;
962 pointPtr[1] = -ry;
963 numPoints++;
964 pointPtr += 2;
965 }
966 tmp = 180.0 - arcPtr->start;
967 if (tmp < 0) {
968 tmp += 360.0;
969 }
970 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
971 pointPtr[0] = -rx;
972 pointPtr[1] = 0.0;
973 numPoints++;
974 pointPtr += 2;
975 }
976 tmp = 270.0 - arcPtr->start;
977 if (tmp < 0) {
978 tmp += 360.0;
979 }
980 if ((tmp < arcPtr->extent) || ((tmp-360) > arcPtr->extent)) {
981 pointPtr[0] = 0.0;
982 pointPtr[1] = ry;
983 numPoints++;
984 pointPtr += 2;
985 }
986
987 /*
988 * Now that we've located the extreme points, loop through them all
989 * to see which are inside the rectangle.
990 */
991
992 inside = (points[0] > tRect[0]) && (points[0] < tRect[2])
993 && (points[1] > tRect[1]) && (points[1] < tRect[3]);
994 for (pointPtr = points+2; numPoints > 1; pointPtr += 2, numPoints--) {
995 newInside = (pointPtr[0] > tRect[0]) && (pointPtr[0] < tRect[2])
996 && (pointPtr[1] > tRect[1]) && (pointPtr[1] < tRect[3]);
997 if (newInside != inside) {
998 return 0;
999 }
1000 }
1001
1002 if (inside) {
1003 return 1;
1004 }
1005
1006 /*
1007 * So far, oval appears to be outside rectangle, but can't yet tell
1008 * for sure. Next, test each of the four sides of the rectangle
1009 * against the bounding region for the arc. If any intersections
1010 * are found, then return "overlapping". First, test against the
1011 * polygon(s) forming the sides of a chord or pie-slice.
1012 */
1013
1014 if (arcPtr->style == pieSliceUid) {
1015 if (width >= 1.0) {
1016 if (TkPolygonToArea(arcPtr->outlinePtr, PIE_OUTLINE1_PTS,
1017 rectPtr) != -1) {
1018 return 0;
1019 }
1020 if (TkPolygonToArea(arcPtr->outlinePtr + 2*PIE_OUTLINE1_PTS,
1021 PIE_OUTLINE2_PTS, rectPtr) != -1) {
1022 return 0;
1023 }
1024 } else {
1025 if ((TkLineToArea(center, arcPtr->center1, rectPtr) != -1) ||
1026 (TkLineToArea(center, arcPtr->center2, rectPtr) != -1)) {
1027 return 0;
1028 }
1029 }
1030 } else if (arcPtr->style == chordUid) {
1031 if (width >= 1.0) {
1032 if (TkPolygonToArea(arcPtr->outlinePtr, CHORD_OUTLINE_PTS,
1033 rectPtr) != -1) {
1034 return 0;
1035 }
1036 } else {
1037 if (TkLineToArea(arcPtr->center1, arcPtr->center2,
1038 rectPtr) != -1) {
1039 return 0;
1040 }
1041 }
1042 }
1043
1044 /*
1045 * Next check for overlap between each of the four sides and the
1046 * outer perimiter of the arc. If the arc isn't filled, then also
1047 * check the inner perimeter of the arc.
1048 */
1049
1050 if (HorizLineToArc(tRect[0], tRect[2], tRect[1], rx, ry, arcPtr->start,
1051 arcPtr->extent)
1052 || HorizLineToArc(tRect[0], tRect[2], tRect[3], rx, ry,
1053 arcPtr->start, arcPtr->extent)
1054 || VertLineToArc(tRect[0], tRect[1], tRect[3], rx, ry,
1055 arcPtr->start, arcPtr->extent)
1056 || VertLineToArc(tRect[2], tRect[1], tRect[3], rx, ry,
1057 arcPtr->start, arcPtr->extent)) {
1058 return 0;
1059 }
1060 if ((width > 1.0) && !filled) {
1061 rx -= width;
1062 ry -= width;
1063 if (HorizLineToArc(tRect[0], tRect[2], tRect[1], rx, ry, arcPtr->start,
1064 arcPtr->extent)
1065 || HorizLineToArc(tRect[0], tRect[2], tRect[3], rx, ry,
1066 arcPtr->start, arcPtr->extent)
1067 || VertLineToArc(tRect[0], tRect[1], tRect[3], rx, ry,
1068 arcPtr->start, arcPtr->extent)
1069 || VertLineToArc(tRect[2], tRect[1], tRect[3], rx, ry,
1070 arcPtr->start, arcPtr->extent)) {
1071 return 0;
1072 }
1073 }
1074
1075 /*
1076 * The arc still appears to be totally disjoint from the rectangle,
1077 * but it's also possible that the rectangle is totally inside the arc.
1078 * Do one last check, which is to check one point of the rectangle
1079 * to see if it's inside the arc. If it is, we've got overlap. If
1080 * it isn't, the arc's really outside the rectangle.
1081 */
1082
1083 if (ArcToPoint(canvasPtr, itemPtr, rectPtr) == 0.0) {
1084 return 0;
1085 }
1086 return -1;
1087 }
1088 \f
1089 /*
1090 *--------------------------------------------------------------
1091 *
1092 * ScaleArc --
1093 *
1094 * This procedure is invoked to rescale an arc item.
1095 *
1096 * Results:
1097 * None.
1098 *
1099 * Side effects:
1100 * The arc referred to by itemPtr is rescaled so that the
1101 * following transformation is applied to all point
1102 * coordinates:
1103 * x' = originX + scaleX*(x-originX)
1104 * y' = originY + scaleY*(y-originY)
1105 *
1106 *--------------------------------------------------------------
1107 */
1108
1109 static void
1110 ScaleArc(canvasPtr, itemPtr, originX, originY, scaleX, scaleY)
1111 Tk_Canvas *canvasPtr; /* Canvas containing arc. */
1112 Tk_Item *itemPtr; /* Arc to be scaled. */
1113 double originX, originY; /* Origin about which to scale rect. */
1114 double scaleX; /* Amount to scale in X direction. */
1115 double scaleY; /* Amount to scale in Y direction. */
1116 {
1117 register ArcItem *arcPtr = (ArcItem *) itemPtr;
1118
1119 arcPtr->bbox[0] = originX + scaleX*(arcPtr->bbox[0] - originX);
1120 arcPtr->bbox[1] = originY + scaleY*(arcPtr->bbox[1] - originY);
1121 arcPtr->bbox[2] = originX + scaleX*(arcPtr->bbox[2] - originX);
1122 arcPtr->bbox[3] = originY + scaleY*(arcPtr->bbox[3] - originY);
1123 ComputeArcBbox(canvasPtr, arcPtr);
1124 }
1125 \f
1126 /*
1127 *--------------------------------------------------------------
1128 *
1129 * TranslateArc --
1130 *
1131 * This procedure is called to move an arc by a given amount.
1132 *
1133 * Results:
1134 * None.
1135 *
1136 * Side effects:
1137 * The position of the arc is offset by (xDelta, yDelta), and
1138 * the bounding box is updated in the generic part of the item
1139 * structure.
1140 *
1141 *--------------------------------------------------------------
1142 */
1143
1144 static void
1145 TranslateArc(canvasPtr, itemPtr, deltaX, deltaY)
1146 Tk_Canvas *canvasPtr; /* Canvas containing item. */
1147 Tk_Item *itemPtr; /* Item that is being moved. */
1148 double deltaX, deltaY; /* Amount by which item is to be
1149 * moved. */
1150 {
1151 register ArcItem *arcPtr = (ArcItem *) itemPtr;
1152
1153 arcPtr->bbox[0] += deltaX;
1154 arcPtr->bbox[1] += deltaY;
1155 arcPtr->bbox[2] += deltaX;
1156 arcPtr->bbox[3] += deltaY;
1157 ComputeArcBbox(canvasPtr, arcPtr);
1158 }
1159 \f
1160 /*
1161 *--------------------------------------------------------------
1162 *
1163 * ComputeArcOutline --
1164 *
1165 * This procedure creates a polygon describing everything in
1166 * the outline for an arc except what's in the curved part.
1167 * For a "pie slice" arc this is a V-shaped chunk, and for
1168 * a "chord" arc this is a linear chunk (with cutaway corners).
1169 * For "arc" arcs, this stuff isn't relevant.
1170 *
1171 * Results:
1172 * None.
1173 *
1174 * Side effects:
1175 * The information at arcPtr->outlinePtr gets modified, and
1176 * storage for arcPtr->outlinePtr may be allocated or freed.
1177 *
1178 *--------------------------------------------------------------
1179 */
1180
1181 static void
1182 ComputeArcOutline(arcPtr)
1183 register ArcItem *arcPtr;
1184 {
1185 double sin1, cos1, sin2, cos2, angle, halfWidth;
1186 double boxWidth, boxHeight;
1187 double vertex[2], corner1[2], corner2[2];
1188 double *outlinePtr;
1189
1190 /*
1191 * Make sure that the outlinePtr array is large enough to hold
1192 * either a chord or pie-slice outline.
1193 */
1194
1195 if (arcPtr->numOutlinePoints == 0) {
1196 arcPtr->outlinePtr = (double *) ckalloc((unsigned)
1197 (26 * sizeof(double)));
1198 arcPtr->numOutlinePoints = 22;
1199 }
1200 outlinePtr = arcPtr->outlinePtr;
1201
1202 /*
1203 * First compute the two points that lie at the centers of
1204 * the ends of the curved arc segment, which are marked with
1205 * X's in the figure below:
1206 *
1207 *
1208 * * * *
1209 * * *
1210 * * * * *
1211 * * * * *
1212 * * * * *
1213 * X * * X
1214 *
1215 * The code is tricky because the arc can be ovular in shape.
1216 * It computes the position for a unit circle, and then
1217 * scales to fit the shape of the arc's bounding box.
1218 *
1219 * Also, watch out because angles go counter-clockwise like you
1220 * might expect, but the y-coordinate system is inverted. To
1221 * handle this, just negate the angles in all the computations.
1222 */
1223
1224 boxWidth = arcPtr->bbox[2] - arcPtr->bbox[0];
1225 boxHeight = arcPtr->bbox[3] - arcPtr->bbox[1];
1226 angle = -arcPtr->start*PI/180.0;
1227 sin1 = sin(angle);
1228 cos1 = cos(angle);
1229 angle -= arcPtr->extent*PI/180.0;
1230 sin2 = sin(angle);
1231 cos2 = cos(angle);
1232 vertex[0] = (arcPtr->bbox[0] + arcPtr->bbox[2])/2.0;
1233 vertex[1] = (arcPtr->bbox[1] + arcPtr->bbox[3])/2.0;
1234 arcPtr->center1[0] = vertex[0] + cos1*boxWidth/2.0;
1235 arcPtr->center1[1] = vertex[1] + sin1*boxHeight/2.0;
1236 arcPtr->center2[0] = vertex[0] + cos2*boxWidth/2.0;
1237 arcPtr->center2[1] = vertex[1] + sin2*boxHeight/2.0;
1238
1239 /*
1240 * Next compute the "outermost corners" of the arc, which are
1241 * marked with X's in the figure below:
1242 *
1243 * * * *
1244 * * *
1245 * * * * *
1246 * * * * *
1247 * X * * X
1248 * * *
1249 *
1250 * The code below is tricky because it has to handle eccentricity
1251 * in the shape of the oval. The key in the code below is to
1252 * realize that the slope of the line from arcPtr->center1 to corner1
1253 * is (boxWidth*sin1)/(boxHeight*cos1), and similarly for arcPtr->center2
1254 * and corner2. These formulas can be computed from the formula for
1255 * the oval.
1256 */
1257
1258 halfWidth = arcPtr->width/2.0;
1259 angle = atan2(boxWidth*sin1, boxHeight*cos1);
1260 corner1[0] = arcPtr->center1[0] + cos(angle)*halfWidth;
1261 corner1[1] = arcPtr->center1[1] + sin(angle)*halfWidth;
1262 angle = atan2(boxWidth*sin2, boxHeight*cos2);
1263 corner2[0] = arcPtr->center2[0] + cos(angle)*halfWidth;
1264 corner2[1] = arcPtr->center2[1] + sin(angle)*halfWidth;
1265
1266 /*
1267 * For a chord outline, generate a six-sided polygon with three
1268 * points for each end of the chord. The first and third points
1269 * for each end are butt points generated on either side of the
1270 * center point. The second point is the corner point.
1271 */
1272
1273 if (arcPtr->style == chordUid) {
1274 outlinePtr[0] = outlinePtr[12] = corner1[0];
1275 outlinePtr[1] = outlinePtr[13] = corner1[1];
1276 TkGetButtPoints(arcPtr->center2, arcPtr->center1,
1277 (double) arcPtr->width, 0, outlinePtr+10, outlinePtr+2);
1278 outlinePtr[4] = arcPtr->center2[0] + outlinePtr[2]
1279 - arcPtr->center1[0];
1280 outlinePtr[5] = arcPtr->center2[1] + outlinePtr[3]
1281 - arcPtr->center1[1];
1282 outlinePtr[6] = corner2[0];
1283 outlinePtr[7] = corner2[1];
1284 outlinePtr[8] = arcPtr->center2[0] + outlinePtr[10]
1285 - arcPtr->center1[0];
1286 outlinePtr[9] = arcPtr->center2[1] + outlinePtr[11]
1287 - arcPtr->center1[1];
1288 } else if (arcPtr->style == pieSliceUid) {
1289 /*
1290 * For pie slices, generate two polygons, one for each side
1291 * of the pie slice. The first arm has a shape like this,
1292 * where the center of the oval is X, arcPtr->center1 is at Y, and
1293 * corner1 is at Z:
1294 *
1295 * _____________________
1296 * | \
1297 * | \
1298 * X Y Z
1299 * | /
1300 * |_____________________/
1301 *
1302 */
1303
1304 TkGetButtPoints(arcPtr->center1, vertex, (double) arcPtr->width, 0,
1305 outlinePtr, outlinePtr+2);
1306 outlinePtr[4] = arcPtr->center1[0] + outlinePtr[2] - vertex[0];
1307 outlinePtr[5] = arcPtr->center1[1] + outlinePtr[3] - vertex[1];
1308 outlinePtr[6] = corner1[0];
1309 outlinePtr[7] = corner1[1];
1310 outlinePtr[8] = arcPtr->center1[0] + outlinePtr[0] - vertex[0];
1311 outlinePtr[9] = arcPtr->center1[1] + outlinePtr[1] - vertex[1];
1312 outlinePtr[10] = outlinePtr[0];
1313 outlinePtr[11] = outlinePtr[1];
1314
1315 /*
1316 * The second arm has a shape like this:
1317 *
1318 *
1319 * ______________________
1320 * / \
1321 * / \
1322 * Z Y X /
1323 * \ /
1324 * \______________________/
1325 *
1326 * Similar to above X is the center of the oval/circle, Y is
1327 * arcPtr->center2, and Z is corner2. The extra jog out to the left
1328 * of X is needed in or to produce a butted joint with the
1329 * first arm; the corner to the right of X is one of the
1330 * first two points of the first arm, depending on extent.
1331 */
1332
1333 TkGetButtPoints(arcPtr->center2, vertex, (double) arcPtr->width, 0,
1334 outlinePtr+12, outlinePtr+16);
1335 if ((arcPtr->extent > 180) ||
1336 ((arcPtr->extent < 0) && (arcPtr->extent > -180))) {
1337 outlinePtr[14] = outlinePtr[0];
1338 outlinePtr[15] = outlinePtr[1];
1339 } else {
1340 outlinePtr[14] = outlinePtr[2];
1341 outlinePtr[15] = outlinePtr[3];
1342 }
1343 outlinePtr[18] = arcPtr->center2[0] + outlinePtr[16] - vertex[0];
1344 outlinePtr[19] = arcPtr->center2[1] + outlinePtr[17] - vertex[1];
1345 outlinePtr[20] = corner2[0];
1346 outlinePtr[21] = corner2[1];
1347 outlinePtr[22] = arcPtr->center2[0] + outlinePtr[12] - vertex[0];
1348 outlinePtr[23] = arcPtr->center2[1] + outlinePtr[13] - vertex[1];
1349 outlinePtr[24] = outlinePtr[12];
1350 outlinePtr[25] = outlinePtr[13];
1351 }
1352 }
1353 \f
1354 /*
1355 *--------------------------------------------------------------
1356 *
1357 * HorizLineToArc --
1358 *
1359 * Determines whether a horizontal line segment intersects
1360 * a given arc.
1361 *
1362 * Results:
1363 * The return value is 1 if the given line intersects the
1364 * infinitely-thin arc section defined by rx, ry, start,
1365 * and extent, and 0 otherwise. Only the perimeter of the
1366 * arc is checked: interior areas (e.g. pie-slice or chord)
1367 * are not checked.
1368 *
1369 * Side effects:
1370 * None.
1371 *
1372 *--------------------------------------------------------------
1373 */
1374
1375 static int
1376 HorizLineToArc(x1, x2, y, rx, ry, start, extent)
1377 double x1, x2; /* X-coords of endpoints of line segment.
1378 * X1 must be <= x2. */
1379 double y; /* Y-coordinate of line segment. */
1380 double rx, ry; /* These x- and y-radii define an oval
1381 * centered at the origin. */
1382 double start, extent; /* Angles that define extent of arc, in
1383 * the standard fashion for this module. */
1384 {
1385 double tmp;
1386 double tx, ty; /* Coordinates of intersection point in
1387 * transformed coordinate system. */
1388 double x;
1389
1390 /*
1391 * Compute the x-coordinate of one possible intersection point
1392 * between the arc and the line. Use a transformed coordinate
1393 * system where the oval is a unit circle centered at the origin.
1394 * Then scale back to get actual x-coordinate.
1395 */
1396
1397 ty = y/ry;
1398 tmp = 1 - ty*ty;
1399 if (tmp < 0) {
1400 return 0;
1401 }
1402 tx = sqrt(tmp);
1403 x = tx*rx;
1404
1405 /*
1406 * Test both intersection points.
1407 */
1408
1409 if ((x >= x1) && (x <= x2) && AngleInRange(tx, ty, start, extent)) {
1410 return 1;
1411 }
1412 if ((-x >= x1) && (-x <= x2) && AngleInRange(-tx, ty, start, extent)) {
1413 return 1;
1414 }
1415 return 0;
1416 }
1417 \f
1418 /*
1419 *--------------------------------------------------------------
1420 *
1421 * VertLineToArc --
1422 *
1423 * Determines whether a vertical line segment intersects
1424 * a given arc.
1425 *
1426 * Results:
1427 * The return value is 1 if the given line intersects the
1428 * infinitely-thin arc section defined by rx, ry, start,
1429 * and extent, and 0 otherwise. Only the perimeter of the
1430 * arc is checked: interior areas (e.g. pie-slice or chord)
1431 * are not checked.
1432 *
1433 * Side effects:
1434 * None.
1435 *
1436 *--------------------------------------------------------------
1437 */
1438
1439 static int
1440 VertLineToArc(x, y1, y2, rx, ry, start, extent)
1441 double x; /* X-coordinate of line segment. */
1442 double y1, y2; /* Y-coords of endpoints of line segment.
1443 * Y1 must be <= y2. */
1444 double rx, ry; /* These x- and y-radii define an oval
1445 * centered at the origin. */
1446 double start, extent; /* Angles that define extent of arc, in
1447 * the standard fashion for this module. */
1448 {
1449 double tmp;
1450 double tx, ty; /* Coordinates of intersection point in
1451 * transformed coordinate system. */
1452 double y;
1453
1454 /*
1455 * Compute the y-coordinate of one possible intersection point
1456 * between the arc and the line. Use a transformed coordinate
1457 * system where the oval is a unit circle centered at the origin.
1458 * Then scale back to get actual y-coordinate.
1459 */
1460
1461 tx = x/rx;
1462 tmp = 1 - tx*tx;
1463 if (tmp < 0) {
1464 return 0;
1465 }
1466 ty = sqrt(tmp);
1467 y = ty*ry;
1468
1469 /*
1470 * Test both intersection points.
1471 */
1472
1473 if ((y > y1) && (y < y2) && AngleInRange(tx, ty, start, extent)) {
1474 return 1;
1475 }
1476 if ((-y > y1) && (-y < y2) && AngleInRange(tx, -ty, start, extent)) {
1477 return 1;
1478 }
1479 return 0;
1480 }
1481 \f
1482 /*
1483 *--------------------------------------------------------------
1484 *
1485 * AngleInRange --
1486 *
1487 * Determine whether the angle from the origin to a given
1488 * point is within a given range.
1489 *
1490 * Results:
1491 * The return value is 1 if the angle from (0,0) to (x,y)
1492 * is in the range given by start and extent, where angles
1493 * are interpreted in the standard way for ovals (meaning
1494 * backwards from normal interpretation). Otherwise the
1495 * return value is 0.
1496 *
1497 * Side effects:
1498 * None.
1499 *
1500 *--------------------------------------------------------------
1501 */
1502
1503 static int
1504 AngleInRange(x, y, start, extent)
1505 double x, y; /* Coordinate of point; angle measured
1506 * from origin to here, relative to x-axis. */
1507 double start; /* First angle, degrees, >=0, <=360. */
1508 double extent; /* Size of arc in degrees >=-360, <=360. */
1509 {
1510 double diff;
1511
1512 diff = -atan2(y, x);
1513 diff = diff*(180.0/PI) - start;
1514 while (diff > 360.0) {
1515 diff -= 360.0;
1516 }
1517 while (diff < 0.0) {
1518 diff += 360.0;
1519 }
1520 if (extent >= 0) {
1521 return diff <= extent;
1522 }
1523 return (diff-360.0) >= extent;
1524 }
Impressum, Datenschutz