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