home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkCanvLine.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  49KB  |  1,623 lines

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