4  *      This module provides procedures to draw borders in 
   5  *      the three-dimensional Motif style. 
   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. 
  18 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tk3d.c,v 1.30 92/06/15 14:28:18 ouster Exp $ SPRITE (Berkeley)"; 
  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. 
  32     Display 
*display
;           /* Display for which the resources 
  33                                  * below are allocated. */ 
  34     int refCount
;               /* Number of different users of 
  36     XColor 
*bgColorPtr
;         /* Background color (intensity 
  37                                  * between lightColorPtr and 
  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 
  48     GC lightGC
;                 /* Used to draw lighter parts of 
  50     GC darkGC
;                  /* Used to draw darker parts of the 
  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). */ 
  59  * Hash table to map from a border's values (color, etc.) to a 
  60  * Border structure for those values. 
  63 static Tcl_HashTable borderTable
; 
  65     Tk_Uid colorName
;           /* Color for border. */ 
  66     Colormap colormap
;          /* Colormap used for allocating border 
  68     Screen 
*screen
;             /* Screen on which border will be drawn. */ 
  72  * Maximum intensity for a color: 
  75 #define MAX_INTENSITY 65535 
  78 static int initialized 
= 0;     /* 0 means static structures haven't 
  79                                  * been initialized yet. */ 
  82  * Forward declarations for procedures defined in this file: 
  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
)); 
  92  *-------------------------------------------------------------- 
  96  *      Create a data structure for displaying a 3-D border. 
  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 
 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. 
 111  *-------------------------------------------------------------- 
 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 
 122     Tk_Uid colorName
;           /* String giving name of color 
 123                                  * for window background. */ 
 126     Tcl_HashEntry 
*hashPtr
; 
 127     register Border 
*borderPtr
; 
 129     unsigned long light
, dark
; 
 138      * First, check to see if there's already a border that will work 
 142     key
.colorName 
= colorName
; 
 143     if (colormap 
== None
) { 
 144         colormap 
= Tk_DefaultColormap(Tk_Screen(tkwin
)); 
 146     key
.colormap 
= colormap
; 
 147     key
.screen 
= Tk_Screen(tkwin
); 
 149     hashPtr 
= Tcl_CreateHashEntry(&borderTable
, (char *) &key
, &new); 
 151         borderPtr 
= (Border 
*) Tcl_GetHashValue(hashPtr
); 
 152         borderPtr
->refCount
++; 
 156          * No satisfactory border exists yet.  Initialize a new one. 
 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
); 
 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 
 179         borderPtr
->bgColorPtr 
= Tk_GetColor(interp
, tkwin
, 
 180                 key
.colormap
, colorName
); 
 181         if (borderPtr
->bgColorPtr 
== NULL
) { 
 184         if (Tk_DefaultDepth(Tk_Screen(tkwin
)) == 1) { 
 186              * Monochrome display. 
 189             light 
= borderPtr
->bgColorPtr
->pixel
; 
 190             if (light 
== WhitePixelOfScreen(Tk_Screen(tkwin
))) { 
 191                 dark 
= BlackPixelOfScreen(Tk_Screen(tkwin
)); 
 193                 dark 
= WhitePixelOfScreen(Tk_Screen(tkwin
)); 
 195             borderPtr
->shadow 
= Tk_GetBitmap(interp
, tkwin
, 
 196                     Tk_GetUid("gray50")); 
 197             if (borderPtr
->shadow 
== None
) { 
 201             XColor lightColor
, darkColor
; 
 205              * Color display.  Compute the colors for the illuminated 
 206              * and shaded portions of the border. 
 209             tmp 
= (14*(int)borderPtr
->bgColorPtr
->red
)/10; 
 210             if (tmp 
> MAX_INTENSITY
) { 
 213             lightColor
.red 
= tmp
; 
 214             tmp 
= (14*(int)borderPtr
->bgColorPtr
->green
)/10; 
 215             if (tmp 
> MAX_INTENSITY
) { 
 218             lightColor
.green 
= tmp
; 
 219             tmp 
= (14*(int)borderPtr
->bgColorPtr
->blue
)/10; 
 220             if (tmp 
> MAX_INTENSITY
) { 
 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
) { 
 232             borderPtr
->darkColorPtr 
= Tk_GetColorByValue(interp
, tkwin
, 
 233                     key
.colormap
, &darkColor
); 
 234             if (borderPtr
->darkColorPtr 
== NULL
) { 
 237             light 
= borderPtr
->lightColorPtr
->pixel
; 
 238             dark 
= borderPtr
->darkColorPtr
->pixel
; 
 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
; 
 248         borderPtr
->lightGC 
= Tk_GetGC(tkwin
, mask
, &gcValues
); 
 249         gcValues
.foreground 
= dark
; 
 250         gcValues
.background 
= light
; 
 251         borderPtr
->darkGC 
= Tk_GetGC(tkwin
, GCForeground
|GCBackground
, 
 253         gcValues
.foreground 
= borderPtr
->bgColorPtr
->pixel
; 
 254         borderPtr
->bgGC 
= Tk_GetGC(tkwin
, GCForeground
, &gcValues
); 
 256     return (Tk_3DBorder
) borderPtr
; 
 259     Tk_Free3DBorder((Tk_3DBorder
) borderPtr
); 
 264  *-------------------------------------------------------------- 
 266  * Tk_Draw3DRectangle -- 
 268  *      Draw a 3-D border at a given place in a given window. 
 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. 
 279  *-------------------------------------------------------------- 
 283 Tk_Draw3DRectangle(display
, drawable
, border
, x
, y
, width
, height
, 
 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 
 292     int relief
;                 /* Should be either TK_RELIEF_RAISED 
 293                                  * or TK_RELIEF_SUNKEN;  indicates 
 294                                  * position of interior of window relative 
 297     register Border 
*borderPtr 
= (Border 
*) border
; 
 301     if ((width 
< 2*borderWidth
) || (height 
< 2*borderWidth
)) { 
 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
; 
 312         top 
= bottom 
= borderPtr
->bgGC
; 
 314     XFillRectangle(display
, drawable
, bottom
, x
, y
+height
-borderWidth
, 
 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
, 
 332  *-------------------------------------------------------------- 
 334  * Tk_NameOf3DBorder -- 
 336  *      Given a border, return a textual string identifying the 
 340  *      The return value is the string that was used to create 
 346  *-------------------------------------------------------------- 
 350 Tk_NameOf3DBorder(border
) 
 351     Tk_3DBorder border
;         /* Token for border. */ 
 353     Border 
*borderPtr 
= (Border 
*) border
; 
 355     return ((BorderKey 
*) borderPtr
->hashPtr
->key
.words
)->colorName
; 
 359  *-------------------------------------------------------------------- 
 361  * Tk_3DBorderColor -- 
 363  *      Given a 3D border, return the X color used for the "flat" 
 367  *      Returns the color used drawing flat surfaces with the border. 
 372  *-------------------------------------------------------------------- 
 375 Tk_3DBorderColor(border
) 
 378     return(((Border 
*) border
)->bgColorPtr
); 
 382  *-------------------------------------------------------------- 
 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. 
 395  *      Resources are freed. 
 397  *-------------------------------------------------------------- 
 401 Tk_Free3DBorder(border
) 
 402     Tk_3DBorder border
;         /* Token for border to be released. */ 
 404     register Border 
*borderPtr 
= (Border 
*) border
; 
 406     borderPtr
->refCount
--; 
 407     if (borderPtr
->refCount 
== 0) { 
 408         if (borderPtr
->bgColorPtr 
!= NULL
) { 
 409             Tk_FreeColor(borderPtr
->bgColorPtr
); 
 411         if (borderPtr
->lightColorPtr 
!= NULL
) { 
 412             Tk_FreeColor(borderPtr
->lightColorPtr
); 
 414         if (borderPtr
->darkColorPtr 
!= NULL
) { 
 415             Tk_FreeColor(borderPtr
->darkColorPtr
); 
 417         if (borderPtr
->shadow 
!= None
) { 
 418             Tk_FreeBitmap(borderPtr
->shadow
); 
 420         if (borderPtr
->lightGC 
!= None
) { 
 421             Tk_FreeGC(borderPtr
->lightGC
); 
 423         if (borderPtr
->darkGC 
!= None
) { 
 424             Tk_FreeGC(borderPtr
->darkGC
); 
 426         if (borderPtr
->bgGC 
!= None
) { 
 427             Tk_FreeGC(borderPtr
->bgGC
); 
 429         Tcl_DeleteHashEntry(borderPtr
->hashPtr
); 
 430         ckfree((char *) borderPtr
); 
 435  *---------------------------------------------------------------------- 
 437  * Tk_SetBackgroundFromBorder -- 
 439  *      Change the background of a window to one appropriate for a given 
 446  *      Tkwin's background gets modified. 
 448  *---------------------------------------------------------------------- 
 452 Tk_SetBackgroundFromBorder(tkwin
, border
) 
 453     Tk_Window tkwin
;            /* Window whose background is to be set. */ 
 454     Tk_3DBorder border
;         /* Token for border. */ 
 456     register Border 
*borderPtr 
= (Border 
*) border
; 
 458     Tk_SetWindowBackground(tkwin
, borderPtr
->bgColorPtr
->pixel
); 
 462  *---------------------------------------------------------------------- 
 466  *      Parse a relief description and return the corresponding 
 467  *      relief value, or an error. 
 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. 
 477  *---------------------------------------------------------------------- 
 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. */ 
 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
; 
 498         sprintf(interp
->result
, "bad relief type \"%.50s\":  must be %s", 
 499                 name
, "flat, raised, or sunken"); 
 506  *-------------------------------------------------------------- 
 510  *      Given a relief value, produce a string describing that 
 514  *      The return value is a static string that is equivalent 
 520  *-------------------------------------------------------------- 
 524 Tk_NameOfRelief(relief
) 
 525     int relief
;         /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED, 
 526                          * or TK_RELIEF_SUNKEN. */ 
 528     if (relief 
== TK_RELIEF_FLAT
) { 
 530     } else if (relief 
== TK_RELIEF_SUNKEN
) { 
 532     } else if (relief 
== TK_RELIEF_RAISED
) { 
 535         return "unknown relief"; 
 540  *-------------------------------------------------------------- 
 542  * Tk_Draw3DPolygon -- 
 544  *      Draw a border with 3-D appearance around the edge of a 
 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 
 557  *-------------------------------------------------------------- 
 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. */ 
 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
; 
 583     int i
, lightOnLeft
, dx
, dy
, parallel
, pointsSeen
; 
 586      * If the polygon is already closed, drop the last point from it 
 587      * (we'll close it automatically). 
 590     p1Ptr 
= &pointPtr
[numPoints
-1]; 
 591     p2Ptr 
= &pointPtr
[0]; 
 592     if ((p1Ptr
->x 
== p2Ptr
->x
) && (p1Ptr
->y 
== p2Ptr
->y
)) { 
 597      * The loop below is executed once for each vertex in the polgon. 
 598      * At the beginning of each iteration things look like this: 
 603      *             b1   * poly[0] (pointPtr[i-1]) 
 610      *             b2   *--------------------* 
 613      *             x------------------------- 
 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. 
 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 
 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)) { 
 642         if ((p2Ptr
->x 
== p1Ptr
->x
) && (p2Ptr
->y 
== p1Ptr
->y
)) { 
 644              * Ignore duplicate points (they'd cause core dumps in 
 645              * ShiftLine calls below). 
 649         ShiftLine(p1Ptr
, p2Ptr
, borderWidth
, &newB1
); 
 650         newB2
.x 
= newB1
.x 
+ (p2Ptr
->x 
- p1Ptr
->x
); 
 651         newB2
.y 
= newB1
.y 
+ (p2Ptr
->y 
- p1Ptr
->y
); 
 654         if (pointsSeen 
>= 1) { 
 655             parallel 
= Intersect(&newB1
, &newB2
, &b1
, &b2
, &poly
[2]); 
 658              * If two consecutive segments of the polygon are parallel, 
 659              * then things get more complex.  Consider the following 
 663              *    *----b1-----------b2------a 
 666              *         *---------*----------*    b 
 667              *        poly[0]  *p2Ptr   *p1Ptr  / 
 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 
 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. 
 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]); 
 697         if (pointsSeen 
>= 2) { 
 698             dx 
= poly
[3].x 
- poly
[0].x
; 
 699             dy 
= poly
[3].y 
- poly
[0].y
; 
 701                 lightOnLeft 
= (dy 
<= dx
); 
 703                 lightOnLeft 
= (dy 
< dx
); 
 705             if (lightOnLeft 
^ (leftRelief 
== TK_RELIEF_RAISED
)) { 
 706                 gc 
= borderPtr
->lightGC
; 
 708                 gc 
= borderPtr
->darkGC
; 
 710             XFillPolygon(display
, drawable
, gc
, poly
, 4, Convex
, 
 717         poly
[0].x 
= poly
[3].x
; 
 718         poly
[0].y 
= poly
[3].y
; 
 722         } else if (pointsSeen 
>= 1) { 
 723             poly
[1].x 
= poly
[2].x
; 
 724             poly
[1].y 
= poly
[2].y
; 
 731  *---------------------------------------------------------------------- 
 733  * Tk_Fill3DRectangle -- 
 735  *      Fill a rectangular area, supplying a 3D border if desired. 
 741  *      Information gets drawn on the screen. 
 743  *---------------------------------------------------------------------- 
 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. */ 
 758     register Border 
*borderPtr 
= (Border 
*) border
; 
 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
); 
 769  *---------------------------------------------------------------------- 
 771  * Tk_Fill3DPolygon -- 
 773  *      Fill a polygonal area, supplying a 3D border if desired. 
 779  *      Information gets drawn on the screen. 
 781  *---------------------------------------------------------------------- 
 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. */ 
 802     register Border 
*borderPtr 
= (Border 
*) border
; 
 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
); 
 813  *-------------------------------------------------------------- 
 817  *      Initialize the structures used for border management. 
 825  *------------------------------------------------------------- 
 832     Tcl_InitHashTable(&borderTable
, sizeof(BorderKey
)/sizeof(int)); 
 836  *-------------------------------------------------------------- 
 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. 
 850  *-------------------------------------------------------------- 
 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 
 864     int dx
, dy
, dxNeg
, dyNeg
; 
 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. 
 879     static int shiftTable
[129]; 
 882      * Initialize the table if this is the first time it is 
 886     if (shiftTable
[0] == 0) { 
 888         double tangent
, cosine
; 
 890         for (i 
= 0; i 
<= 128; i
++) { 
 892             cosine 
= 128/cos(atan(tangent
)) + .5; 
 893             shiftTable
[i
] = cosine
; 
 898     dx 
= p2Ptr
->x 
- p1Ptr
->x
; 
 899     dy 
= p2Ptr
->y 
- p1Ptr
->y
; 
 913         dy 
= ((distance 
* shiftTable
[(dy
<<7)/dx
]) + 64) >> 7; 
 919         dx 
= ((distance 
* shiftTable
[(dx
<<7)/dy
]) + 64) >> 7; 
 928  *-------------------------------------------------------------- 
 932  *      Find the intersection point between two lines. 
 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. 
 943  *-------------------------------------------------------------- 
 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. */ 
 954     int dxadyb
, dxbdya
, dxadxb
, dyadyb
, p
, q
; 
 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. 
 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
); 
 967     if (dxadyb 
== dxbdya
) { 
 970     p 
= (a1Ptr
->x
*dxbdya 
- b1Ptr
->x
*dxadyb 
+ (b1Ptr
->y 
- a1Ptr
->y
)*dxadxb
); 
 977         iPtr
->x 
= - ((-p 
+ q
/2)/q
); 
 979         iPtr
->x 
= (p 
+ q
/2)/q
; 
 981     p 
= (a1Ptr
->y
*dxadyb 
- b1Ptr
->y
*dxbdya 
+ (b1Ptr
->x 
- a1Ptr
->x
)*dyadyb
); 
 988         iPtr
->y 
= - ((-p 
+ q
/2)/q
); 
 990         iPtr
->y 
= (p 
+ q
/2)/q
;