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

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