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

1.1       root        1: /* 
                      2:  * tkCanvLine.c --
                      3:  *
                      4:  *     This file implements line items for canvas widgets.
                      5:  *
                      6:  * Copyright 1991-1992 Regents of the University of California.
                      7:  * Permission to use, copy, modify, and distribute this
                      8:  * software and its documentation for any purpose and without
                      9:  * fee is hereby granted, provided that the above copyright
                     10:  * notice appear in all copies.  The University of California
                     11:  * makes no representations about the suitability of this
                     12:  * software for any purpose.  It is provided "as is" without
                     13:  * express or implied warranty.
                     14:  */
                     15: 
                     16: #ifndef lint
                     17: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvLine.c,v 1.7 92/07/28 15:40:08 ouster Exp $ SPRITE (Berkeley)";
                     18: #endif
                     19: 
                     20: #include <stdio.h>
                     21: #include <math.h>
                     22: #include "tkint.h"
                     23: #include "tkcanvas.h"
                     24: #include "tkconfig.h"
                     25: 
                     26: /*
                     27:  * The structure below defines the record for each line item.
                     28:  */
                     29: 
                     30: typedef struct LineItem  {
                     31:     Tk_Item header;            /* Generic stuff that's the same for all
                     32:                                 * types.  MUST BE FIRST IN STRUCTURE. */
                     33:     Tk_Canvas *canvasPtr;      /* Canvas containing item.  Needed for
                     34:                                 * parsing arrow shapes. */
                     35:     int numPoints;             /* Number of points in line (always >= 2). */
                     36:     double *coordPtr;          /* Pointer to malloc-ed array containing
                     37:                                 * x- and y-coords of all points in line.
                     38:                                 * X-coords are even-valued indices, y-coords
                     39:                                 * are corresponding odd-valued indices. */
                     40:     int width;                 /* Width of line. */
                     41:     XColor *fg;                        /* Foreground color for line. */
                     42:     Pixmap fillStipple;                /* Stipple bitmap for filling line. */
                     43:     int capStyle;              /* Cap style for line. */
                     44:     int joinStyle;             /* Join style for line. */
                     45:     GC gc;                     /* Graphics context for filling line. */
                     46:     Tk_Uid arrow;              /* Indicates whether or not to draw arrowheads:
                     47:                                 * "none", "first", "last", or "both". */
                     48:     float arrowShapeA;         /* Distance from tip of arrowhead to center. */
                     49:     float arrowShapeB;         /* Distance from tip of arrowhead to trailing
                     50:                                 * point, measured along shaft. */
                     51:     float arrowShapeC;         /* Distance of trailing points from outside
                     52:                                 * edge of shaft. */
                     53:     double *firstArrowPtr;     /* Points to array of 5 points describing
                     54:                                 * polygon for arrowhead at first point in
                     55:                                 * line.  First point of arrowhead is tip.
                     56:                                 * Malloc'ed.  NULL means no arrowhead at
                     57:                                 * first point. */
                     58:     double *lastArrowPtr;      /* Points to polygon for arrowhead at last
                     59:                                 * point in line (5 points, first of which
                     60:                                 * is tip).  Malloc'ed.  NULL means no
                     61:                                 * arrowhead at last point. */
                     62:     int smooth;                        /* Non-zero means draw line smoothed (i.e.
                     63:                                 * with Bezier splines). */
                     64:     int splineSteps;           /* Number of steps in each spline segment. */
                     65: } LineItem;
                     66: 
                     67: /*
                     68:  * Number of points in an arrowHead:
                     69:  */
                     70: 
                     71: #define PTS_IN_ARROW 6
                     72: 
                     73: /*
                     74:  * Prototypes for procedures defined in this file:
                     75:  */
                     76: 
                     77: static void            ComputeLineBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     78:                            LineItem *linePtr));
                     79: static int             ConfigureLine _ANSI_ARGS_((
                     80:                            Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc,
                     81:                            char **argv, int flags));
                     82: static int             ConfigureArrows _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     83:                            LineItem *linePtr));
                     84: static int             CreateLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     85:                            struct Tk_Item *itemPtr, int argc, char **argv));
                     86: static void            DeleteLine _ANSI_ARGS_((Tk_Item *itemPtr));
                     87: static void            DisplayLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     88:                            Tk_Item *itemPtr, Drawable dst));
                     89: static int             LineCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     90:                            Tk_Item *itemPtr, int argc, char **argv));
                     91: static int             LineToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     92:                            Tk_Item *itemPtr, double *rectPtr));
                     93: static double          LineToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                     94:                            Tk_Item *itemPtr, double *coordPtr));
                     95: static int             ParseArrowShape _ANSI_ARGS_((ClientData clientData,
                     96:                            Tcl_Interp *interp, Tk_Window tkwin, char *value,
                     97:                            char *recordPtr, int offset));
                     98: static char *          PrintArrowShape _ANSI_ARGS_((ClientData clientData,
                     99:                            Tk_Window tkwin, char *recordPtr, int offset,
                    100:                            Tcl_FreeProc **freeProcPtr));
                    101: static void            ScaleLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                    102:                            Tk_Item *itemPtr, double originX, double originY,
                    103:                            double scaleX, double scaleY));
                    104: static void            TranslateLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
                    105:                            Tk_Item *itemPtr, double deltaX, double deltaY));
                    106: 
                    107: /*
                    108:  * Information used for parsing configuration specs.  If you change any
                    109:  * of the default strings, be sure to change the corresponding default
                    110:  * values in CreateLine.
                    111:  */
                    112: 
                    113: static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
                    114:        PrintArrowShape, (ClientData) NULL};
                    115: 
                    116: static Tk_ConfigSpec configSpecs[] = {
                    117:     {TK_CONFIG_UID, "-arrow", (char *) NULL, (char *) NULL,
                    118:        "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT},
                    119:     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
                    120:        "8 10 3", Tk_Offset(LineItem, arrowShapeA),
                    121:        TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
                    122:     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
                    123:        "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
                    124:     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
                    125:        "black", Tk_Offset(LineItem, fg), 0},
                    126:     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
                    127:        "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
                    128:     {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL,
                    129:        "no", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
                    130:     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
                    131:        "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
                    132:     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
                    133:        (char *) NULL, Tk_Offset(LineItem, fillStipple), TK_CONFIG_NULL_OK},
                    134:     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
                    135:        (char *) NULL, 0, TK_CONFIG_NULL_OK, &tkCanvasTagsOption},
                    136:     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
                    137:        "1", Tk_Offset(LineItem, width), TK_CONFIG_DONT_SET_DEFAULT},
                    138:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    139:        (char *) NULL, 0, 0}
                    140: };
                    141: 
                    142: /*
                    143:  * The structures below defines the line item type by means
                    144:  * of procedures that can be invoked by generic item code.
                    145:  */
                    146: 
                    147: Tk_ItemType TkLineType = {
                    148:     "line",                            /* name */
                    149:     sizeof(LineItem),                  /* itemSize */
                    150:     CreateLine,                                /* createProc */
                    151:     configSpecs,                       /* configSpecs */
                    152:     ConfigureLine,                     /* configureProc */
                    153:     LineCoords,                                /* coordProc */
                    154:     DeleteLine,                                /* deleteProc */
                    155:     DisplayLine,                       /* displayProc */
                    156:     0,                                 /* alwaysRedraw */
                    157:     LineToPoint,                       /* pointProc */
                    158:     LineToArea,                                /* areaProc */
                    159:     (Tk_ItemPostscriptProc *) NULL,    /* postscriptProc */
                    160:     ScaleLine,                         /* scaleProc */
                    161:     TranslateLine,                     /* translateProc */
                    162:     (Tk_ItemIndexProc *) NULL,         /* indexProc */
                    163:     (Tk_ItemCursorProc *) NULL,                /* cursorProc */
                    164:     (Tk_ItemSelectionProc *) NULL,     /* selectionProc */
                    165:     (Tk_ItemInsertProc *) NULL,                /* insertProc */
                    166:     (Tk_ItemDCharsProc *) NULL,                /* dTextProc */
                    167:     (Tk_ItemType *) NULL               /* nextPtr */
                    168: };
                    169: 
                    170: /*
                    171:  * The Tk_Uid's below refer to uids for the various arrow types:
                    172:  */
                    173: 
                    174: static Tk_Uid noneUid = NULL;
                    175: static Tk_Uid firstUid = NULL;
                    176: static Tk_Uid lastUid = NULL;
                    177: static Tk_Uid bothUid = NULL;
                    178: 
                    179: /*
                    180:  * The definition below determines how large are static arrays
                    181:  * used to hold spline points (splines larger than this have to
                    182:  * have their arrays malloc-ed).
                    183:  */
                    184: 
                    185: #define MAX_STATIC_POINTS 200
                    186: 
                    187: /*
                    188:  *--------------------------------------------------------------
                    189:  *
                    190:  * CreateLine --
                    191:  *
                    192:  *     This procedure is invoked to create a new line item in
                    193:  *     a canvas.
                    194:  *
                    195:  * Results:
                    196:  *     A standard Tcl return value.  If an error occurred in
                    197:  *     creating the item, then an error message is left in
                    198:  *     canvasPtr->interp->result;  in this case itemPtr is
                    199:  *     left uninitialized, so it can be safely freed by the
                    200:  *     caller.
                    201:  *
                    202:  * Side effects:
                    203:  *     A new line item is created.
                    204:  *
                    205:  *--------------------------------------------------------------
                    206:  */
                    207: 
                    208: static int
                    209: CreateLine(canvasPtr, itemPtr, argc, argv)
                    210:     register Tk_Canvas *canvasPtr;     /* Canvas to hold new item. */
                    211:     Tk_Item *itemPtr;                  /* Record to hold new item;  header
                    212:                                         * has been initialized by caller. */
                    213:     int argc;                          /* Number of arguments in argv. */
                    214:     char **argv;                       /* Arguments describing line. */
                    215: {
                    216:     register LineItem *linePtr = (LineItem *) itemPtr;
                    217:     int i;
                    218: 
                    219:     if (argc < 4) {
                    220:        Tcl_AppendResult(canvasPtr->interp, "wrong # args:  should be \"",
                    221:                Tk_PathName(canvasPtr->tkwin),
                    222:                "\" create x1 y1 x2 y2 ?x3 y3 ...? ?options?",
                    223:                (char *) NULL);
                    224:        return TCL_ERROR;
                    225:     }
                    226: 
                    227:     /*
                    228:      * Carry out initialization that is needed to set defaults and to
                    229:      * allow proper cleanup after errors during the the remainder of
                    230:      * this procedure.
                    231:      */
                    232: 
                    233:     linePtr->canvasPtr = canvasPtr;
                    234:     linePtr->numPoints = 0;
                    235:     linePtr->coordPtr = NULL;
                    236:     linePtr->width = 1;
                    237:     linePtr->fg = None;
                    238:     linePtr->fillStipple = None;
                    239:     linePtr->capStyle = CapButt;
                    240:     linePtr->joinStyle = JoinRound;
                    241:     linePtr->gc = None;
                    242:     if (noneUid == NULL) {
                    243:        noneUid = Tk_GetUid("none");
                    244:        firstUid = Tk_GetUid("first");
                    245:        lastUid = Tk_GetUid("last");
                    246:        bothUid = Tk_GetUid("both");
                    247:     }
                    248:     linePtr->arrow = noneUid;
                    249:     linePtr->arrowShapeA = 8.0;
                    250:     linePtr->arrowShapeB = 10.0;
                    251:     linePtr->arrowShapeC = 3.0;
                    252:     linePtr->firstArrowPtr = NULL;
                    253:     linePtr->lastArrowPtr = NULL;
                    254:     linePtr->smooth = 0;
                    255:     linePtr->splineSteps = 12;
                    256: 
                    257:     /*
                    258:      * Count the number of points and then parse them into a point
                    259:      * array.  Leading arguments are assumed to be points if they
                    260:      * start with a digit or a minus sign followed by a digit.
                    261:      */
                    262: 
                    263:     for (i = 4; i < (argc-1); i+=2) {
                    264:        if ((!isdigit(argv[i][0])) &&
                    265:                ((argv[i][0] != '-') || (!isdigit(argv[i][1])))) {
                    266:            break;
                    267:        }
                    268:     }
                    269:     if (LineCoords(canvasPtr, itemPtr, i, argv) != TCL_OK) {
                    270:        goto error;
                    271:     }
                    272:     if (ConfigureLine(canvasPtr, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
                    273:        return TCL_OK;
                    274:     }
                    275: 
                    276:     error:
                    277:     DeleteLine(itemPtr);
                    278:     return TCL_ERROR;
                    279: }
                    280: 
                    281: /*
                    282:  *--------------------------------------------------------------
                    283:  *
                    284:  * LineCoords --
                    285:  *
                    286:  *     This procedure is invoked to process the "coords" widget
                    287:  *     command on lines.  See the user documentation for details
                    288:  *     on what it does.
                    289:  *
                    290:  * Results:
                    291:  *     Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
                    292:  *
                    293:  * Side effects:
                    294:  *     The coordinates for the given item may be changed.
                    295:  *
                    296:  *--------------------------------------------------------------
                    297:  */
                    298: 
                    299: static int
                    300: LineCoords(canvasPtr, itemPtr, argc, argv)
                    301:     register Tk_Canvas *canvasPtr;     /* Canvas containing item. */
                    302:     Tk_Item *itemPtr;                  /* Item whose coordinates are to be
                    303:                                         * read or modified. */
                    304:     int argc;                          /* Number of coordinates supplied in
                    305:                                         * argv. */
                    306:     char **argv;                       /* Array of coordinates: x1, y1,
                    307:                                         * x2, y2, ... */
                    308: {
                    309:     register LineItem *linePtr = (LineItem *) itemPtr;
                    310:     char buffer[300];
                    311:     int i, numPoints;
                    312: 
                    313:     if (argc == 0) {
                    314:        for (i = 0; i < 2*linePtr->numPoints; i++) {
                    315:            sprintf(buffer, "%g", linePtr->coordPtr[i]);
                    316:            Tcl_AppendElement(canvasPtr->interp, buffer, 0);
                    317:        }
                    318:     } else if (argc < 4) {
                    319:        Tcl_AppendResult(canvasPtr->interp,
                    320:                "too few coordinates for line:  must have at least 4",
                    321:                (char *) NULL);
                    322:        return TCL_ERROR;
                    323:     } else if (argc & 1) {
                    324:        Tcl_AppendResult(canvasPtr->interp,
                    325:                "odd number of coordinates specified for line",
                    326:                (char *) NULL);
                    327:        return TCL_ERROR;
                    328:     } else {
                    329:        numPoints = argc/2;
                    330:        if (linePtr->numPoints != numPoints) {
                    331:            if (linePtr->coordPtr != NULL) {
                    332:                ckfree((char *) linePtr->coordPtr);
                    333:            }
                    334:            linePtr->coordPtr = (double *) ckalloc((unsigned)
                    335:                    (sizeof(double) * argc));
                    336:            linePtr->numPoints = numPoints;
                    337:        }
                    338:        for (i = argc-1; i >= 0; i--) {
                    339:            if (TkGetCanvasCoord(canvasPtr, argv[i], &linePtr->coordPtr[i])
                    340:                    != TCL_OK) {
                    341:                return TCL_ERROR;
                    342:            }
                    343:        }
                    344:        ComputeLineBbox(canvasPtr, linePtr);
                    345:     }
                    346:     return TCL_OK;
                    347: }
                    348: 
                    349: /*
                    350:  *--------------------------------------------------------------
                    351:  *
                    352:  * ConfigureLine --
                    353:  *
                    354:  *     This procedure is invoked to configure various aspects
                    355:  *     of a line item such as its background color.
                    356:  *
                    357:  * Results:
                    358:  *     A standard Tcl result code.  If an error occurs, then
                    359:  *     an error message is left in canvasPtr->interp->result.
                    360:  *
                    361:  * Side effects:
                    362:  *     Configuration information, such as colors and stipple
                    363:  *     patterns, may be set for itemPtr.
                    364:  *
                    365:  *--------------------------------------------------------------
                    366:  */
                    367: 
                    368: static int
                    369: ConfigureLine(canvasPtr, itemPtr, argc, argv, flags)
                    370:     Tk_Canvas *canvasPtr;      /* Canvas containing itemPtr. */
                    371:     Tk_Item *itemPtr;          /* Line item to reconfigure. */
                    372:     int argc;                  /* Number of elements in argv.  */
                    373:     char **argv;               /* Arguments describing things to configure. */
                    374:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
                    375: {
                    376:     register LineItem *linePtr = (LineItem *) itemPtr;
                    377:     XGCValues gcValues;
                    378:     GC newGC;
                    379:     unsigned long mask;
                    380: 
                    381:     if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin,
                    382:            configSpecs, argc, argv, (char *) linePtr, flags) != TCL_OK) {
                    383:        return TCL_ERROR;
                    384:     }
                    385: 
                    386:     /*
                    387:      * A few of the options require additional processing, such as
                    388:      * graphics contexts.
                    389:      */
                    390: 
                    391:     if (linePtr->fg == NULL) {
                    392:        newGC = None;
                    393:     } else {
                    394:        gcValues.foreground = linePtr->fg->pixel;
                    395:        gcValues.join_style = linePtr->joinStyle;
                    396:        if (linePtr->width < 0) {
                    397:            linePtr->width = 1;
                    398:        }
                    399:        gcValues.line_width = linePtr->width;
                    400:        mask = GCForeground|GCJoinStyle|GCLineWidth;
                    401:        if (linePtr->fillStipple != None) {
                    402:            gcValues.stipple = linePtr->fillStipple;
                    403:            gcValues.fill_style = FillStippled;
                    404:            mask |= GCStipple|GCFillStyle;
                    405:        }
                    406:        if (linePtr->arrow == noneUid) {
                    407:            gcValues.cap_style = linePtr->capStyle;
                    408:            mask |= GCCapStyle;
                    409:        }
                    410:        newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
                    411:     }
                    412:     if (linePtr->gc != None) {
                    413:        Tk_FreeGC(linePtr->gc);
                    414:     }
                    415:     linePtr->gc = newGC;
                    416: 
                    417:     /*
                    418:      * Keep spline parameters within reasonable limits.
                    419:      */
                    420: 
                    421:     if (linePtr->splineSteps < 1) {
                    422:        linePtr->splineSteps = 1;
                    423:     } else if (linePtr->splineSteps > 100) {
                    424:        linePtr->splineSteps = 100;
                    425:     }
                    426: 
                    427:     /*
                    428:      * Setup arrowheads, if needed.  If arrowheads are turned off,
                    429:      * restore the line's endpoints (they were shortened when the
                    430:      * arrowheads were added).
                    431:      */
                    432: 
                    433:     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != firstUid)
                    434:            && (linePtr->arrow != bothUid)) {
                    435:        linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
                    436:        linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
                    437:        ckfree((char *) linePtr->firstArrowPtr);
                    438:        linePtr->firstArrowPtr = NULL;
                    439:     }
                    440:     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != lastUid)
                    441:            && (linePtr->arrow != bothUid)) {
                    442:        int index;
                    443: 
                    444:        index = 2*(linePtr->numPoints-1);
                    445:        linePtr->coordPtr[index] = linePtr->lastArrowPtr[0];
                    446:        linePtr->coordPtr[index+1] = linePtr->lastArrowPtr[1];
                    447:        ckfree((char *) linePtr->lastArrowPtr);
                    448:        linePtr->lastArrowPtr = NULL;
                    449:     }
                    450:     if (linePtr->arrow != noneUid) {
                    451:        if ((linePtr->arrow != firstUid) && (linePtr->arrow != lastUid)
                    452:                && (linePtr->arrow != bothUid)) {
                    453:            Tcl_AppendResult(canvasPtr->interp, "bad arrow spec \"",
                    454:                    linePtr->arrow, "\": must be none, first, last, or both",
                    455:                    (char *) NULL);
                    456:            linePtr->arrow = noneUid;
                    457:            return TCL_ERROR;
                    458:        }
                    459:        ConfigureArrows(canvasPtr, linePtr);
                    460:     }
                    461: 
                    462:     /*
                    463:      * Recompute bounding box for line.
                    464:      */
                    465: 
                    466:     ComputeLineBbox(canvasPtr, linePtr);
                    467: 
                    468:     return TCL_OK;
                    469: }
                    470: 
                    471: /*
                    472:  *--------------------------------------------------------------
                    473:  *
                    474:  * DeleteLine --
                    475:  *
                    476:  *     This procedure is called to clean up the data structure
                    477:  *     associated with a line item.
                    478:  *
                    479:  * Results:
                    480:  *     None.
                    481:  *
                    482:  * Side effects:
                    483:  *     Resources associated with itemPtr are released.
                    484:  *
                    485:  *--------------------------------------------------------------
                    486:  */
                    487: 
                    488: static void
                    489: DeleteLine(itemPtr)
                    490:     Tk_Item *itemPtr;                  /* Item that is being deleted. */
                    491: {
                    492:     register LineItem *linePtr = (LineItem *) itemPtr;
                    493: 
                    494:     if (linePtr->coordPtr != NULL) {
                    495:        ckfree((char *) linePtr->coordPtr);
                    496:     }
                    497:     if (linePtr->fg != NULL) {
                    498:        Tk_FreeColor(linePtr->fg);
                    499:     }
                    500:     if (linePtr->fillStipple != None) {
                    501:        Tk_FreeBitmap(linePtr->fillStipple);
                    502:     }
                    503:     if (linePtr->gc != None) {
                    504:        Tk_FreeGC(linePtr->gc);
                    505:     }
                    506:     if (linePtr->firstArrowPtr != NULL) {
                    507:        ckfree((char *) linePtr->firstArrowPtr);
                    508:     }
                    509:     if (linePtr->lastArrowPtr != NULL) {
                    510:        ckfree((char *) linePtr->lastArrowPtr);
                    511:     }
                    512: }
                    513: 
                    514: /*
                    515:  *--------------------------------------------------------------
                    516:  *
                    517:  * ComputeLineBbox --
                    518:  *
                    519:  *     This procedure is invoked to compute the bounding box of
                    520:  *     all the pixels that may be drawn as part of a line.
                    521:  *
                    522:  * Results:
                    523:  *     None.
                    524:  *
                    525:  * Side effects:
                    526:  *     The fields x1, y1, x2, and y2 are updated in the header
                    527:  *     for itemPtr.
                    528:  *
                    529:  *--------------------------------------------------------------
                    530:  */
                    531: 
                    532: static void
                    533: ComputeLineBbox(canvasPtr, linePtr)
                    534:     register Tk_Canvas *canvasPtr;     /* Canvas that contains item. */
                    535:     LineItem *linePtr;                 /* Item whose bbos is to be
                    536:                                         * recomputed. */
                    537: {
                    538:     register double *coordPtr;
                    539:     int i;
                    540: 
                    541:     coordPtr = linePtr->coordPtr;
                    542:     linePtr->header.x1 = linePtr->header.x2 = *coordPtr;
                    543:     linePtr->header.y1 = linePtr->header.y2 = coordPtr[1];
                    544: 
                    545:     /*
                    546:      * Compute the bounding box of all the points in the line,
                    547:      * then expand in all directions by the line's width to take
                    548:      * care of butting or rounded corners and projecting or
                    549:      * rounded caps.  This expansion is an overestimate (worst-case
                    550:      * is square root of two over two) but it's simple.  Don't do
                    551:      * anything special for curves.  This causes an additional
                    552:      * overestimate in the bounding box, but is faster.
                    553:      */
                    554: 
                    555:     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
                    556:            i++, coordPtr += 2) {
                    557:        TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
                    558:     }
                    559:     linePtr->header.x1 -= linePtr->width;
                    560:     linePtr->header.x2 += linePtr->width;
                    561:     linePtr->header.y1 -= linePtr->width;
                    562:     linePtr->header.y2 += linePtr->width;
                    563: 
                    564:     /*
                    565:      * For mitered lines, make a second pass through all the points.
                    566:      * Compute the locations of the two miter vertex points and add
                    567:      * those into the bounding box.
                    568:      */
                    569: 
                    570:     if (linePtr->joinStyle == JoinMiter) {
                    571:        for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
                    572:                i--, coordPtr += 2) {
                    573:            double miter[4];
                    574:            int j;
                    575:     
                    576:            if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
                    577:                    (double) linePtr->width, miter, miter+2)) {
                    578:                for (j = 0; j < 4; j += 2) {
                    579:                    TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, miter+j);
                    580:                }
                    581:            }
                    582:        }
                    583:     }
                    584: 
                    585:     /*
                    586:      * Add in the sizes of arrowheads, if any.
                    587:      */
                    588: 
                    589:     if (linePtr->arrow != noneUid) {
                    590:        if (linePtr->arrow != lastUid) {
                    591:            for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
                    592:                    i++, coordPtr += 2) {
                    593:                TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
                    594:            }
                    595:        }
                    596:        if (linePtr->arrow != firstUid) {
                    597:            for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
                    598:                    i++, coordPtr += 2) {
                    599:                TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
                    600:            }
                    601:        }
                    602:     }
                    603: 
                    604:     /*
                    605:      * Add one more pixel of fudge factor just to be safe (e.g.
                    606:      * X may round differently than we do).
                    607:      */
                    608: 
                    609:     linePtr->header.x1 -= 1;
                    610:     linePtr->header.x2 += 1;
                    611:     linePtr->header.y1 -= 1;
                    612:     linePtr->header.y2 += 1;
                    613: }
                    614: 
                    615: /*
                    616:  *--------------------------------------------------------------
                    617:  *
                    618:  * DisplayLine --
                    619:  *
                    620:  *     This procedure is invoked to draw a line item in a given
                    621:  *     drawable.
                    622:  *
                    623:  * Results:
                    624:  *     None.
                    625:  *
                    626:  * Side effects:
                    627:  *     ItemPtr is drawn in drawable using the transformation
                    628:  *     information in canvasPtr.
                    629:  *
                    630:  *--------------------------------------------------------------
                    631:  */
                    632: 
                    633: static void
                    634: DisplayLine(canvasPtr, itemPtr, drawable)
                    635:     register Tk_Canvas *canvasPtr;     /* Canvas that contains item. */
                    636:     Tk_Item *itemPtr;                  /* Item to be displayed. */
                    637:     Drawable drawable;                 /* Pixmap or window in which to draw
                    638:                                         * item. */
                    639: {
                    640:     register LineItem *linePtr = (LineItem *) itemPtr;
                    641:     XPoint staticPoints[MAX_STATIC_POINTS];
                    642:     XPoint *pointPtr;
                    643:     register XPoint *pPtr;
                    644:     register double *coordPtr;
                    645:     int i, numPoints;
                    646: 
                    647:     if (linePtr->gc == None) {
                    648:        return;
                    649:     }
                    650: 
                    651:     /*
                    652:      * Build up an array of points in screen coordinates.  Use a
                    653:      * static array unless the line has an enormous number of points;
                    654:      * in this case, dynamically allocate an array.  For smoothed lines,
                    655:      * generate the curve points on each redisplay.
                    656:      */
                    657: 
                    658:     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
                    659:        numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
                    660:     } else {
                    661:        numPoints = linePtr->numPoints;
                    662:     }
                    663: 
                    664:     if (numPoints <= MAX_STATIC_POINTS) {
                    665:        pointPtr = staticPoints;
                    666:     } else {
                    667:        pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
                    668:     }
                    669: 
                    670:     if (linePtr->smooth) {
                    671:        numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
                    672:                linePtr->numPoints, linePtr->splineSteps, pointPtr,
                    673:                (double *) NULL);
                    674:     } else {
                    675:        for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
                    676:                i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
                    677:            pPtr->x = SCREEN_X(canvasPtr, *coordPtr);
                    678:            pPtr->y = SCREEN_Y(canvasPtr, coordPtr[1]);
                    679:        }
                    680:     }
                    681: 
                    682:     /*
                    683:      * Display line, the free up line storage if it was dynamically
                    684:      * allocated.
                    685:      */
                    686: 
                    687:     XDrawLines(Tk_Display(canvasPtr->tkwin), drawable, linePtr->gc,
                    688:            pointPtr, numPoints, CoordModeOrigin);
                    689:     if (pointPtr != staticPoints) {
                    690:        ckfree((char *) pointPtr);
                    691:     }
                    692: 
                    693:     /*
                    694:      * Display arrowheads, if they are wanted.
                    695:      */
                    696: 
                    697:     if (linePtr->arrow != noneUid) {
                    698:        if (linePtr->arrow != lastUid) {
                    699:            TkFillPolygon(canvasPtr, linePtr->firstArrowPtr, PTS_IN_ARROW,
                    700:                    drawable, linePtr->gc);
                    701:        }
                    702:        if (linePtr->arrow != firstUid) {
                    703:            TkFillPolygon(canvasPtr, linePtr->lastArrowPtr, PTS_IN_ARROW,
                    704:                    drawable, linePtr->gc);
                    705:        }
                    706:     }
                    707: }
                    708: 
                    709: /*
                    710:  *--------------------------------------------------------------
                    711:  *
                    712:  * LineToPoint --
                    713:  *
                    714:  *     Computes the distance from a given point to a given
                    715:  *     line, in canvas units.
                    716:  *
                    717:  * Results:
                    718:  *     The return value is 0 if the point whose x and y coordinates
                    719:  *     are pointPtr[0] and pointPtr[1] is inside the line.  If the
                    720:  *     point isn't inside the line then the return value is the
                    721:  *     distance from the point to the line.
                    722:  *
                    723:  * Side effects:
                    724:  *     None.
                    725:  *
                    726:  *--------------------------------------------------------------
                    727:  */
                    728: 
                    729:        /* ARGSUSED */
                    730: static double
                    731: LineToPoint(canvasPtr, itemPtr, pointPtr)
                    732:     Tk_Canvas *canvasPtr;      /* Canvas containing item. */
                    733:     Tk_Item *itemPtr;          /* Item to check against point. */
                    734:     double *pointPtr;          /* Pointer to x and y coordinates. */
                    735: {
                    736:     register LineItem *linePtr = (LineItem *) itemPtr;
                    737:     register double *coordPtr, *linePoints;
                    738:     double staticSpace[2*MAX_STATIC_POINTS];
                    739:     double poly[10];
                    740:     double bestDist, dist;
                    741:     int numPoints, count;
                    742:     int changedMiterToBevel;   /* Non-zero means that a mitered corner
                    743:                                 * had to be treated as beveled after all
                    744:                                 * because the angle was < 11 degrees. */
                    745: 
                    746:     bestDist = 1.0e40;
                    747: 
                    748:     /*
                    749:      * Handle smoothed lines by generating an expanded set of points
                    750:      * against which to do the check.
                    751:      */
                    752: 
                    753:     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
                    754:        numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
                    755:        if (numPoints <= MAX_STATIC_POINTS) {
                    756:            linePoints = staticSpace;
                    757:        } else {
                    758:            linePoints = (double *) ckalloc((unsigned)
                    759:                    (2*numPoints*sizeof(double)));
                    760:        }
                    761:        numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
                    762:                linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
                    763:                linePoints);
                    764:     } else {
                    765:        numPoints = linePtr->numPoints;
                    766:        linePoints = linePtr->coordPtr;
                    767:     }
                    768: 
                    769:     /*
                    770:      * The overall idea is to iterate through all of the edges of
                    771:      * the line, computing a polygon for each edge and testing the
                    772:      * point against that polygon.  In addition, there are additional
                    773:      * tests to deal with rounded joints and caps.
                    774:      */
                    775: 
                    776:     changedMiterToBevel = 0;
                    777:     for (count = numPoints, coordPtr = linePoints; count >= 2;
                    778:            count--, coordPtr += 2) {
                    779: 
                    780:        /*
                    781:         * If rounding is done around the first point then compute
                    782:         * the distance between the point and the point.
                    783:         */
                    784: 
                    785:        if (((linePtr->capStyle == CapRound) && (count == numPoints))
                    786:                || ((linePtr->joinStyle == JoinRound)
                    787:                        && (count != numPoints))) {
                    788:            dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
                    789:                    - linePtr->width/2.0;
                    790:            if (dist <= 0.0) {
                    791:                bestDist = 0.0;
                    792:                goto done;
                    793:            } else if (dist < bestDist) {
                    794:                bestDist = dist;
                    795:            }
                    796:        }
                    797: 
                    798:        /*
                    799:         * Compute the polygonal shape corresponding to this edge,
                    800:         * consisting of two points for the first point of the edge
                    801:         * and two points for the last point of the edge.
                    802:         */
                    803: 
                    804:        if (count == numPoints) {
                    805:            TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
                    806:                    linePtr->capStyle == CapProjecting, poly, poly+2);
                    807:        } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
                    808:            poly[0] = poly[6];
                    809:            poly[1] = poly[7];
                    810:            poly[2] = poly[4];
                    811:            poly[3] = poly[5];
                    812:        } else {
                    813:            TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
                    814:                    poly, poly+2);
                    815: 
                    816:            /*
                    817:             * If this line uses beveled joints, then check the distance
                    818:             * to a polygon comprising the last two points of the previous
                    819:             * polygon and the first two from this polygon;  this checks
                    820:             * the wedges that fill the mitered joint.
                    821:             */
                    822: 
                    823:            if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
                    824:                poly[8] = poly[0];
                    825:                poly[9] = poly[1];
                    826:                dist = TkPolygonToPoint(poly, 5, pointPtr);
                    827:                if (dist <= 0.0) {
                    828:                    bestDist = 0.0;
                    829:                    goto done;
                    830:                } else if (dist < bestDist) {
                    831:                    bestDist = dist;
                    832:                }
                    833:                changedMiterToBevel = 0;
                    834:            }
                    835:        }
                    836:        if (count == 2) {
                    837:            TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
                    838:                    linePtr->capStyle == CapProjecting, poly+4, poly+6);
                    839:        } else if (linePtr->joinStyle == JoinMiter) {
                    840:            if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
                    841:                    (double) linePtr->width, poly+4, poly+6) == 0) {
                    842:                changedMiterToBevel = 1;
                    843:                TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
                    844:                        0, poly+4, poly+6);
                    845:            }
                    846:        } else {
                    847:            TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
                    848:                    poly+4, poly+6);
                    849:        }
                    850:        poly[8] = poly[0];
                    851:        poly[9] = poly[1];
                    852:        dist = TkPolygonToPoint(poly, 5, pointPtr);
                    853:        if (dist <= 0.0) {
                    854:            bestDist = 0.0;
                    855:            goto done;
                    856:        } else if (dist < bestDist) {
                    857:            bestDist = dist;
                    858:        }
                    859:     }
                    860: 
                    861:     /*
                    862:      * If caps are rounded, check the distance to the cap around the
                    863:      * final end point of the line.
                    864:      */
                    865: 
                    866:     if (linePtr->capStyle == CapRound) {
                    867:        dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
                    868:                - linePtr->width/2.0;
                    869:        if (dist <= 0.0) {
                    870:            bestDist = 0.0;
                    871:            goto done;
                    872:        } else if (dist < bestDist) {
                    873:            bestDist = dist;
                    874:        }
                    875:     }
                    876: 
                    877:     /*
                    878:      * If there are arrowheads, check the distance to the arrowheads.
                    879:      */
                    880: 
                    881:     if (linePtr->arrow != noneUid) {
                    882:        if (linePtr->arrow != lastUid) {
                    883:            dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
                    884:                    pointPtr);
                    885:            if (dist <= 0.0) {
                    886:                bestDist = 0.0;
                    887:                goto done;
                    888:            } else if (dist < bestDist) {
                    889:                bestDist = dist;
                    890:            }
                    891:        }
                    892:        if (linePtr->arrow != firstUid) {
                    893:            dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
                    894:                    pointPtr);
                    895:            if (dist <= 0.0) {
                    896:                bestDist = 0.0;
                    897:                goto done;
                    898:            } else if (dist < bestDist) {
                    899:                bestDist = dist;
                    900:            }
                    901:        }
                    902:     }
                    903: 
                    904:     done:
                    905:     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
                    906:        ckfree((char *) linePoints);
                    907:     }
                    908:     return bestDist;
                    909: }
                    910: 
                    911: /*
                    912:  *--------------------------------------------------------------
                    913:  *
                    914:  * LineToArea --
                    915:  *
                    916:  *     This procedure is called to determine whether an item
                    917:  *     lies entirely inside, entirely outside, or overlapping
                    918:  *     a given rectangular area.
                    919:  *
                    920:  * Results:
                    921:  *     -1 is returned if the item is entirely outside the
                    922:  *     area, 0 if it overlaps, and 1 if it is entirely
                    923:  *     inside the given area.
                    924:  *
                    925:  * Side effects:
                    926:  *     None.
                    927:  *
                    928:  *--------------------------------------------------------------
                    929:  */
                    930: 
                    931:        /* ARGSUSED */
                    932: static int
                    933: LineToArea(canvasPtr, itemPtr, rectPtr)
                    934:     Tk_Canvas *canvasPtr;      /* Canvas containing item. */
                    935:     Tk_Item *itemPtr;          /* Item to check against line. */
                    936:     double *rectPtr;
                    937: {
                    938:     register LineItem *linePtr = (LineItem *) itemPtr;
                    939:     register double *coordPtr;
                    940:     double staticSpace[2*MAX_STATIC_POINTS];
                    941:     double *linePoints, poly[10];
                    942:     double radius;
                    943:     int numPoints, count;
                    944:     int changedMiterToBevel;   /* Non-zero means that a mitered corner
                    945:                                 * had to be treated as beveled after all
                    946:                                 * because the angle was < 11 degrees. */
                    947:     int inside;                        /* Tentative guess about what to return,
                    948:                                 * based on all points seen so far:  one
                    949:                                 * means everything seen so far was
                    950:                                 * inside the area;  -1 means everything
                    951:                                 * was outside the area.  0 means overlap
                    952:                                 * has been found. */ 
                    953: 
                    954:     radius = linePtr->width/2.0;
                    955:     inside = -1;
                    956: 
                    957:     /*
                    958:      * Handle smoothed lines by generating an expanded set of points
                    959:      * against which to do the check.
                    960:      */
                    961: 
                    962:     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
                    963:        numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
                    964:        if (numPoints <= MAX_STATIC_POINTS) {
                    965:            linePoints = staticSpace;
                    966:        } else {
                    967:            linePoints = (double *) ckalloc((unsigned)
                    968:                    (2*numPoints*sizeof(double)));
                    969:        }
                    970:        numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
                    971:                linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
                    972:                linePoints);
                    973:     } else {
                    974:        numPoints = linePtr->numPoints;
                    975:        linePoints = linePtr->coordPtr;
                    976:     }
                    977: 
                    978:     coordPtr = linePoints;
                    979:     if ((coordPtr[0] >= rectPtr[0]) && (coordPtr[0] <= rectPtr[2])
                    980:            && (coordPtr[1] >= rectPtr[1]) && (coordPtr[1] <= rectPtr[3])) {
                    981:        inside = 1;
                    982:     }
                    983: 
                    984:     /*
                    985:      * Iterate through all of the edges of the line, computing a polygon
                    986:      * for each edge and testing the area against that polygon.  In
                    987:      * addition, there are additional tests to deal with rounded joints
                    988:      * and caps.
                    989:      */
                    990: 
                    991:     changedMiterToBevel = 0;
                    992:     for (count = numPoints; count >= 2; count--, coordPtr += 2) {
                    993: 
                    994:        /*
                    995:         * If rounding is done around the first point of the edge
                    996:         * then test a circular region around the point with the
                    997:         * area.
                    998:         */
                    999: 
                   1000:        if (((linePtr->capStyle == CapRound) && (count == numPoints))
                   1001:                || ((linePtr->joinStyle == JoinRound)
                   1002:                && (count != numPoints))) {
                   1003:            poly[0] = coordPtr[0] - radius;
                   1004:            poly[1] = coordPtr[1] - radius;
                   1005:            poly[2] = coordPtr[0] + radius;
                   1006:            poly[3] = coordPtr[1] + radius;
                   1007:            if (TkOvalToArea(poly, rectPtr) != inside) {
                   1008:                inside = 0;
                   1009:                goto done;
                   1010:            }
                   1011:        }
                   1012: 
                   1013:        /*
                   1014:         * Compute the polygonal shape corresponding to this edge,
                   1015:         * consisting of two points for the first point of the edge
                   1016:         * and two points for the last point of the edge.
                   1017:         */
                   1018: 
                   1019:        if (count == numPoints) {
                   1020:            TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
                   1021:                    linePtr->capStyle == CapProjecting, poly, poly+2);
                   1022:        } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
                   1023:            poly[0] = poly[6];
                   1024:            poly[1] = poly[7];
                   1025:            poly[2] = poly[4];
                   1026:            poly[3] = poly[5];
                   1027:        } else {
                   1028:            TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
                   1029:                    poly, poly+2);
                   1030: 
                   1031:            /*
                   1032:             * If the last joint was beveled, then also check a
                   1033:             * polygon comprising the last two points of the previous
                   1034:             * polygon and the first two from this polygon;  this checks
                   1035:             * the wedges that fill the beveled joint.
                   1036:             */
                   1037: 
                   1038:            if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
                   1039:                poly[8] = poly[0];
                   1040:                poly[9] = poly[1];
                   1041:                if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
                   1042:                    inside = 0;
                   1043:                    goto done;
                   1044:                }
                   1045:                changedMiterToBevel = 0;
                   1046:            }
                   1047:        }
                   1048:        if (count == 2) {
                   1049:            TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
                   1050:                    linePtr->capStyle == CapProjecting, poly+4, poly+6);
                   1051:        } else if (linePtr->joinStyle == JoinMiter) {
                   1052:            if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
                   1053:                    (double) linePtr->width, poly+4, poly+6) == 0) {
                   1054:                changedMiterToBevel = 1;
                   1055:                TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
                   1056:                        0, poly+4, poly+6);
                   1057:            }
                   1058:        } else {
                   1059:            TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
                   1060:                    poly+4, poly+6);
                   1061:        }
                   1062:        poly[8] = poly[0];
                   1063:        poly[9] = poly[1];
                   1064:        if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
                   1065:            inside = 0;
                   1066:            goto done;
                   1067:        }
                   1068:     }
                   1069: 
                   1070:     /*
                   1071:      * If caps are rounded, check the cap around the final point
                   1072:      * of the line.
                   1073:      */
                   1074: 
                   1075:     if (linePtr->capStyle == CapRound) {
                   1076:        poly[0] = coordPtr[0] - radius;
                   1077:        poly[1] = coordPtr[1] - radius;
                   1078:        poly[2] = coordPtr[0] + radius;
                   1079:        poly[3] = coordPtr[1] + radius;
                   1080:        if (TkOvalToArea(poly, rectPtr) != inside) {
                   1081:            inside = 0;
                   1082:            goto done;
                   1083:        }
                   1084:     }
                   1085: 
                   1086:     /*
                   1087:      * Check arrowheads, if any.
                   1088:      */
                   1089: 
                   1090:     if (linePtr->arrow != noneUid) {
                   1091:        if (linePtr->arrow != lastUid) {
                   1092:            if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
                   1093:                    rectPtr) != inside) {
                   1094:                inside = 0;
                   1095:                goto done;
                   1096:            }
                   1097:        }
                   1098:        if (linePtr->arrow != firstUid) {
                   1099:            if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
                   1100:                    rectPtr) != inside) {
                   1101:                inside = 0;
                   1102:                goto done;
                   1103:            }
                   1104:        }
                   1105:     }
                   1106: 
                   1107:     done:
                   1108:     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
                   1109:        ckfree((char *) linePoints);
                   1110:     }
                   1111:     return inside;
                   1112: }
                   1113: 
                   1114: /*
                   1115:  *--------------------------------------------------------------
                   1116:  *
                   1117:  * ScaleLine --
                   1118:  *
                   1119:  *     This procedure is invoked to rescale a line item.
                   1120:  *
                   1121:  * Results:
                   1122:  *     None.
                   1123:  *
                   1124:  * Side effects:
                   1125:  *     The line referred to by itemPtr is rescaled so that the
                   1126:  *     following transformation is applied to all point
                   1127:  *     coordinates:
                   1128:  *             x' = originX + scaleX*(x-originX)
                   1129:  *             y' = originY + scaleY*(y-originY)
                   1130:  *
                   1131:  *--------------------------------------------------------------
                   1132:  */
                   1133: 
                   1134: static void
                   1135: ScaleLine(canvasPtr, itemPtr, originX, originY, scaleX, scaleY)
                   1136:     Tk_Canvas *canvasPtr;              /* Canvas containing line. */
                   1137:     Tk_Item *itemPtr;                  /* Line to be scaled. */
                   1138:     double originX, originY;           /* Origin about which to scale rect. */
                   1139:     double scaleX;                     /* Amount to scale in X direction. */
                   1140:     double scaleY;                     /* Amount to scale in Y direction. */
                   1141: {
                   1142:     LineItem *linePtr = (LineItem *) itemPtr;
                   1143:     register double *coordPtr;
                   1144:     int i;
                   1145: 
                   1146:     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
                   1147:            i++, coordPtr += 2) {
                   1148:        coordPtr[0] = originX + scaleX*(*coordPtr - originX);
                   1149:        coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
                   1150:     }
                   1151:     if (linePtr->firstArrowPtr != NULL) {
                   1152:        for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
                   1153:                i++, coordPtr += 2) {
                   1154:            coordPtr[0] = originX + scaleX*(coordPtr[0] - originX);
                   1155:            coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
                   1156:        }
                   1157:     }
                   1158:     if (linePtr->lastArrowPtr != NULL) {
                   1159:        for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
                   1160:                i++, coordPtr += 2) {
                   1161:            coordPtr[0] = originX + scaleX*(coordPtr[0] - originX);
                   1162:            coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
                   1163:        }
                   1164:     }
                   1165:     ComputeLineBbox(canvasPtr, linePtr);
                   1166: }
                   1167: 
                   1168: /*
                   1169:  *--------------------------------------------------------------
                   1170:  *
                   1171:  * TranslateLine --
                   1172:  *
                   1173:  *     This procedure is called to move a line by a given amount.
                   1174:  *
                   1175:  * Results:
                   1176:  *     None.
                   1177:  *
                   1178:  * Side effects:
                   1179:  *     The position of the line is offset by (xDelta, yDelta), and
                   1180:  *     the bounding box is updated in the generic part of the item
                   1181:  *     structure.
                   1182:  *
                   1183:  *--------------------------------------------------------------
                   1184:  */
                   1185: 
                   1186: static void
                   1187: TranslateLine(canvasPtr, itemPtr, deltaX, deltaY)
                   1188:     Tk_Canvas *canvasPtr;              /* Canvas containing item. */
                   1189:     Tk_Item *itemPtr;                  /* Item that is being moved. */
                   1190:     double deltaX, deltaY;             /* Amount by which item is to be
                   1191:                                         * moved. */
                   1192: {
                   1193:     LineItem *linePtr = (LineItem *) itemPtr;
                   1194:     register double *coordPtr;
                   1195:     int i;
                   1196: 
                   1197:     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
                   1198:            i++, coordPtr += 2) {
                   1199:        coordPtr[0] += deltaX;
                   1200:        coordPtr[1] += deltaY;
                   1201:     }
                   1202:     if (linePtr->firstArrowPtr != NULL) {
                   1203:        for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
                   1204:                i++, coordPtr += 2) {
                   1205:            coordPtr[0] += deltaX;
                   1206:            coordPtr[1] += deltaY;
                   1207:        }
                   1208:     }
                   1209:     if (linePtr->lastArrowPtr != NULL) {
                   1210:        for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
                   1211:                i++, coordPtr += 2) {
                   1212:            coordPtr[0] += deltaX;
                   1213:            coordPtr[1] += deltaY;
                   1214:        }
                   1215:     }
                   1216:     ComputeLineBbox(canvasPtr, linePtr);
                   1217: }
                   1218: 
                   1219: /*
                   1220:  *--------------------------------------------------------------
                   1221:  *
                   1222:  * ParseArrowShape --
                   1223:  *
                   1224:  *     This procedure is called back during option parsing to
                   1225:  *     parse arrow shape information.
                   1226:  *
                   1227:  * Results:
                   1228:  *     The return value is a standard Tcl result:  TCL_OK means
                   1229:  *     that the arrow shape information was parsed ok, and
                   1230:  *     TCL_ERROR means it couldn't be parsed.
                   1231:  *
                   1232:  * Side effects:
                   1233:  *     Arrow information in recordPtr is updated.
                   1234:  *
                   1235:  *--------------------------------------------------------------
                   1236:  */
                   1237: 
                   1238:        /* ARGSUSED */
                   1239: static int
                   1240: ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
                   1241:     ClientData clientData;     /* Not used. */
                   1242:     Tcl_Interp *interp;                /* Used for error reporting. */
                   1243:     Tk_Window tkwin;           /* Not used. */
                   1244:     char *value;               /* Textual specification of arrow shape. */
                   1245:     char *recordPtr;           /* Pointer to item record in which to
                   1246:                                 * store arrow information. */
                   1247:     int offset;                        /* Offset of shape information in widget
                   1248:                                 * record. */
                   1249: {
                   1250:     LineItem *linePtr = (LineItem *) recordPtr;
                   1251:     double a, b, c;
                   1252:     int argc;
                   1253:     char **argv = NULL;
                   1254: 
                   1255:     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
                   1256:        panic("ParseArrowShape received bogus offset");
                   1257:     }
                   1258: 
                   1259:     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
                   1260:        syntaxError:
                   1261:        Tcl_ResetResult(interp);
                   1262:        Tcl_AppendResult(interp, "bad arrow shape \"", value,
                   1263:                "\": must be list with three numbers", (char *) NULL);
                   1264:        if (argv != NULL) {
                   1265:            ckfree((char *) argv);
                   1266:        }
                   1267:        return TCL_ERROR;
                   1268:     }
                   1269:     if (argc != 3) {
                   1270:        goto syntaxError;
                   1271:     }
                   1272:     if ((TkGetCanvasCoord(linePtr->canvasPtr, argv[0], &a) != TCL_OK)
                   1273:            || (TkGetCanvasCoord(linePtr->canvasPtr, argv[1], &b) != TCL_OK)
                   1274:            || (TkGetCanvasCoord(linePtr->canvasPtr, argv[2], &c) != TCL_OK)) {
                   1275:        goto syntaxError;
                   1276:     }
                   1277:     linePtr->arrowShapeA = a;
                   1278:     linePtr->arrowShapeB = b;
                   1279:     linePtr->arrowShapeC = c;
                   1280:     ckfree((char *) argv);
                   1281:     return TCL_OK;
                   1282: }
                   1283: 
                   1284: /*
                   1285:  *--------------------------------------------------------------
                   1286:  *
                   1287:  * PrintArrowShape --
                   1288:  *
                   1289:  *     This procedure is a callback invoked by the configuration
                   1290:  *     code to return a printable value describing an arrow shape.
                   1291:  *
                   1292:  * Results:
                   1293:  *     None.
                   1294:  *
                   1295:  * Side effects:
                   1296:  *     None.
                   1297:  *
                   1298:  *--------------------------------------------------------------
                   1299:  */
                   1300: 
                   1301:     /* ARGSUSED */
                   1302: static char *
                   1303: PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)
                   1304:     ClientData clientData;     /* Not used. */
                   1305:     Tk_Window tkwin;           /* Window associated with linePtr's widget. */
                   1306:     char *recordPtr;           /* Pointer to item record containing current
                   1307:                                 * shape information. */
                   1308:     int offset;                        /* Offset of arrow information in record. */
                   1309:     Tcl_FreeProc **freeProcPtr;        /* Store address of procedure to call to
                   1310:                                 * free string here. */
                   1311: {
                   1312:     LineItem *linePtr = (LineItem *) recordPtr;
                   1313:     char *buffer;
                   1314: 
                   1315:     buffer = ckalloc(120);
                   1316:     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
                   1317:            linePtr->arrowShapeB, linePtr->arrowShapeC);
                   1318:     *freeProcPtr = (Tcl_FreeProc *) free;
                   1319:     return buffer;
                   1320: }
                   1321: 
                   1322: /*
                   1323:  *--------------------------------------------------------------
                   1324:  *
                   1325:  * ConfigureArrows --
                   1326:  *
                   1327:  *     If arrowheads have been requested for a line, this
                   1328:  *     procedure makes arrangements for the arrowheads.
                   1329:  *
                   1330:  * Results:
                   1331:  *     A standard Tcl return value.  If an error occurs, then
                   1332:  *     an error message is left in canvasPtr->interp->result.
                   1333:  *
                   1334:  * Side effects:
                   1335:  *     Information in linePtr is set up for one or two arrowheads.
                   1336:  *     the firstArrowPtr and lastArrowPtr polygons are allocated
                   1337:  *     and initialized, if need be, and the end points of the line
                   1338:  *     are adjusted so that a thick line doesn't stick out past
                   1339:  *     the arrowheads.
                   1340:  *
                   1341:  *--------------------------------------------------------------
                   1342:  */
                   1343: 
                   1344:        /* ARGSUSED */
                   1345: static int
                   1346: ConfigureArrows(canvasPtr, linePtr)
                   1347:     Tk_Canvas *canvasPtr;              /* Canvas in which arrows will be
                   1348:                                         * displayed (interp and tkwin
                   1349:                                         * fields are needed). */
                   1350:     register LineItem *linePtr;                /* Item to configure for arrows. */
                   1351: {
                   1352:     double *poly, *coordPtr;
                   1353:     double dx, dy, length, sinTheta, cosTheta, temp, shapeC;
                   1354:     double fracHeight;                 /* Line width as fraction of
                   1355:                                         * arrowhead width. */
                   1356:     double backup;                     /* Distance to backup end points
                   1357:                                         * so the line ends in the middle
                   1358:                                         * of the arrowhead. */
                   1359:     double vertX, vertY;               /* Position of arrowhead vertex. */
                   1360: 
                   1361:     /*
                   1362:      * If there's an arrowhead on the first point of the line, compute
                   1363:      * its polygon and adjust the first point of the line so that the
                   1364:      * line doesn't stick out past the leading edge of the arrowhead.
                   1365:      */
                   1366: 
                   1367:     shapeC = linePtr->arrowShapeC + linePtr->width/2.0;
                   1368:     fracHeight = (linePtr->width/2.0)/shapeC;
                   1369:     backup = fracHeight*linePtr->arrowShapeB
                   1370:            + linePtr->arrowShapeA*(1.0 - fracHeight)/2.0;
                   1371:     if (linePtr->arrow != lastUid) {
                   1372:        poly = linePtr->firstArrowPtr;
                   1373:        if (poly == NULL) {
                   1374:            poly = (double *) ckalloc((unsigned)
                   1375:                    (2*PTS_IN_ARROW*sizeof(double)));
                   1376:            poly[0] = poly[10] = linePtr->coordPtr[0];
                   1377:            poly[1] = poly[11] = linePtr->coordPtr[1];
                   1378:            linePtr->firstArrowPtr = poly;
                   1379:        }
                   1380:        dx = poly[0] - linePtr->coordPtr[2];
                   1381:        dy = poly[1] - linePtr->coordPtr[3];
                   1382:        length = hypot(dx, dy);
                   1383:        if (length == 0) {
                   1384:            sinTheta = cosTheta = 0.0;
                   1385:        } else {
                   1386:            sinTheta = dy/length;
                   1387:            cosTheta = dx/length;
                   1388:        }
                   1389:        vertX = poly[0] - linePtr->arrowShapeA*cosTheta;
                   1390:        vertY = poly[1] - linePtr->arrowShapeA*sinTheta;
                   1391:        temp = shapeC*sinTheta;
                   1392:        poly[2] = poly[0] - linePtr->arrowShapeB*cosTheta + temp;
                   1393:        poly[8] = poly[2] - 2*temp;
                   1394:        temp = shapeC*cosTheta;
                   1395:        poly[3] = poly[1] - linePtr->arrowShapeB*sinTheta - temp;
                   1396:        poly[9] = poly[3] + 2*temp;
                   1397:        poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
                   1398:        poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
                   1399:        poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
                   1400:        poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
                   1401: 
                   1402:        /*
                   1403:         * Polygon done.  Now move the first point towards the second so
                   1404:         * that the corners at the end of the line are inside the
                   1405:         * arrowhead.
                   1406:         */
                   1407: 
                   1408:        linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
                   1409:        linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
                   1410:     }
                   1411: 
                   1412:     /*
                   1413:      * Similar arrowhead calculation for the last point of the line.
                   1414:      */
                   1415: 
                   1416:     if (linePtr->arrow != firstUid) {
                   1417:        coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
                   1418:        poly = linePtr->lastArrowPtr;
                   1419:        if (poly == NULL) {
                   1420:            poly = (double *) ckalloc((unsigned)
                   1421:                    (2*PTS_IN_ARROW*sizeof(double)));
                   1422:            poly[0] = poly[10] = coordPtr[2];
                   1423:            poly[1] = poly[11] = coordPtr[3];
                   1424:            linePtr->lastArrowPtr = poly;
                   1425:        }
                   1426:        dx = poly[0] - coordPtr[0];
                   1427:        dy = poly[1] - coordPtr[1];
                   1428:        length = hypot(dx, dy);
                   1429:        if (length == 0) {
                   1430:            sinTheta = cosTheta = 0.0;
                   1431:        } else {
                   1432:            sinTheta = dy/length;
                   1433:            cosTheta = dx/length;
                   1434:        }
                   1435:        vertX = poly[0] - linePtr->arrowShapeA*cosTheta;
                   1436:        vertY = poly[1] - linePtr->arrowShapeA*sinTheta;
                   1437:        temp = shapeC*sinTheta;
                   1438:        poly[2] = poly[0] - linePtr->arrowShapeB*cosTheta + temp;
                   1439:        poly[8] = poly[2] - 2*temp;
                   1440:        temp = shapeC*cosTheta;
                   1441:        poly[3] = poly[1] - linePtr->arrowShapeB*sinTheta - temp;
                   1442:        poly[9] = poly[3] + 2*temp;
                   1443:        poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
                   1444:        poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
                   1445:        poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
                   1446:        poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
                   1447:        coordPtr[2] = poly[0] - backup*cosTheta;
                   1448:        coordPtr[3] = poly[1] - backup*sinTheta;
                   1449:     }
                   1450: 
                   1451:     return TCL_OK;
                   1452: }

unix.superglobalmegacorp.com

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