]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tk3D.c -- | |
3 | * | |
4 | * This module provides procedures to draw borders in | |
5 | * the three-dimensional Motif style. | |
6 | * | |
7 | * Copyright 1990 Regents of the University of California. | |
8 | * Permission to use, copy, modify, and distribute this | |
9 | * software and its documentation for any purpose and without | |
10 | * fee is hereby granted, provided that the above copyright | |
11 | * notice appear in all copies. The University of California | |
12 | * makes no representations about the suitability of this | |
13 | * software for any purpose. It is provided "as is" without | |
14 | * express or implied warranty. | |
15 | */ | |
16 | ||
17 | #ifndef lint | |
18 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tk3d.c,v 1.30 92/06/15 14:28:18 ouster Exp $ SPRITE (Berkeley)"; | |
19 | #endif | |
20 | ||
21 | #include "tkconfig.h" | |
22 | #include "tk.h" | |
23 | ||
24 | /* | |
25 | * One of the following data structures is allocated for | |
26 | * each 3-D border currently in use. Structures of this | |
27 | * type are indexed by borderTable, so that a single | |
28 | * structure can be shared for several uses. | |
29 | */ | |
30 | ||
31 | typedef struct { | |
32 | Display *display; /* Display for which the resources | |
33 | * below are allocated. */ | |
34 | int refCount; /* Number of different users of | |
35 | * this border. */ | |
36 | XColor *bgColorPtr; /* Background color (intensity | |
37 | * between lightColorPtr and | |
38 | * darkColorPtr). */ | |
39 | XColor *lightColorPtr; /* Color used for lighter areas of | |
40 | * border (must free this when | |
41 | * deleting structure). */ | |
42 | XColor *darkColorPtr; /* Color for darker areas (must | |
43 | * free when deleting structure). */ | |
44 | Pixmap shadow; /* Stipple pattern to use for drawing | |
45 | * lighter-shadow-ed areas. Only used on | |
46 | * monochrome displays; on color displays | |
47 | * this is None. */ | |
48 | GC lightGC; /* Used to draw lighter parts of | |
49 | * the border. */ | |
50 | GC darkGC; /* Used to draw darker parts of the | |
51 | * border. */ | |
52 | GC bgGC; /* Used (if necessary) to draw areas in | |
53 | * the background color. */ | |
54 | Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in | |
55 | * order to delete structure). */ | |
56 | } Border; | |
57 | ||
58 | /* | |
59 | * Hash table to map from a border's values (color, etc.) to a | |
60 | * Border structure for those values. | |
61 | */ | |
62 | ||
63 | static Tcl_HashTable borderTable; | |
64 | typedef struct { | |
65 | Tk_Uid colorName; /* Color for border. */ | |
66 | Colormap colormap; /* Colormap used for allocating border | |
67 | * colors. */ | |
68 | Screen *screen; /* Screen on which border will be drawn. */ | |
69 | } BorderKey; | |
70 | ||
71 | /* | |
72 | * Maximum intensity for a color: | |
73 | */ | |
74 | ||
75 | #define MAX_INTENSITY 65535 | |
76 | ||
77 | ||
78 | static int initialized = 0; /* 0 means static structures haven't | |
79 | * been initialized yet. */ | |
80 | ||
81 | /* | |
82 | * Forward declarations for procedures defined in this file: | |
83 | */ | |
84 | ||
85 | static void BorderInit _ANSI_ARGS_((void)); | |
86 | static int Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr, | |
87 | XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr)); | |
88 | static void ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr, | |
89 | int distance, XPoint *p3Ptr)); | |
90 | \f | |
91 | /* | |
92 | *-------------------------------------------------------------- | |
93 | * | |
94 | * Tk_Get3DBorder -- | |
95 | * | |
96 | * Create a data structure for displaying a 3-D border. | |
97 | * | |
98 | * Results: | |
99 | * The return value is a token for a data structure | |
100 | * describing a 3-D border. This token may be passed | |
101 | * to Tk_Draw3DRectangle and Tk_Free3DBorder. If an | |
102 | * error prevented the border from being created then | |
103 | * NULL is returned and an error message will be left | |
104 | * in interp->result. | |
105 | * | |
106 | * Side effects: | |
107 | * Data structures, graphics contexts, etc. are allocated. | |
108 | * It is the caller's responsibility to eventually call | |
109 | * Tk_Free3DBorder to release the resources. | |
110 | * | |
111 | *-------------------------------------------------------------- | |
112 | */ | |
113 | ||
114 | Tk_3DBorder | |
115 | Tk_Get3DBorder(interp, tkwin, colormap, colorName) | |
116 | Tcl_Interp *interp; /* Place to store an error message. */ | |
117 | Tk_Window tkwin; /* Token for window in which | |
118 | * border will be drawn. */ | |
119 | Colormap colormap; /* Colormap to use for allocating border | |
120 | * colors. None means use default colormap | |
121 | * for screen. */ | |
122 | Tk_Uid colorName; /* String giving name of color | |
123 | * for window background. */ | |
124 | { | |
125 | BorderKey key; | |
126 | Tcl_HashEntry *hashPtr; | |
127 | register Border *borderPtr; | |
128 | int new; | |
129 | unsigned long light, dark; | |
130 | XGCValues gcValues; | |
131 | unsigned long mask; | |
132 | ||
133 | if (!initialized) { | |
134 | BorderInit(); | |
135 | } | |
136 | ||
137 | /* | |
138 | * First, check to see if there's already a border that will work | |
139 | * for this request. | |
140 | */ | |
141 | ||
142 | key.colorName = colorName; | |
143 | if (colormap == None) { | |
144 | colormap = Tk_DefaultColormap(Tk_Screen(tkwin)); | |
145 | } | |
146 | key.colormap = colormap; | |
147 | key.screen = Tk_Screen(tkwin); | |
148 | ||
149 | hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &new); | |
150 | if (!new) { | |
151 | borderPtr = (Border *) Tcl_GetHashValue(hashPtr); | |
152 | borderPtr->refCount++; | |
153 | } else { | |
154 | ||
155 | /* | |
156 | * No satisfactory border exists yet. Initialize a new one. | |
157 | */ | |
158 | ||
159 | borderPtr = (Border *) ckalloc(sizeof(Border)); | |
160 | borderPtr->display = Tk_Display(tkwin); | |
161 | borderPtr->refCount = 1; | |
162 | borderPtr->bgColorPtr = NULL; | |
163 | borderPtr->lightColorPtr = NULL; | |
164 | borderPtr->darkColorPtr = NULL; | |
165 | borderPtr->shadow = None; | |
166 | borderPtr->lightGC = None; | |
167 | borderPtr->darkGC = None; | |
168 | borderPtr->bgGC = None; | |
169 | borderPtr->hashPtr = hashPtr; | |
170 | Tcl_SetHashValue(hashPtr, borderPtr); | |
171 | ||
172 | /* | |
173 | * Figure out what colors and GC's to use for the light | |
174 | * and dark areas and set up the graphics contexts. | |
175 | * Monochrome displays get handled differently than | |
176 | * color displays. | |
177 | */ | |
178 | ||
179 | borderPtr->bgColorPtr = Tk_GetColor(interp, tkwin, | |
180 | key.colormap, colorName); | |
181 | if (borderPtr->bgColorPtr == NULL) { | |
182 | goto error; | |
183 | } | |
184 | if (Tk_DefaultDepth(Tk_Screen(tkwin)) == 1) { | |
185 | /* | |
186 | * Monochrome display. | |
187 | */ | |
188 | ||
189 | light = borderPtr->bgColorPtr->pixel; | |
190 | if (light == WhitePixelOfScreen(Tk_Screen(tkwin))) { | |
191 | dark = BlackPixelOfScreen(Tk_Screen(tkwin)); | |
192 | } else { | |
193 | dark = WhitePixelOfScreen(Tk_Screen(tkwin)); | |
194 | } | |
195 | borderPtr->shadow = Tk_GetBitmap(interp, tkwin, | |
196 | Tk_GetUid("gray50")); | |
197 | if (borderPtr->shadow == None) { | |
198 | goto error; | |
199 | } | |
200 | } else { | |
201 | XColor lightColor, darkColor; | |
202 | int tmp; | |
203 | ||
204 | /* | |
205 | * Color display. Compute the colors for the illuminated | |
206 | * and shaded portions of the border. | |
207 | */ | |
208 | ||
209 | tmp = (14*(int)borderPtr->bgColorPtr->red)/10; | |
210 | if (tmp > MAX_INTENSITY) { | |
211 | tmp = MAX_INTENSITY; | |
212 | } | |
213 | lightColor.red = tmp; | |
214 | tmp = (14*(int)borderPtr->bgColorPtr->green)/10; | |
215 | if (tmp > MAX_INTENSITY) { | |
216 | tmp = MAX_INTENSITY; | |
217 | } | |
218 | lightColor.green = tmp; | |
219 | tmp = (14*(int)borderPtr->bgColorPtr->blue)/10; | |
220 | if (tmp > MAX_INTENSITY) { | |
221 | tmp = MAX_INTENSITY; | |
222 | } | |
223 | lightColor.blue = tmp; | |
224 | darkColor.red = (60*(int)borderPtr->bgColorPtr->red)/100; | |
225 | darkColor.green = (60*(int)borderPtr->bgColorPtr->green)/100; | |
226 | darkColor.blue = (60*(int)borderPtr->bgColorPtr->blue)/100; | |
227 | borderPtr->lightColorPtr = Tk_GetColorByValue(interp, tkwin, | |
228 | key.colormap, &lightColor); | |
229 | if (borderPtr->lightColorPtr == NULL) { | |
230 | goto error; | |
231 | } | |
232 | borderPtr->darkColorPtr = Tk_GetColorByValue(interp, tkwin, | |
233 | key.colormap, &darkColor); | |
234 | if (borderPtr->darkColorPtr == NULL) { | |
235 | goto error; | |
236 | } | |
237 | light = borderPtr->lightColorPtr->pixel; | |
238 | dark = borderPtr->darkColorPtr->pixel; | |
239 | } | |
240 | gcValues.foreground = light; | |
241 | gcValues.background = dark; | |
242 | mask = GCForeground|GCBackground; | |
243 | if (borderPtr->shadow != None) { | |
244 | gcValues.stipple = borderPtr->shadow; | |
245 | gcValues.fill_style = FillOpaqueStippled; | |
246 | mask |= GCStipple|GCFillStyle; | |
247 | } | |
248 | borderPtr->lightGC = Tk_GetGC(tkwin, mask, &gcValues); | |
249 | gcValues.foreground = dark; | |
250 | gcValues.background = light; | |
251 | borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground|GCBackground, | |
252 | &gcValues); | |
253 | gcValues.foreground = borderPtr->bgColorPtr->pixel; | |
254 | borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues); | |
255 | } | |
256 | return (Tk_3DBorder) borderPtr; | |
257 | ||
258 | error: | |
259 | Tk_Free3DBorder((Tk_3DBorder) borderPtr); | |
260 | return NULL; | |
261 | } | |
262 | \f | |
263 | /* | |
264 | *-------------------------------------------------------------- | |
265 | * | |
266 | * Tk_Draw3DRectangle -- | |
267 | * | |
268 | * Draw a 3-D border at a given place in a given window. | |
269 | * | |
270 | * Results: | |
271 | * None. | |
272 | * | |
273 | * Side effects: | |
274 | * A 3-D border will be drawn in the indicated drawable. | |
275 | * The outside edges of the border will be determined by x, | |
276 | * y, width, and height. The inside edges of the border | |
277 | * will be determined by the borderWidth argument. | |
278 | * | |
279 | *-------------------------------------------------------------- | |
280 | */ | |
281 | ||
282 | void | |
283 | Tk_Draw3DRectangle(display, drawable, border, x, y, width, height, | |
284 | borderWidth, relief) | |
285 | Display *display; /* X display in which to draw. */ | |
286 | Drawable drawable; /* X window or pixmap in which to draw. */ | |
287 | Tk_3DBorder border; /* Token for border to draw. */ | |
288 | int x, y, width, height; /* Outside area of region in | |
289 | * which border will be drawn. */ | |
290 | int borderWidth; /* Desired width for border, in | |
291 | * pixels. */ | |
292 | int relief; /* Should be either TK_RELIEF_RAISED | |
293 | * or TK_RELIEF_SUNKEN; indicates | |
294 | * position of interior of window relative | |
295 | * to exterior. */ | |
296 | { | |
297 | register Border *borderPtr = (Border *) border; | |
298 | GC top, bottom; | |
299 | XPoint points[7]; | |
300 | ||
301 | if ((width < 2*borderWidth) || (height < 2*borderWidth)) { | |
302 | return; | |
303 | } | |
304 | ||
305 | if (relief == TK_RELIEF_RAISED) { | |
306 | top = borderPtr->lightGC; | |
307 | bottom = borderPtr->darkGC; | |
308 | } else if (relief == TK_RELIEF_SUNKEN) { | |
309 | top = borderPtr->darkGC; | |
310 | bottom = borderPtr->lightGC; | |
311 | } else { | |
312 | top = bottom = borderPtr->bgGC; | |
313 | } | |
314 | XFillRectangle(display, drawable, bottom, x, y+height-borderWidth, | |
315 | ||
316 | (unsigned int) width, (unsigned int) borderWidth); | |
317 | XFillRectangle(display, drawable, bottom, x+width-borderWidth, y, | |
318 | (unsigned int) borderWidth, (unsigned int) height); | |
319 | points[0].x = points[1].x = points[6].x = x; | |
320 | points[0].y = points[6].y = y + height; | |
321 | points[1].y = points[2].y = y; | |
322 | points[2].x = x + width; | |
323 | points[3].x = x + width - borderWidth; | |
324 | points[3].y = points[4].y = y + borderWidth; | |
325 | points[4].x = points[5].x = x + borderWidth; | |
326 | points[5].y = y + height - borderWidth; | |
327 | XFillPolygon(display, drawable, top, points, 7, Nonconvex, | |
328 | CoordModeOrigin); | |
329 | } | |
330 | \f | |
331 | /* | |
332 | *-------------------------------------------------------------- | |
333 | * | |
334 | * Tk_NameOf3DBorder -- | |
335 | * | |
336 | * Given a border, return a textual string identifying the | |
337 | * border's color. | |
338 | * | |
339 | * Results: | |
340 | * The return value is the string that was used to create | |
341 | * the border. | |
342 | * | |
343 | * Side effects: | |
344 | * None. | |
345 | * | |
346 | *-------------------------------------------------------------- | |
347 | */ | |
348 | ||
349 | char * | |
350 | Tk_NameOf3DBorder(border) | |
351 | Tk_3DBorder border; /* Token for border. */ | |
352 | { | |
353 | Border *borderPtr = (Border *) border; | |
354 | ||
355 | return ((BorderKey *) borderPtr->hashPtr->key.words)->colorName; | |
356 | } | |
357 | \f | |
358 | /* | |
359 | *-------------------------------------------------------------------- | |
360 | * | |
361 | * Tk_3DBorderColor -- | |
362 | * | |
363 | * Given a 3D border, return the X color used for the "flat" | |
364 | * surfaces. | |
365 | * | |
366 | * Results: | |
367 | * Returns the color used drawing flat surfaces with the border. | |
368 | * | |
369 | * Side effects: | |
370 | * None. | |
371 | * | |
372 | *-------------------------------------------------------------------- | |
373 | */ | |
374 | XColor * | |
375 | Tk_3DBorderColor(border) | |
376 | Tk_3DBorder border; | |
377 | { | |
378 | return(((Border *) border)->bgColorPtr); | |
379 | } | |
380 | \f | |
381 | /* | |
382 | *-------------------------------------------------------------- | |
383 | * | |
384 | * Tk_Free3DBorder -- | |
385 | * | |
386 | * This procedure is called when a 3D border is no longer | |
387 | * needed. It frees the resources associated with the | |
388 | * border. After this call, the caller should never again | |
389 | * use the "border" token. | |
390 | * | |
391 | * Results: | |
392 | * None. | |
393 | * | |
394 | * Side effects: | |
395 | * Resources are freed. | |
396 | * | |
397 | *-------------------------------------------------------------- | |
398 | */ | |
399 | ||
400 | void | |
401 | Tk_Free3DBorder(border) | |
402 | Tk_3DBorder border; /* Token for border to be released. */ | |
403 | { | |
404 | register Border *borderPtr = (Border *) border; | |
405 | ||
406 | borderPtr->refCount--; | |
407 | if (borderPtr->refCount == 0) { | |
408 | if (borderPtr->bgColorPtr != NULL) { | |
409 | Tk_FreeColor(borderPtr->bgColorPtr); | |
410 | } | |
411 | if (borderPtr->lightColorPtr != NULL) { | |
412 | Tk_FreeColor(borderPtr->lightColorPtr); | |
413 | } | |
414 | if (borderPtr->darkColorPtr != NULL) { | |
415 | Tk_FreeColor(borderPtr->darkColorPtr); | |
416 | } | |
417 | if (borderPtr->shadow != None) { | |
418 | Tk_FreeBitmap(borderPtr->shadow); | |
419 | } | |
420 | if (borderPtr->lightGC != None) { | |
421 | Tk_FreeGC(borderPtr->lightGC); | |
422 | } | |
423 | if (borderPtr->darkGC != None) { | |
424 | Tk_FreeGC(borderPtr->darkGC); | |
425 | } | |
426 | if (borderPtr->bgGC != None) { | |
427 | Tk_FreeGC(borderPtr->bgGC); | |
428 | } | |
429 | Tcl_DeleteHashEntry(borderPtr->hashPtr); | |
430 | ckfree((char *) borderPtr); | |
431 | } | |
432 | } | |
433 | \f | |
434 | /* | |
435 | *---------------------------------------------------------------------- | |
436 | * | |
437 | * Tk_SetBackgroundFromBorder -- | |
438 | * | |
439 | * Change the background of a window to one appropriate for a given | |
440 | * 3-D border. | |
441 | * | |
442 | * Results: | |
443 | * None. | |
444 | * | |
445 | * Side effects: | |
446 | * Tkwin's background gets modified. | |
447 | * | |
448 | *---------------------------------------------------------------------- | |
449 | */ | |
450 | ||
451 | void | |
452 | Tk_SetBackgroundFromBorder(tkwin, border) | |
453 | Tk_Window tkwin; /* Window whose background is to be set. */ | |
454 | Tk_3DBorder border; /* Token for border. */ | |
455 | { | |
456 | register Border *borderPtr = (Border *) border; | |
457 | ||
458 | Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel); | |
459 | } | |
460 | \f | |
461 | /* | |
462 | *---------------------------------------------------------------------- | |
463 | * | |
464 | * Tk_GetRelief -- | |
465 | * | |
466 | * Parse a relief description and return the corresponding | |
467 | * relief value, or an error. | |
468 | * | |
469 | * Results: | |
470 | * A standard Tcl return value. If all goes well then | |
471 | * *reliefPtr is filled in with one of the values | |
472 | * TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN. | |
473 | * | |
474 | * Side effects: | |
475 | * None. | |
476 | * | |
477 | *---------------------------------------------------------------------- | |
478 | */ | |
479 | ||
480 | int | |
481 | Tk_GetRelief(interp, name, reliefPtr) | |
482 | Tcl_Interp *interp; /* For error messages. */ | |
483 | char *name; /* Name of a relief type. */ | |
484 | int *reliefPtr; /* Where to store converted relief. */ | |
485 | { | |
486 | char c; | |
487 | int length; | |
488 | ||
489 | c = name[0]; | |
490 | length = strlen(name); | |
491 | if ((c == 'f') && (strncmp(name, "flat", length) == 0)) { | |
492 | *reliefPtr = TK_RELIEF_FLAT; | |
493 | } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)) { | |
494 | *reliefPtr = TK_RELIEF_RAISED; | |
495 | } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) { | |
496 | *reliefPtr = TK_RELIEF_SUNKEN; | |
497 | } else { | |
498 | sprintf(interp->result, "bad relief type \"%.50s\": must be %s", | |
499 | name, "flat, raised, or sunken"); | |
500 | return TCL_ERROR; | |
501 | } | |
502 | return TCL_OK; | |
503 | } | |
504 | \f | |
505 | /* | |
506 | *-------------------------------------------------------------- | |
507 | * | |
508 | * Tk_NameOfRelief -- | |
509 | * | |
510 | * Given a relief value, produce a string describing that | |
511 | * relief value. | |
512 | * | |
513 | * Results: | |
514 | * The return value is a static string that is equivalent | |
515 | * to relief. | |
516 | * | |
517 | * Side effects: | |
518 | * None. | |
519 | * | |
520 | *-------------------------------------------------------------- | |
521 | */ | |
522 | ||
523 | char * | |
524 | Tk_NameOfRelief(relief) | |
525 | int relief; /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED, | |
526 | * or TK_RELIEF_SUNKEN. */ | |
527 | { | |
528 | if (relief == TK_RELIEF_FLAT) { | |
529 | return "flat"; | |
530 | } else if (relief == TK_RELIEF_SUNKEN) { | |
531 | return "sunken"; | |
532 | } else if (relief == TK_RELIEF_RAISED) { | |
533 | return "raised"; | |
534 | } else { | |
535 | return "unknown relief"; | |
536 | } | |
537 | } | |
538 | \f | |
539 | /* | |
540 | *-------------------------------------------------------------- | |
541 | * | |
542 | * Tk_Draw3DPolygon -- | |
543 | * | |
544 | * Draw a border with 3-D appearance around the edge of a | |
545 | * given polygon. | |
546 | * | |
547 | * Results: | |
548 | * None. | |
549 | * | |
550 | * Side effects: | |
551 | * Information is drawn in "drawable" in the form of a | |
552 | * 3-D border borderWidth units width wide on the left | |
553 | * of the trajectory given by pointPtr and numPoints (or | |
554 | * -borderWidth units wide on the right side, if borderWidth | |
555 | * is negative. | |
556 | * | |
557 | *-------------------------------------------------------------- | |
558 | */ | |
559 | ||
560 | void | |
561 | Tk_Draw3DPolygon(display, drawable, border, pointPtr, numPoints, | |
562 | borderWidth, leftRelief) | |
563 | Display *display; /* X display in which to draw polygon. */ | |
564 | Drawable drawable; /* X window or pixmap in which to draw. */ | |
565 | Tk_3DBorder border; /* Token for border to draw. */ | |
566 | XPoint *pointPtr; /* Array of points describing | |
567 | * polygon. All points must be | |
568 | * absolute (CoordModeOrigin). */ | |
569 | int numPoints; /* Number of points at *pointPtr. */ | |
570 | int borderWidth; /* Width of border, measured in | |
571 | * pixels to the left of the polygon's | |
572 | * trajectory. May be negative. */ | |
573 | int leftRelief; /* TK_RELIEF_RAISED or | |
574 | * TK_RELIEF_SUNKEN: indicates how | |
575 | * stuff to left of trajectory looks | |
576 | * relative to stuff on right. */ | |
577 | { | |
578 | XPoint poly[4], b1, b2, newB1, newB2; | |
579 | XPoint perp, c, shift1, shift2; /* Used for handling parallel lines. */ | |
580 | register XPoint *p1Ptr, *p2Ptr; | |
581 | Border *borderPtr = (Border *) border; | |
582 | GC gc; | |
583 | int i, lightOnLeft, dx, dy, parallel, pointsSeen; | |
584 | ||
585 | /* | |
586 | * If the polygon is already closed, drop the last point from it | |
587 | * (we'll close it automatically). | |
588 | */ | |
589 | ||
590 | p1Ptr = &pointPtr[numPoints-1]; | |
591 | p2Ptr = &pointPtr[0]; | |
592 | if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) { | |
593 | numPoints--; | |
594 | } | |
595 | ||
596 | /* | |
597 | * The loop below is executed once for each vertex in the polgon. | |
598 | * At the beginning of each iteration things look like this: | |
599 | * | |
600 | * poly[1] / | |
601 | * * / | |
602 | * | / | |
603 | * b1 * poly[0] (pointPtr[i-1]) | |
604 | * | | | |
605 | * | | | |
606 | * | | | |
607 | * | | | |
608 | * | | | |
609 | * | | *p1Ptr *p2Ptr | |
610 | * b2 *--------------------* | |
611 | * | | |
612 | * | | |
613 | * x------------------------- | |
614 | * | |
615 | * The job of this iteration is to do the following: | |
616 | * (a) Compute x (the border corner corresponding to | |
617 | * pointPtr[i]) and put it in poly[2]. As part of | |
618 | * this, compute a new b1 and b2 value for the next | |
619 | * side of the polygon. | |
620 | * (b) Put pointPtr[i] into poly[3]. | |
621 | * (c) Draw the polygon given by poly[0..3]. | |
622 | * (d) Advance poly[0], poly[1], b1, and b2 for the | |
623 | * next side of the polygon. | |
624 | */ | |
625 | ||
626 | /* | |
627 | * The above situation doesn't first come into existence until | |
628 | * two points have been processed; the first two points are | |
629 | * used to "prime the pump", so some parts of the processing | |
630 | * are ommitted for these points. The variable "pointsSeen" | |
631 | * keeps track of the priming process; it has to be separate | |
632 | * from i in order to be able to ignore duplicate points in the | |
633 | * polygon. | |
634 | */ | |
635 | ||
636 | pointsSeen = 0; | |
637 | for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1; | |
638 | i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) { | |
639 | if ((i == -1) || (i == numPoints-1)) { | |
640 | p2Ptr = pointPtr; | |
641 | } | |
642 | if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) { | |
643 | /* | |
644 | * Ignore duplicate points (they'd cause core dumps in | |
645 | * ShiftLine calls below). | |
646 | */ | |
647 | continue; | |
648 | } | |
649 | ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1); | |
650 | newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x); | |
651 | newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y); | |
652 | poly[3] = *p1Ptr; | |
653 | parallel = 0; | |
654 | if (pointsSeen >= 1) { | |
655 | parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]); | |
656 | ||
657 | /* | |
658 | * If two consecutive segments of the polygon are parallel, | |
659 | * then things get more complex. Consider the following | |
660 | * diagram: | |
661 | * | |
662 | * poly[1] | |
663 | * *----b1-----------b2------a | |
664 | * \ | |
665 | * \ | |
666 | * *---------*----------* b | |
667 | * poly[0] *p2Ptr *p1Ptr / | |
668 | * / | |
669 | * --*--------*----c | |
670 | * newB1 newB2 | |
671 | * | |
672 | * Instead of using x and *p1Ptr for poly[2] and poly[3], as | |
673 | * in the original diagram, use a and b as above. Then instead | |
674 | * of using x and *p1Ptr for the new poly[0] and poly[1], use | |
675 | * b and c as above. | |
676 | * | |
677 | * Do the computation in three stages: | |
678 | * 1. Compute a point "perp" such that the line p1Ptr-perp | |
679 | * is perpendicular to p1Ptr-p2Ptr. | |
680 | * 2. Compute the points a and c by intersecting the lines | |
681 | * b1-b2 and newB1-newB2 with p1Ptr-perp. | |
682 | * 3. Compute b by shifting p1Ptr-perp to the right and | |
683 | * intersecting it with p1Ptr-p2Ptr. | |
684 | */ | |
685 | ||
686 | if (parallel) { | |
687 | perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y); | |
688 | perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x); | |
689 | (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]); | |
690 | (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c); | |
691 | ShiftLine(p1Ptr, &perp, borderWidth, &shift1); | |
692 | shift2.x = shift1.x + (perp.x - p1Ptr->x); | |
693 | shift2.y = shift1.y + (perp.y - p1Ptr->y); | |
694 | (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]); | |
695 | } | |
696 | } | |
697 | if (pointsSeen >= 2) { | |
698 | dx = poly[3].x - poly[0].x; | |
699 | dy = poly[3].y - poly[0].y; | |
700 | if (dx > 0) { | |
701 | lightOnLeft = (dy <= dx); | |
702 | } else { | |
703 | lightOnLeft = (dy < dx); | |
704 | } | |
705 | if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) { | |
706 | gc = borderPtr->lightGC; | |
707 | } else { | |
708 | gc = borderPtr->darkGC; | |
709 | } | |
710 | XFillPolygon(display, drawable, gc, poly, 4, Convex, | |
711 | CoordModeOrigin); | |
712 | } | |
713 | b1.x = newB1.x; | |
714 | b1.y = newB1.y; | |
715 | b2.x = newB2.x; | |
716 | b2.y = newB2.y; | |
717 | poly[0].x = poly[3].x; | |
718 | poly[0].y = poly[3].y; | |
719 | if (parallel) { | |
720 | poly[1].x = c.x; | |
721 | poly[1].y = c.y; | |
722 | } else if (pointsSeen >= 1) { | |
723 | poly[1].x = poly[2].x; | |
724 | poly[1].y = poly[2].y; | |
725 | } | |
726 | pointsSeen++; | |
727 | } | |
728 | } | |
729 | \f | |
730 | /* | |
731 | *---------------------------------------------------------------------- | |
732 | * | |
733 | * Tk_Fill3DRectangle -- | |
734 | * | |
735 | * Fill a rectangular area, supplying a 3D border if desired. | |
736 | * | |
737 | * Results: | |
738 | * None. | |
739 | * | |
740 | * Side effects: | |
741 | * Information gets drawn on the screen. | |
742 | * | |
743 | *---------------------------------------------------------------------- | |
744 | */ | |
745 | ||
746 | void | |
747 | Tk_Fill3DRectangle(display, drawable, border, x, y, width, | |
748 | height, borderWidth, relief) | |
749 | Display *display; /* X display in which to draw rectangle. */ | |
750 | Drawable drawable; /* X window or pixmap in which to draw. */ | |
751 | Tk_3DBorder border; /* Token for border to draw. */ | |
752 | int x, y, width, height; /* Outside area of rectangular region. */ | |
753 | int borderWidth; /* Desired width for border, in | |
754 | * pixels. Border will be *inside* region. */ | |
755 | int relief; /* Indicates 3D effect: TK_RELIEF_FLAT, | |
756 | * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */ | |
757 | { | |
758 | register Border *borderPtr = (Border *) border; | |
759 | ||
760 | XFillRectangle(display, drawable, borderPtr->bgGC, | |
761 | x, y, (unsigned int) width, (unsigned int) height); | |
762 | if (relief != TK_RELIEF_FLAT) { | |
763 | Tk_Draw3DRectangle(display, drawable, border, x, y, width, | |
764 | height, borderWidth, relief); | |
765 | } | |
766 | } | |
767 | \f | |
768 | /* | |
769 | *---------------------------------------------------------------------- | |
770 | * | |
771 | * Tk_Fill3DPolygon -- | |
772 | * | |
773 | * Fill a polygonal area, supplying a 3D border if desired. | |
774 | * | |
775 | * Results: | |
776 | * None. | |
777 | * | |
778 | * Side effects: | |
779 | * Information gets drawn on the screen. | |
780 | * | |
781 | *---------------------------------------------------------------------- | |
782 | */ | |
783 | ||
784 | void | |
785 | Tk_Fill3DPolygon(display, drawable, border, pointPtr, numPoints, | |
786 | borderWidth, leftRelief) | |
787 | Display *display; /* X display in which to draw polygon. */ | |
788 | Drawable drawable; /* X window or pixmap in which to draw. */ | |
789 | Tk_3DBorder border; /* Token for border to draw. */ | |
790 | XPoint *pointPtr; /* Array of points describing | |
791 | * polygon. All points must be | |
792 | * absolute (CoordModeOrigin). */ | |
793 | int numPoints; /* Number of points at *pointPtr. */ | |
794 | int borderWidth; /* Width of border, measured in | |
795 | * pixels to the left of the polygon's | |
796 | * trajectory. May be negative. */ | |
797 | int leftRelief; /* Indicates 3D effect of left side of | |
798 | * trajectory relative to right: | |
799 | * TK_RELIEF_FLAT, TK_RELIEF_RAISED, | |
800 | * or TK_RELIEF_SUNKEN. */ | |
801 | { | |
802 | register Border *borderPtr = (Border *) border; | |
803 | ||
804 | XFillPolygon(display, drawable, borderPtr->bgGC, | |
805 | pointPtr, numPoints, Complex, CoordModeOrigin); | |
806 | if (leftRelief != TK_RELIEF_FLAT) { | |
807 | Tk_Draw3DPolygon(display, drawable, border, pointPtr, numPoints, | |
808 | borderWidth, leftRelief); | |
809 | } | |
810 | } | |
811 | \f | |
812 | /* | |
813 | *-------------------------------------------------------------- | |
814 | * | |
815 | * BorderInit -- | |
816 | * | |
817 | * Initialize the structures used for border management. | |
818 | * | |
819 | * Results: | |
820 | * None. | |
821 | * | |
822 | * Side effects: | |
823 | * Read the code. | |
824 | * | |
825 | *------------------------------------------------------------- | |
826 | */ | |
827 | ||
828 | static void | |
829 | BorderInit() | |
830 | { | |
831 | initialized = 1; | |
832 | Tcl_InitHashTable(&borderTable, sizeof(BorderKey)/sizeof(int)); | |
833 | } | |
834 | \f | |
835 | /* | |
836 | *-------------------------------------------------------------- | |
837 | * | |
838 | * ShiftLine -- | |
839 | * | |
840 | * Given two points on a line, compute a point on a | |
841 | * new line that is parallel to the given line and | |
842 | * a given distance away from it. | |
843 | * | |
844 | * Results: | |
845 | * None. | |
846 | * | |
847 | * Side effects: | |
848 | * None. | |
849 | * | |
850 | *-------------------------------------------------------------- | |
851 | */ | |
852 | ||
853 | static void | |
854 | ShiftLine(p1Ptr, p2Ptr, distance, p3Ptr) | |
855 | XPoint *p1Ptr; /* First point on line. */ | |
856 | XPoint *p2Ptr; /* Second point on line. */ | |
857 | int distance; /* New line is to be this many | |
858 | * units to the left of original | |
859 | * line, when looking from p1 to | |
860 | * p2. May be negative. */ | |
861 | XPoint *p3Ptr; /* Store coords of point on new | |
862 | * line here. */ | |
863 | { | |
864 | int dx, dy, dxNeg, dyNeg; | |
865 | ||
866 | /* | |
867 | * The table below is used for a quick approximation in | |
868 | * computing the new point. An index into the table | |
869 | * is 128 times the slope of the original line (the slope | |
870 | * must always be between 0 and 1). The value of the table | |
871 | * entry is 128 times the amount to displace the new line | |
872 | * in y for each unit of perpendicular distance. In other | |
873 | * words, the table maps from the tangent of an angle to | |
874 | * the inverse of its cosine. If the slope of the original | |
875 | * line is greater than 1, then the displacement is done in | |
876 | * x rather than in y. | |
877 | */ | |
878 | ||
879 | static int shiftTable[129]; | |
880 | ||
881 | /* | |
882 | * Initialize the table if this is the first time it is | |
883 | * used. | |
884 | */ | |
885 | ||
886 | if (shiftTable[0] == 0) { | |
887 | int i; | |
888 | double tangent, cosine; | |
889 | ||
890 | for (i = 0; i <= 128; i++) { | |
891 | tangent = i/128.0; | |
892 | cosine = 128/cos(atan(tangent)) + .5; | |
893 | shiftTable[i] = cosine; | |
894 | } | |
895 | } | |
896 | ||
897 | *p3Ptr = *p1Ptr; | |
898 | dx = p2Ptr->x - p1Ptr->x; | |
899 | dy = p2Ptr->y - p1Ptr->y; | |
900 | if (dy < 0) { | |
901 | dyNeg = 1; | |
902 | dy = -dy; | |
903 | } else { | |
904 | dyNeg = 0; | |
905 | } | |
906 | if (dx < 0) { | |
907 | dxNeg = 1; | |
908 | dx = -dx; | |
909 | } else { | |
910 | dxNeg = 0; | |
911 | } | |
912 | if (dy <= dx) { | |
913 | dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7; | |
914 | if (!dxNeg) { | |
915 | dy = -dy; | |
916 | } | |
917 | p3Ptr->y += dy; | |
918 | } else { | |
919 | dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7; | |
920 | if (dyNeg) { | |
921 | dx = -dx; | |
922 | } | |
923 | p3Ptr->x += dx; | |
924 | } | |
925 | } | |
926 | \f | |
927 | /* | |
928 | *-------------------------------------------------------------- | |
929 | * | |
930 | * Intersect -- | |
931 | * | |
932 | * Find the intersection point between two lines. | |
933 | * | |
934 | * Results: | |
935 | * Under normal conditions 0 is returned and the point | |
936 | * at *iPtr is filled in with the intersection between | |
937 | * the two lines. If the two lines are parallel, then | |
938 | * -1 is returned and *iPtr isn't modified. | |
939 | * | |
940 | * Side effects: | |
941 | * None. | |
942 | * | |
943 | *-------------------------------------------------------------- | |
944 | */ | |
945 | ||
946 | static int | |
947 | Intersect(a1Ptr, a2Ptr, b1Ptr, b2Ptr, iPtr) | |
948 | XPoint *a1Ptr; /* First point of first line. */ | |
949 | XPoint *a2Ptr; /* Second point of first line. */ | |
950 | XPoint *b1Ptr; /* First point of second line. */ | |
951 | XPoint *b2Ptr; /* Second point of second line. */ | |
952 | XPoint *iPtr; /* Filled in with intersection point. */ | |
953 | { | |
954 | int dxadyb, dxbdya, dxadxb, dyadyb, p, q; | |
955 | ||
956 | /* | |
957 | * The code below is just a straightforward manipulation of two | |
958 | * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve | |
959 | * for the x-coordinate of intersection, then the y-coordinate. | |
960 | */ | |
961 | ||
962 | dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y); | |
963 | dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y); | |
964 | dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x); | |
965 | dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y); | |
966 | ||
967 | if (dxadyb == dxbdya) { | |
968 | return -1; | |
969 | } | |
970 | p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb); | |
971 | q = dxbdya - dxadyb; | |
972 | if (q < 0) { | |
973 | p = -p; | |
974 | q = -q; | |
975 | } | |
976 | if (p < 0) { | |
977 | iPtr->x = - ((-p + q/2)/q); | |
978 | } else { | |
979 | iPtr->x = (p + q/2)/q; | |
980 | } | |
981 | p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb); | |
982 | q = dxadyb - dxbdya; | |
983 | if (q < 0) { | |
984 | p = -p; | |
985 | q = -q; | |
986 | } | |
987 | if (p < 0) { | |
988 | iPtr->y = - ((-p + q/2)/q); | |
989 | } else { | |
990 | iPtr->y = (p + q/2)/q; | |
991 | } | |
992 | return 0; | |
993 | } |