home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tk3d.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  29.7 KB  |  1,050 lines

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