Annotation of micropolis/src/tk/tk3d.c, revision 1.1.1.1

1.1       root        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: 
                     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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.