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-1997 Sun Microsystems, Inc. 8 * Copyright (c) 1998-1999 by Scriptics Corporation. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include <stdio.h> 17#include "tkInt.h" 18#include "tkCanvas.h" 19 20/* 21 * The structure below defines the record for each line item. 22 */ 23 24typedef enum { 25 ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH 26} Arrows; 27 28typedef struct LineItem { 29 Tk_Item header; /* Generic stuff that's the same for all 30 * types. MUST BE FIRST IN STRUCTURE. */ 31 Tk_Outline outline; /* Outline structure */ 32 Tk_Canvas canvas; /* Canvas containing item. Needed for parsing 33 * arrow shapes. */ 34 int numPoints; /* Number of points in line (always >= 0). */ 35 double *coordPtr; /* Pointer to malloc-ed array containing x- 36 * and y-coords of all points in line. 37 * X-coords are even-valued indices, y-coords 38 * are corresponding odd-valued indices. If 39 * the line has arrowheads then the first and 40 * last points have been adjusted to refer to 41 * the necks of the arrowheads rather than 42 * their tips. The actual endpoints are stored 43 * in the *firstArrowPtr and *lastArrowPtr, if 44 * they exist. */ 45 int capStyle; /* Cap style for line. */ 46 int joinStyle; /* Join style for line. */ 47 GC arrowGC; /* Graphics context for drawing arrowheads. */ 48 Arrows arrow; /* Indicates whether or not to draw arrowheads: 49 * "none", "first", "last", or "both". */ 50 float arrowShapeA; /* Distance from tip of arrowhead to center. */ 51 float arrowShapeB; /* Distance from tip of arrowhead to trailing 52 * point, measured along shaft. */ 53 float arrowShapeC; /* Distance of trailing points from outside 54 * edge of shaft. */ 55 double *firstArrowPtr; /* Points to array of PTS_IN_ARROW points 56 * describing polygon for arrowhead at first 57 * point in line. First point of arrowhead is 58 * tip. Malloc'ed. NULL means no arrowhead at 59 * first point. */ 60 double *lastArrowPtr; /* Points to polygon for arrowhead at last 61 * point in line (PTS_IN_ARROW points, first 62 * of which is tip). Malloc'ed. NULL means no 63 * arrowhead at last point. */ 64 Tk_SmoothMethod *smooth; /* Non-zero means draw line smoothed (i.e. 65 * with Bezier splines). */ 66 int splineSteps; /* Number of steps in each spline segment. */ 67} LineItem; 68 69/* 70 * Number of points in an arrowHead: 71 */ 72 73#define PTS_IN_ARROW 6 74 75/* 76 * Prototypes for functions defined in this file: 77 */ 78 79static int ArrowheadPostscript(Tcl_Interp *interp, 80 Tk_Canvas canvas, LineItem *linePtr, 81 double *arrowPtr); 82static void ComputeLineBbox(Tk_Canvas canvas, LineItem *linePtr); 83static int ConfigureLine(Tcl_Interp *interp, 84 Tk_Canvas canvas, Tk_Item *itemPtr, int objc, 85 Tcl_Obj *CONST objv[], int flags); 86static int ConfigureArrows(Tk_Canvas canvas, LineItem *linePtr); 87static int CreateLine(Tcl_Interp *interp, 88 Tk_Canvas canvas, struct Tk_Item *itemPtr, 89 int objc, Tcl_Obj *CONST objv[]); 90static void DeleteLine(Tk_Canvas canvas, 91 Tk_Item *itemPtr, Display *display); 92static void DisplayLine(Tk_Canvas canvas, 93 Tk_Item *itemPtr, Display *display, Drawable dst, 94 int x, int y, int width, int height); 95static int GetLineIndex(Tcl_Interp *interp, 96 Tk_Canvas canvas, Tk_Item *itemPtr, 97 Tcl_Obj *obj, int *indexPtr); 98static int LineCoords(Tcl_Interp *interp, 99 Tk_Canvas canvas, Tk_Item *itemPtr, 100 int objc, Tcl_Obj *CONST objv[]); 101static void LineDeleteCoords(Tk_Canvas canvas, 102 Tk_Item *itemPtr, int first, int last); 103static void LineInsert(Tk_Canvas canvas, 104 Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj); 105static int LineToArea(Tk_Canvas canvas, 106 Tk_Item *itemPtr, double *rectPtr); 107static double LineToPoint(Tk_Canvas canvas, 108 Tk_Item *itemPtr, double *coordPtr); 109static int LineToPostscript(Tcl_Interp *interp, 110 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass); 111static int ArrowParseProc(ClientData clientData, 112 Tcl_Interp *interp, Tk_Window tkwin, 113 CONST char *value, char *recordPtr, int offset); 114static char * ArrowPrintProc(ClientData clientData, 115 Tk_Window tkwin, char *recordPtr, int offset, 116 Tcl_FreeProc **freeProcPtr); 117static int ParseArrowShape(ClientData clientData, 118 Tcl_Interp *interp, Tk_Window tkwin, 119 CONST char *value, char *recordPtr, int offset); 120static char * PrintArrowShape(ClientData clientData, 121 Tk_Window tkwin, char *recordPtr, int offset, 122 Tcl_FreeProc **freeProcPtr); 123static void ScaleLine(Tk_Canvas canvas, 124 Tk_Item *itemPtr, double originX, double originY, 125 double scaleX, double scaleY); 126static void TranslateLine(Tk_Canvas canvas, 127 Tk_Item *itemPtr, double deltaX, double deltaY); 128 129/* 130 * Information used for parsing configuration specs. If you change any of the 131 * default strings, be sure to change the corresponding default values in 132 * CreateLine. 133 */ 134 135static Tk_CustomOption arrowShapeOption = { 136 (Tk_OptionParseProc *) ParseArrowShape, 137 PrintArrowShape, (ClientData) NULL 138}; 139static Tk_CustomOption arrowOption = { 140 (Tk_OptionParseProc *) ArrowParseProc, 141 ArrowPrintProc, (ClientData) NULL 142}; 143static Tk_CustomOption smoothOption = { 144 (Tk_OptionParseProc *) TkSmoothParseProc, 145 TkSmoothPrintProc, (ClientData) NULL 146}; 147static Tk_CustomOption stateOption = { 148 (Tk_OptionParseProc *) TkStateParseProc, 149 TkStatePrintProc, (ClientData) 2 150}; 151static Tk_CustomOption tagsOption = { 152 (Tk_OptionParseProc *) Tk_CanvasTagsParseProc, 153 Tk_CanvasTagsPrintProc, (ClientData) NULL 154}; 155static Tk_CustomOption dashOption = { 156 (Tk_OptionParseProc *) TkCanvasDashParseProc, 157 TkCanvasDashPrintProc, (ClientData) NULL 158}; 159static Tk_CustomOption offsetOption = { 160 (Tk_OptionParseProc *) TkOffsetParseProc, 161 TkOffsetPrintProc, 162 (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX) 163}; 164static Tk_CustomOption pixelOption = { 165 (Tk_OptionParseProc *) TkPixelParseProc, 166 TkPixelPrintProc, (ClientData) NULL 167}; 168 169static Tk_ConfigSpec configSpecs[] = { 170 {TK_CONFIG_CUSTOM, "-activedash", NULL, NULL, 171 NULL, Tk_Offset(LineItem, outline.activeDash), 172 TK_CONFIG_NULL_OK, &dashOption}, 173 {TK_CONFIG_COLOR, "-activefill", NULL, NULL, 174 NULL, Tk_Offset(LineItem, outline.activeColor), TK_CONFIG_NULL_OK}, 175 {TK_CONFIG_BITMAP, "-activestipple", NULL, NULL, 176 NULL, Tk_Offset(LineItem, outline.activeStipple), TK_CONFIG_NULL_OK}, 177 {TK_CONFIG_CUSTOM, "-activewidth", NULL, NULL, 178 "0.0", Tk_Offset(LineItem, outline.activeWidth), 179 TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, 180 {TK_CONFIG_CUSTOM, "-arrow", NULL, NULL, 181 "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT, &arrowOption}, 182 {TK_CONFIG_CUSTOM, "-arrowshape", NULL, NULL, 183 "8 10 3", Tk_Offset(LineItem, arrowShapeA), 184 TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption}, 185 {TK_CONFIG_CAP_STYLE, "-capstyle", NULL, NULL, 186 "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT}, 187 {TK_CONFIG_COLOR, "-fill", NULL, NULL, 188 "black", Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK}, 189 {TK_CONFIG_CUSTOM, "-dash", NULL, NULL, 190 NULL, Tk_Offset(LineItem, outline.dash), 191 TK_CONFIG_NULL_OK, &dashOption}, 192 {TK_CONFIG_PIXELS, "-dashoffset", NULL, NULL, 193 "0", Tk_Offset(LineItem, outline.offset), TK_CONFIG_DONT_SET_DEFAULT}, 194 {TK_CONFIG_CUSTOM, "-disableddash", NULL, NULL, 195 NULL, Tk_Offset(LineItem, outline.disabledDash), 196 TK_CONFIG_NULL_OK, &dashOption}, 197 {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL, 198 NULL, Tk_Offset(LineItem, outline.disabledColor), TK_CONFIG_NULL_OK}, 199 {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL, 200 NULL, Tk_Offset(LineItem, outline.disabledStipple), TK_CONFIG_NULL_OK}, 201 {TK_CONFIG_CUSTOM, "-disabledwidth", NULL, NULL, 202 "0.0", Tk_Offset(LineItem, outline.disabledWidth), 203 TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, 204 {TK_CONFIG_JOIN_STYLE, "-joinstyle", NULL, NULL, 205 "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, 206 {TK_CONFIG_CUSTOM, "-offset", NULL, NULL, 207 "0,0", Tk_Offset(LineItem, outline.tsoffset), 208 TK_CONFIG_DONT_SET_DEFAULT, &offsetOption}, 209 {TK_CONFIG_CUSTOM, "-smooth", NULL, NULL, 210 "0", Tk_Offset(LineItem, smooth), 211 TK_CONFIG_DONT_SET_DEFAULT, &smoothOption}, 212 {TK_CONFIG_INT, "-splinesteps", NULL, NULL, 213 "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT}, 214 {TK_CONFIG_CUSTOM, "-state", NULL, NULL, 215 NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, &stateOption}, 216 {TK_CONFIG_BITMAP, "-stipple", NULL, NULL, 217 NULL, Tk_Offset(LineItem, outline.stipple), TK_CONFIG_NULL_OK}, 218 {TK_CONFIG_CUSTOM, "-tags", NULL, NULL, 219 NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, 220 {TK_CONFIG_CUSTOM, "-width", NULL, NULL, 221 "1.0", Tk_Offset(LineItem, outline.width), 222 TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, 223 {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} 224}; 225 226/* 227 * The structures below defines the line item type by means of functions that 228 * can be invoked by generic item code. 229 */ 230 231Tk_ItemType tkLineType = { 232 "line", /* name */ 233 sizeof(LineItem), /* itemSize */ 234 CreateLine, /* createProc */ 235 configSpecs, /* configSpecs */ 236 ConfigureLine, /* configureProc */ 237 LineCoords, /* coordProc */ 238 DeleteLine, /* deleteProc */ 239 DisplayLine, /* displayProc */ 240 TK_CONFIG_OBJS, /* flags */ 241 LineToPoint, /* pointProc */ 242 LineToArea, /* areaProc */ 243 LineToPostscript, /* postscriptProc */ 244 ScaleLine, /* scaleProc */ 245 TranslateLine, /* translateProc */ 246 (Tk_ItemIndexProc *) GetLineIndex, /* indexProc */ 247 NULL, /* icursorProc */ 248 NULL, /* selectionProc */ 249 (Tk_ItemInsertProc *) LineInsert, /* insertProc */ 250 LineDeleteCoords, /* dTextProc */ 251 NULL, /* nextPtr */ 252}; 253 254/* 255 * The definition below determines how large are static arrays used to hold 256 * spline points (splines larger than this have to have their arrays 257 * malloc-ed). 258 */ 259 260#define MAX_STATIC_POINTS 200 261 262/* 263 *-------------------------------------------------------------- 264 * 265 * CreateLine -- 266 * 267 * This function is invoked to create a new line item in a canvas. 268 * 269 * Results: 270 * A standard Tcl return value. If an error occurred in creating the 271 * item, then an error message is left in the interp's result; in this 272 * case itemPtr is left uninitialized, so it can be safely freed by the 273 * caller. 274 * 275 * Side effects: 276 * A new line item is created. 277 * 278 *-------------------------------------------------------------- 279 */ 280 281static int 282CreateLine( 283 Tcl_Interp *interp, /* Interpreter for error reporting. */ 284 Tk_Canvas canvas, /* Canvas to hold new item. */ 285 Tk_Item *itemPtr, /* Record to hold new item; header has been 286 * initialized by caller. */ 287 int objc, /* Number of arguments in objv. */ 288 Tcl_Obj *CONST objv[]) /* Arguments describing line. */ 289{ 290 LineItem *linePtr = (LineItem *) itemPtr; 291 int i; 292 293 if (objc == 0) { 294 Tcl_Panic("canvas did not pass any coords\n"); 295 } 296 297 /* 298 * Carry out initialization that is needed to set defaults and to allow 299 * proper cleanup after errors during the the remainder of this function. 300 */ 301 302 Tk_CreateOutline(&(linePtr->outline)); 303 linePtr->canvas = canvas; 304 linePtr->numPoints = 0; 305 linePtr->coordPtr = NULL; 306 linePtr->capStyle = CapButt; 307 linePtr->joinStyle = JoinRound; 308 linePtr->arrowGC = None; 309 linePtr->arrow = ARROWS_NONE; 310 linePtr->arrowShapeA = (float)8.0; 311 linePtr->arrowShapeB = (float)10.0; 312 linePtr->arrowShapeC = (float)3.0; 313 linePtr->firstArrowPtr = NULL; 314 linePtr->lastArrowPtr = NULL; 315 linePtr->smooth = NULL; 316 linePtr->splineSteps = 12; 317 318 /* 319 * Count the number of points and then parse them into a point array. 320 * Leading arguments are assumed to be points if they start with a digit 321 * or a minus sign followed by a digit. 322 */ 323 324 for (i = 1; i < objc; i++) { 325 char *arg = Tcl_GetString(objv[i]); 326 327 if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) { 328 break; 329 } 330 } 331 if (LineCoords(interp, canvas, itemPtr, i, objv) != TCL_OK) { 332 goto error; 333 } 334 if (ConfigureLine(interp, canvas, itemPtr, objc-i, objv+i, 0) == TCL_OK) { 335 return TCL_OK; 336 } 337 338 error: 339 DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas))); 340 return TCL_ERROR; 341} 342 343/* 344 *-------------------------------------------------------------- 345 * 346 * LineCoords -- 347 * 348 * This function is invoked to process the "coords" widget command on 349 * lines. See the user documentation for details on what it does. 350 * 351 * Results: 352 * Returns TCL_OK or TCL_ERROR, and sets the interp's result. 353 * 354 * Side effects: 355 * The coordinates for the given item may be changed. 356 * 357 *-------------------------------------------------------------- 358 */ 359 360static int 361LineCoords( 362 Tcl_Interp *interp, /* Used for error reporting. */ 363 Tk_Canvas canvas, /* Canvas containing item. */ 364 Tk_Item *itemPtr, /* Item whose coordinates are to be read or 365 * modified. */ 366 int objc, /* Number of coordinates supplied in objv. */ 367 Tcl_Obj *CONST objv[]) /* Array of coordinates: x1, y1, x2, y2, ... */ 368{ 369 LineItem *linePtr = (LineItem *) itemPtr; 370 int i, numPoints; 371 double *coordPtr; 372 373 if (objc == 0) { 374 int numCoords; 375 Tcl_Obj *subobj, *obj = Tcl_NewObj(); 376 377 numCoords = 2*linePtr->numPoints; 378 if (linePtr->firstArrowPtr != NULL) { 379 coordPtr = linePtr->firstArrowPtr; 380 } else { 381 coordPtr = linePtr->coordPtr; 382 } 383 for (i = 0; i < numCoords; i++, coordPtr++) { 384 if (i == 2) { 385 coordPtr = linePtr->coordPtr+2; 386 } 387 if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) { 388 coordPtr = linePtr->lastArrowPtr; 389 } 390 subobj = Tcl_NewDoubleObj(*coordPtr); 391 Tcl_ListObjAppendElement(interp, obj, subobj); 392 } 393 Tcl_SetObjResult(interp, obj); 394 return TCL_OK; 395 } 396 if (objc == 1) { 397 if (Tcl_ListObjGetElements(interp, objv[0], &objc, 398 (Tcl_Obj ***) &objv) != TCL_OK) { 399 return TCL_ERROR; 400 } 401 } 402 if (objc & 1) { 403 char buf[64 + TCL_INTEGER_SPACE]; 404 405 sprintf(buf, "wrong # coordinates: expected an even number, got %d", 406 objc); 407 Tcl_SetResult(interp, buf, TCL_VOLATILE); 408 return TCL_ERROR; 409 } else if (objc < 4) { 410 char buf[64 + TCL_INTEGER_SPACE]; 411 412 sprintf(buf, "wrong # coordinates: expected at least 4, got %d", objc); 413 Tcl_SetResult(interp, buf, TCL_VOLATILE); 414 return TCL_ERROR; 415 } else { 416 numPoints = objc/2; 417 if (linePtr->numPoints != numPoints) { 418 coordPtr = (double *) 419 ckalloc((unsigned) (sizeof(double) * objc)); 420 if (linePtr->coordPtr != NULL) { 421 ckfree((char *) linePtr->coordPtr); 422 } 423 linePtr->coordPtr = coordPtr; 424 linePtr->numPoints = numPoints; 425 } 426 coordPtr = linePtr->coordPtr; 427 for (i = 0; i <objc; i++) { 428 if (Tk_CanvasGetCoordFromObj(interp, canvas, objv[i], 429 coordPtr++) != TCL_OK) { 430 return TCL_ERROR; 431 } 432 } 433 434 /* 435 * Update arrowheads by throwing away any existing arrow-head 436 * information and calling ConfigureArrows to recompute it. 437 */ 438 439 if (linePtr->firstArrowPtr != NULL) { 440 ckfree((char *) linePtr->firstArrowPtr); 441 linePtr->firstArrowPtr = NULL; 442 } 443 if (linePtr->lastArrowPtr != NULL) { 444 ckfree((char *) linePtr->lastArrowPtr); 445 linePtr->lastArrowPtr = NULL; 446 } 447 if (linePtr->arrow != ARROWS_NONE) { 448 ConfigureArrows(canvas, linePtr); 449 } 450 ComputeLineBbox(canvas, linePtr); 451 } 452 return TCL_OK; 453} 454 455/* 456 *-------------------------------------------------------------- 457 * 458 * ConfigureLine -- 459 * 460 * This function is invoked to configure various aspects of a line item 461 * such as its background color. 462 * 463 * Results: 464 * A standard Tcl result code. If an error occurs, then an error message 465 * is left in the interp's result. 466 * 467 * Side effects: 468 * Configuration information, such as colors and stipple patterns, may be 469 * set for itemPtr. 470 * 471 *-------------------------------------------------------------- 472 */ 473 474static int 475ConfigureLine( 476 Tcl_Interp *interp, /* Used for error reporting. */ 477 Tk_Canvas canvas, /* Canvas containing itemPtr. */ 478 Tk_Item *itemPtr, /* Line item to reconfigure. */ 479 int objc, /* Number of elements in objv. */ 480 Tcl_Obj *CONST objv[], /* Arguments describing things to configure. */ 481 int flags) /* Flags to pass to Tk_ConfigureWidget. */ 482{ 483 LineItem *linePtr = (LineItem *) itemPtr; 484 XGCValues gcValues; 485 GC newGC, arrowGC; 486 unsigned long mask; 487 Tk_Window tkwin; 488 Tk_State state; 489 490 tkwin = Tk_CanvasTkwin(canvas); 491 if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc, 492 (CONST char **) objv, (char *) linePtr, flags|TK_CONFIG_OBJS)) { 493 return TCL_ERROR; 494 } 495 496 /* 497 * A few of the options require additional processing, such as graphics 498 * contexts. 499 */ 500 501 state = itemPtr->state; 502 503 if(state == TK_STATE_NULL) { 504 state = ((TkCanvas *)canvas)->canvas_state; 505 } 506 507 if (linePtr->outline.activeWidth > linePtr->outline.width || 508 linePtr->outline.activeDash.number != 0 || 509 linePtr->outline.activeColor != NULL || 510 linePtr->outline.activeStipple != None) { 511 itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT; 512 } else { 513 itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; 514 } 515 mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, 516 &(linePtr->outline)); 517 if (mask) { 518 if (linePtr->arrow == ARROWS_NONE) { 519 gcValues.cap_style = linePtr->capStyle; 520 mask |= GCCapStyle; 521 } 522 gcValues.join_style = linePtr->joinStyle; 523 mask |= GCJoinStyle; 524 newGC = Tk_GetGC(tkwin, mask, &gcValues); 525#ifdef MAC_OSX_TK 526 /* 527 * Mac OS X CG drawing needs access to linewidth even for 528 * arrow fills (as linewidth controls antialiasing). 529 */ 530 mask |= GCLineWidth; 531#else 532 gcValues.line_width = 0; 533#endif 534 arrowGC = Tk_GetGC(tkwin, mask, &gcValues); 535 } else { 536 newGC = arrowGC = None; 537 } 538 if (linePtr->outline.gc != None) { 539 Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc); 540 } 541 if (linePtr->arrowGC != None) { 542 Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC); 543 } 544 linePtr->outline.gc = newGC; 545 linePtr->arrowGC = arrowGC; 546 547 /* 548 * Keep spline parameters within reasonable limits. 549 */ 550 551 if (linePtr->splineSteps < 1) { 552 linePtr->splineSteps = 1; 553 } else if (linePtr->splineSteps > 100) { 554 linePtr->splineSteps = 100; 555 } 556 557 if ((!linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { 558 ComputeLineBbox(canvas, linePtr); 559 return TCL_OK; 560 } 561 562 /* 563 * Setup arrowheads, if needed. If arrowheads are turned off, restore the 564 * line's endpoints (they were shortened when the arrowheads were added). 565 */ 566 567 if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST) 568 && (linePtr->arrow != ARROWS_BOTH)) { 569 linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; 570 linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; 571 ckfree((char *) linePtr->firstArrowPtr); 572 linePtr->firstArrowPtr = NULL; 573 } 574 if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST) 575 && (linePtr->arrow != ARROWS_BOTH)) { 576 int i; 577 578 i = 2*(linePtr->numPoints-1); 579 linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; 580 linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; 581 ckfree((char *) linePtr->lastArrowPtr); 582 linePtr->lastArrowPtr = NULL; 583 } 584 if (linePtr->arrow != ARROWS_NONE) { 585 ConfigureArrows(canvas, linePtr); 586 } 587 588 /* 589 * Recompute bounding box for line. 590 */ 591 592 ComputeLineBbox(canvas, linePtr); 593 594 return TCL_OK; 595} 596 597/* 598 *-------------------------------------------------------------- 599 * 600 * DeleteLine -- 601 * 602 * This function is called to clean up the data structure associated with 603 * a line item. 604 * 605 * Results: 606 * None. 607 * 608 * Side effects: 609 * Resources associated with itemPtr are released. 610 * 611 *-------------------------------------------------------------- 612 */ 613 614static void 615DeleteLine( 616 Tk_Canvas canvas, /* Info about overall canvas widget. */ 617 Tk_Item *itemPtr, /* Item that is being deleted. */ 618 Display *display) /* Display containing window for canvas. */ 619{ 620 LineItem *linePtr = (LineItem *) itemPtr; 621 622 Tk_DeleteOutline(display, &(linePtr->outline)); 623 if (linePtr->coordPtr != NULL) { 624 ckfree((char *) linePtr->coordPtr); 625 } 626 if (linePtr->arrowGC != None) { 627 Tk_FreeGC(display, linePtr->arrowGC); 628 } 629 if (linePtr->firstArrowPtr != NULL) { 630 ckfree((char *) linePtr->firstArrowPtr); 631 } 632 if (linePtr->lastArrowPtr != NULL) { 633 ckfree((char *) linePtr->lastArrowPtr); 634 } 635} 636 637/* 638 *-------------------------------------------------------------- 639 * 640 * ComputeLineBbox -- 641 * 642 * This function is invoked to compute the bounding box of all the pixels 643 * that may be drawn as part of a line. 644 * 645 * Results: 646 * None. 647 * 648 * Side effects: 649 * The fields x1, y1, x2, and y2 are updated in the header for itemPtr. 650 * 651 *-------------------------------------------------------------- 652 */ 653 654static void 655ComputeLineBbox( 656 Tk_Canvas canvas, /* Canvas that contains item. */ 657 LineItem *linePtr) /* Item whose bbos is to be recomputed. */ 658{ 659 double *coordPtr; 660 int i, intWidth; 661 double width; 662 Tk_State state = linePtr->header.state; 663 Tk_TSOffset *tsoffset; 664 665 if(state == TK_STATE_NULL) { 666 state = ((TkCanvas *)canvas)->canvas_state; 667 } 668 669 if (!(linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { 670 linePtr->header.x1 = -1; 671 linePtr->header.x2 = -1; 672 linePtr->header.y1 = -1; 673 linePtr->header.y2 = -1; 674 return; 675 } 676 677 width = linePtr->outline.width; 678 if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { 679 if (linePtr->outline.activeWidth>width) { 680 width = linePtr->outline.activeWidth; 681 } 682 } else if (state==TK_STATE_DISABLED) { 683 if (linePtr->outline.disabledWidth>0) { 684 width = linePtr->outline.disabledWidth; 685 } 686 } 687 688 coordPtr = linePtr->coordPtr; 689 linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr; 690 linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1]; 691 692 /* 693 * Compute the bounding box of all the points in the line, then expand in 694 * all directions by the line's width to take care of butting or rounded 695 * corners and projecting or rounded caps. This expansion is an 696 * overestimate (worst-case is square root of two over two) but it's 697 * simple. eDon't do anything special for curves. This causes an 698 * additional overestimate in the bounding box, but is faster. 699 */ 700 701 for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints; 702 i++, coordPtr += 2) { 703 TkIncludePoint((Tk_Item *) linePtr, coordPtr); 704 } 705 width = linePtr->outline.width; 706 if (width < 1.0) { 707 width = 1.0; 708 } 709 if (linePtr->arrow != ARROWS_NONE) { 710 if (linePtr->arrow != ARROWS_LAST) { 711 TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr); 712 } 713 if (linePtr->arrow != ARROWS_FIRST) { 714 TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr); 715 } 716 } 717 718 tsoffset = &linePtr->outline.tsoffset; 719 if (tsoffset->flags & TK_OFFSET_INDEX) { 720 double *coordPtr = linePtr->coordPtr + (tsoffset->flags & ~TK_OFFSET_INDEX); 721 if (tsoffset->flags <= 0) { 722 coordPtr = linePtr->coordPtr; 723 if ((linePtr->arrow == ARROWS_FIRST) || (linePtr->arrow == ARROWS_BOTH)) { 724 coordPtr = linePtr->firstArrowPtr; 725 } 726 } 727 if (tsoffset->flags > (linePtr->numPoints * 2)) { 728 coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2); 729 if ((linePtr->arrow == ARROWS_LAST) || (linePtr->arrow == ARROWS_BOTH)) { 730 coordPtr = linePtr->lastArrowPtr; 731 } 732 } 733 tsoffset->xoffset = (int) (coordPtr[0] + 0.5); 734 tsoffset->yoffset = (int) (coordPtr[1] + 0.5); 735 } else { 736 if (tsoffset->flags & TK_OFFSET_LEFT) { 737 tsoffset->xoffset = linePtr->header.x1; 738 } else if (tsoffset->flags & TK_OFFSET_CENTER) { 739 tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2; 740 } else if (tsoffset->flags & TK_OFFSET_RIGHT) { 741 tsoffset->xoffset = linePtr->header.x2; 742 } 743 if (tsoffset->flags & TK_OFFSET_TOP) { 744 tsoffset->yoffset = linePtr->header.y1; 745 } else if (tsoffset->flags & TK_OFFSET_MIDDLE) { 746 tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2; 747 } else if (tsoffset->flags & TK_OFFSET_BOTTOM) { 748 tsoffset->yoffset = linePtr->header.y2; 749 } 750 } 751 752 intWidth = (int) (width + 0.5); 753 linePtr->header.x1 -= intWidth; 754 linePtr->header.x2 += intWidth; 755 linePtr->header.y1 -= intWidth; 756 linePtr->header.y2 += intWidth; 757 758 if (linePtr->numPoints==1) { 759 linePtr->header.x1 -= 1; 760 linePtr->header.x2 += 1; 761 linePtr->header.y1 -= 1; 762 linePtr->header.y2 += 1; 763 return; 764 } 765 766 /* 767 * For mitered lines, make a second pass through all the points. Compute 768 * the locations of the two miter vertex points and add those into the 769 * bounding box. 770 */ 771 772 if (linePtr->joinStyle == JoinMiter) { 773 for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3; 774 i--, coordPtr += 2) { 775 double miter[4]; 776 int j; 777 778 if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, 779 width, miter, miter+2)) { 780 for (j = 0; j < 4; j += 2) { 781 TkIncludePoint((Tk_Item *) linePtr, miter+j); 782 } 783 } 784 } 785 } 786 787 /* 788 * Add in the sizes of arrowheads, if any. 789 */ 790 791 if (linePtr->arrow != ARROWS_NONE) { 792 if (linePtr->arrow != ARROWS_LAST) { 793 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 794 i++, coordPtr += 2) { 795 TkIncludePoint((Tk_Item *) linePtr, coordPtr); 796 } 797 } 798 if (linePtr->arrow != ARROWS_FIRST) { 799 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 800 i++, coordPtr += 2) { 801 TkIncludePoint((Tk_Item *) linePtr, coordPtr); 802 } 803 } 804 } 805 806 /* 807 * Add one more pixel of fudge factor just to be safe (e.g. X may round 808 * differently than we do). 809 */ 810 811 linePtr->header.x1 -= 1; 812 linePtr->header.x2 += 1; 813 linePtr->header.y1 -= 1; 814 linePtr->header.y2 += 1; 815} 816 817/* 818 *-------------------------------------------------------------- 819 * 820 * DisplayLine -- 821 * 822 * This function is invoked to draw a line item in a given drawable. 823 * 824 * Results: 825 * None. 826 * 827 * Side effects: 828 * ItemPtr is drawn in drawable using the transformation information in 829 * canvas. 830 * 831 *-------------------------------------------------------------- 832 */ 833 834static void 835DisplayLine( 836 Tk_Canvas canvas, /* Canvas that contains item. */ 837 Tk_Item *itemPtr, /* Item to be displayed. */ 838 Display *display, /* Display on which to draw item. */ 839 Drawable drawable, /* Pixmap or window in which to draw item. */ 840 int x, int y, int width, int height) 841 /* Describes region of canvas that must be 842 * redisplayed (not used). */ 843{ 844 LineItem *linePtr = (LineItem *) itemPtr; 845 XPoint staticPoints[MAX_STATIC_POINTS*3]; 846 XPoint *pointPtr; 847 double linewidth; 848 int numPoints; 849 Tk_State state = itemPtr->state; 850 851 if ((!linePtr->numPoints)||(linePtr->outline.gc==None)) { 852 return; 853 } 854 855 if (state == TK_STATE_NULL) { 856 state = ((TkCanvas *)canvas)->canvas_state; 857 } 858 linewidth = linePtr->outline.width; 859 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 860 if (linePtr->outline.activeWidth != linewidth) { 861 linewidth = linePtr->outline.activeWidth; 862 } 863 } else if (state==TK_STATE_DISABLED) { 864 if (linePtr->outline.disabledWidth != linewidth) { 865 linewidth = linePtr->outline.disabledWidth; 866 } 867 } 868 /* 869 * Build up an array of points in screen coordinates. Use a static array 870 * unless the line has an enormous number of points; in this case, 871 * dynamically allocate an array. For smoothed lines, generate the curve 872 * points on each redisplay. 873 */ 874 875 if ((linePtr->smooth) && (linePtr->numPoints > 2)) { 876 numPoints = linePtr->smooth->coordProc(canvas, NULL, 877 linePtr->numPoints, linePtr->splineSteps, NULL, NULL); 878 } else { 879 numPoints = linePtr->numPoints; 880 } 881 882 if (numPoints <= MAX_STATIC_POINTS) { 883 pointPtr = staticPoints; 884 } else { 885 pointPtr = (XPoint *)ckalloc((unsigned)(numPoints * 3*sizeof(XPoint))); 886 } 887 888 if ((linePtr->smooth) && (linePtr->numPoints > 2)) { 889 numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, 890 linePtr->numPoints, linePtr->splineSteps, pointPtr, NULL); 891 } else { 892 numPoints = TkCanvTranslatePath((TkCanvas*)canvas, numPoints, 893 linePtr->coordPtr, 0, pointPtr); 894 } 895 896 /* 897 * Display line, the free up line storage if it was dynamically allocated. 898 * If we're stippling, then modify the stipple offset in the GC. Be sure 899 * to reset the offset when done, since the GC is supposed to be 900 * read-only. 901 */ 902 903 if (Tk_ChangeOutlineGC(canvas, itemPtr, &(linePtr->outline))) { 904 Tk_CanvasSetOffset(canvas, linePtr->arrowGC, &linePtr->outline.tsoffset); 905 } 906 if (numPoints>1) { 907 XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints, 908 CoordModeOrigin); 909 } else { 910 int intwidth = (int) (linewidth + 0.5); 911 if (intwidth<1) { 912 intwidth=1; 913 } 914 XFillArc(display, drawable, linePtr->outline.gc, 915 pointPtr->x - intwidth/2, pointPtr->y - intwidth/2, 916 (unsigned int)intwidth+1, (unsigned int)intwidth+1, 0, 64*360); 917 } 918 if (pointPtr != staticPoints) { 919 ckfree((char *) pointPtr); 920 } 921 922 /* 923 * Display arrowheads, if they are wanted. 924 */ 925 926 if (linePtr->firstArrowPtr != NULL) { 927 TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW, 928 display, drawable, linePtr->arrowGC, NULL); 929 } 930 if (linePtr->lastArrowPtr != NULL) { 931 TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW, 932 display, drawable, linePtr->arrowGC, NULL); 933 } 934 if (Tk_ResetOutlineGC(canvas, itemPtr, &(linePtr->outline))) { 935 XSetTSOrigin(display, linePtr->arrowGC, 0, 0); 936 } 937} 938 939/* 940 *-------------------------------------------------------------- 941 * 942 * LineInsert -- 943 * 944 * Insert coords into a line item at a given index. 945 * 946 * Results: 947 * None. 948 * 949 * Side effects: 950 * The coords in the given item is modified. 951 * 952 *-------------------------------------------------------------- 953 */ 954 955static void 956LineInsert( 957 Tk_Canvas canvas, /* Canvas containing text item. */ 958 Tk_Item *itemPtr, /* Line item to be modified. */ 959 int beforeThis, /* Index before which new coordinates are to 960 * be inserted. */ 961 Tcl_Obj *obj) /* New coordinates to be inserted. */ 962{ 963 LineItem *linePtr = (LineItem *) itemPtr; 964 int length, objc, i; 965 double *newCoordPtr, *coordPtr; 966 Tk_State state = itemPtr->state; 967 Tcl_Obj **objv; 968 969 if (state == TK_STATE_NULL) { 970 state = ((TkCanvas *)canvas)->canvas_state; 971 } 972 973 if (!obj || (Tcl_ListObjGetElements(NULL, obj, &objc, &objv) != TCL_OK) 974 || !objc || objc&1) { 975 return; 976 } 977 length = 2*linePtr->numPoints; 978 if (beforeThis < 0) { 979 beforeThis = 0; 980 } 981 if (beforeThis > length) { 982 beforeThis = length; 983 } 984 if (linePtr->firstArrowPtr != NULL) { 985 linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; 986 linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; 987 } 988 if (linePtr->lastArrowPtr != NULL) { 989 linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; 990 linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; 991 } 992 newCoordPtr = (double *) 993 ckalloc(sizeof(double) * (unsigned)(length + objc)); 994 for (i=0; i<beforeThis; i++) { 995 newCoordPtr[i] = linePtr->coordPtr[i]; 996 } 997 for (i=0; i<objc; i++) { 998 if (Tcl_GetDoubleFromObj(NULL, objv[i], 999 &newCoordPtr[i + beforeThis]) != TCL_OK) { 1000 Tcl_ResetResult(((TkCanvas *)canvas)->interp); 1001 ckfree((char *) newCoordPtr); 1002 return; 1003 } 1004 } 1005 1006 for (i=beforeThis; i<length; i++) { 1007 newCoordPtr[i+objc] = linePtr->coordPtr[i]; 1008 } 1009 if (linePtr->coordPtr) { 1010 ckfree((char *)linePtr->coordPtr); 1011 } 1012 linePtr->coordPtr = newCoordPtr; 1013 linePtr->numPoints = (length + objc)/2; 1014 1015 if ((length>3) && (state != TK_STATE_HIDDEN)) { 1016 /* 1017 * This is some optimizing code that will result that only the part of 1018 * the polygon that changed (and the objects that are overlapping with 1019 * that part) need to be redrawn. A special flag is set that instructs 1020 * the general canvas code not to redraw the whole object. If this 1021 * flag is not set, the canvas will do the redrawing, otherwise I have 1022 * to do it here. 1023 */ 1024 1025 itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; 1026 1027 if (beforeThis>0) {beforeThis -= 2; objc+=2; } 1028 if ((beforeThis+objc)<length) objc+=2; 1029 if (linePtr->smooth) { 1030 if(beforeThis>0) { 1031 beforeThis-=2; objc+=2; 1032 } 1033 if((beforeThis+objc+2)<length) { 1034 objc+=2; 1035 } 1036 } 1037 itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis]; 1038 itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1]; 1039 if ((linePtr->firstArrowPtr != NULL) && (beforeThis<1)) { 1040 /* 1041 * Include old first arrow. 1042 */ 1043 1044 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 1045 i++, coordPtr += 2) { 1046 TkIncludePoint(itemPtr, coordPtr); 1047 } 1048 } 1049 if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+objc)>=length)) { 1050 /* 1051 * Include old last arrow. 1052 */ 1053 1054 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 1055 i++, coordPtr += 2) { 1056 TkIncludePoint(itemPtr, coordPtr); 1057 } 1058 } 1059 coordPtr = linePtr->coordPtr+beforeThis+2; 1060 for (i=2; i<objc; i+=2) { 1061 TkIncludePoint(itemPtr, coordPtr); 1062 coordPtr+=2; 1063 } 1064 } 1065 if (linePtr->firstArrowPtr != NULL) { 1066 ckfree((char *) linePtr->firstArrowPtr); 1067 linePtr->firstArrowPtr = NULL; 1068 } 1069 if (linePtr->lastArrowPtr != NULL) { 1070 ckfree((char *) linePtr->lastArrowPtr); 1071 linePtr->lastArrowPtr = NULL; 1072 } 1073 if (linePtr->arrow != ARROWS_NONE) { 1074 ConfigureArrows(canvas, linePtr); 1075 } 1076 1077 if (itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { 1078 double width; 1079 int intWidth; 1080 1081 if ((linePtr->firstArrowPtr != NULL) && (beforeThis>2)) { 1082 /* 1083 * Include new first arrow. 1084 */ 1085 1086 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 1087 i++, coordPtr += 2) { 1088 TkIncludePoint(itemPtr, coordPtr); 1089 } 1090 } 1091 if ((linePtr->lastArrowPtr != NULL) && (beforeThis+objc < length-2)) { 1092 /* 1093 * Include new right arrow. 1094 */ 1095 1096 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 1097 i++, coordPtr += 2) { 1098 TkIncludePoint(itemPtr, coordPtr); 1099 } 1100 } 1101 width = linePtr->outline.width; 1102 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 1103 if (linePtr->outline.activeWidth>width) { 1104 width = linePtr->outline.activeWidth; 1105 } 1106 } else if (state==TK_STATE_DISABLED) { 1107 if (linePtr->outline.disabledWidth>0) { 1108 width = linePtr->outline.disabledWidth; 1109 } 1110 } 1111 intWidth = (int) (width + 0.5); 1112 if (intWidth < 1) { 1113 intWidth = 1; 1114 } 1115 itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth; 1116 itemPtr->x2 += intWidth; itemPtr->y2 += intWidth; 1117 Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, 1118 itemPtr->x2, itemPtr->y2); 1119 } 1120 1121 ComputeLineBbox(canvas, linePtr); 1122} 1123 1124/* 1125 *-------------------------------------------------------------- 1126 * 1127 * LineDeleteCoords -- 1128 * 1129 * Delete one or more coordinates from a line item. 1130 * 1131 * Results: 1132 * None. 1133 * 1134 * Side effects: 1135 * Characters between "first" and "last", inclusive, get deleted from 1136 * itemPtr. 1137 * 1138 *-------------------------------------------------------------- 1139 */ 1140 1141static void 1142LineDeleteCoords( 1143 Tk_Canvas canvas, /* Canvas containing itemPtr. */ 1144 Tk_Item *itemPtr, /* Item in which to delete characters. */ 1145 int first, /* Index of first character to delete. */ 1146 int last) /* Index of last character to delete. */ 1147{ 1148 LineItem *linePtr = (LineItem *) itemPtr; 1149 int count, i, first1, last1; 1150 int length = 2*linePtr->numPoints; 1151 double *coordPtr; 1152 Tk_State state = itemPtr->state; 1153 1154 if (state == TK_STATE_NULL) { 1155 state = ((TkCanvas *)canvas)->canvas_state; 1156 } 1157 1158 first &= -2; 1159 last &= -2; 1160 1161 if (first < 0) { 1162 first = 0; 1163 } 1164 if (last >= length) { 1165 last = length-2; 1166 } 1167 if (first > last) { 1168 return; 1169 } 1170 if (linePtr->firstArrowPtr != NULL) { 1171 linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; 1172 linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; 1173 } 1174 if (linePtr->lastArrowPtr != NULL) { 1175 linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; 1176 linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; 1177 } 1178 first1 = first; 1179 last1 = last; 1180 if (first1 > 0) { 1181 first1 -= 2; 1182 } 1183 if (last1 < length-2) { 1184 last1 += 2; 1185 } 1186 if (linePtr->smooth) { 1187 if (first1 > 0) { 1188 first1 -= 2; 1189 } 1190 if (last1 < length-2) { 1191 last1 += 2; 1192 } 1193 } 1194 1195 if (!(first1 < 2) && (last1 >= length-2)) { 1196 /* 1197 * This is some optimizing code that will result that only the part of 1198 * the line that changed (and the objects that are overlapping with 1199 * that part) need to be redrawn. A special flag is set that instructs 1200 * the general canvas code not to redraw the whole object. If this 1201 * flag is set, the redrawing has to be done here, otherwise the 1202 * general Canvas code will take care of it. 1203 */ 1204 1205 itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; 1206 itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1]; 1207 itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1]; 1208 if ((linePtr->firstArrowPtr != NULL) && (first1 < 2)) { 1209 /* 1210 * Include old first arrow. 1211 */ 1212 1213 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 1214 i++, coordPtr += 2) { 1215 TkIncludePoint(itemPtr, coordPtr); 1216 } 1217 } 1218 if ((linePtr->lastArrowPtr != NULL) && (last1 >= length-2)) { 1219 /* 1220 * Include old last arrow. 1221 */ 1222 1223 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 1224 i++, coordPtr += 2) { 1225 TkIncludePoint(itemPtr, coordPtr); 1226 } 1227 } 1228 coordPtr = linePtr->coordPtr+first1+2; 1229 for (i=first1+2; i<=last1; i+=2) { 1230 TkIncludePoint(itemPtr, coordPtr); 1231 coordPtr += 2; 1232 } 1233 } 1234 1235 count = last + 2 - first; 1236 for (i=last+2; i<length; i++) { 1237 linePtr->coordPtr[i-count] = linePtr->coordPtr[i]; 1238 } 1239 linePtr->numPoints -= count/2; 1240 if (linePtr->firstArrowPtr != NULL) { 1241 ckfree((char *) linePtr->firstArrowPtr); 1242 linePtr->firstArrowPtr = NULL; 1243 } 1244 if (linePtr->lastArrowPtr != NULL) { 1245 ckfree((char *) linePtr->lastArrowPtr); 1246 linePtr->lastArrowPtr = NULL; 1247 } 1248 if (linePtr->arrow != ARROWS_NONE) { 1249 ConfigureArrows(canvas, linePtr); 1250 } 1251 if (itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { 1252 double width; 1253 int intWidth; 1254 1255 if ((linePtr->firstArrowPtr != NULL) && (first1 < 4)) { 1256 /* 1257 * Include new first arrow. 1258 */ 1259 1260 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 1261 i++, coordPtr += 2) { 1262 TkIncludePoint(itemPtr, coordPtr); 1263 } 1264 } 1265 if ((linePtr->lastArrowPtr != NULL) && (last1 > length-4)) { 1266 /* 1267 * Include new right arrow. 1268 */ 1269 1270 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 1271 i++, coordPtr += 2) { 1272 TkIncludePoint(itemPtr, coordPtr); 1273 } 1274 } 1275 width = linePtr->outline.width; 1276 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 1277 if (linePtr->outline.activeWidth > width) { 1278 width = linePtr->outline.activeWidth; 1279 } 1280 } else if (state==TK_STATE_DISABLED) { 1281 if (linePtr->outline.disabledWidth > 0) { 1282 width = linePtr->outline.disabledWidth; 1283 } 1284 } 1285 intWidth = (int) (width + 0.5); 1286 if (intWidth < 1) { 1287 intWidth = 1; 1288 } 1289 itemPtr->x1 -= intWidth; 1290 itemPtr->y1 -= intWidth; 1291 itemPtr->x2 += intWidth; 1292 itemPtr->y2 += intWidth; 1293 Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, 1294 itemPtr->x2, itemPtr->y2); 1295 } 1296 ComputeLineBbox(canvas, linePtr); 1297} 1298 1299/* 1300 *-------------------------------------------------------------- 1301 * 1302 * LineToPoint -- 1303 * 1304 * Computes the distance from a given point to a given line, in canvas 1305 * units. 1306 * 1307 * Results: 1308 * The return value is 0 if the point whose x and y coordinates are 1309 * pointPtr[0] and pointPtr[1] is inside the line. If the point isn't 1310 * inside the line then the return value is the distance from the point 1311 * to the line. 1312 * 1313 * Side effects: 1314 * None. 1315 * 1316 *-------------------------------------------------------------- 1317 */ 1318 1319 /* ARGSUSED */ 1320static double 1321LineToPoint( 1322 Tk_Canvas canvas, /* Canvas containing item. */ 1323 Tk_Item *itemPtr, /* Item to check against point. */ 1324 double *pointPtr) /* Pointer to x and y coordinates. */ 1325{ 1326 Tk_State state = itemPtr->state; 1327 LineItem *linePtr = (LineItem *) itemPtr; 1328 double *coordPtr, *linePoints; 1329 double staticSpace[2*MAX_STATIC_POINTS]; 1330 double poly[10]; 1331 double bestDist, dist, width; 1332 int numPoints, count; 1333 int changedMiterToBevel; /* Non-zero means that a mitered corner had to 1334 * be treated as beveled after all because the 1335 * angle was < 11 degrees. */ 1336 1337 bestDist = 1.0e36; 1338 1339 /* 1340 * Handle smoothed lines by generating an expanded set of points against 1341 * which to do the check. 1342 */ 1343 1344 if(state == TK_STATE_NULL) { 1345 state = ((TkCanvas *)canvas)->canvas_state; 1346 } 1347 1348 width = linePtr->outline.width; 1349 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 1350 if (linePtr->outline.activeWidth>width) { 1351 width = linePtr->outline.activeWidth; 1352 } 1353 } else if (state==TK_STATE_DISABLED) { 1354 if (linePtr->outline.disabledWidth>0) { 1355 width = linePtr->outline.disabledWidth; 1356 } 1357 } 1358 1359 if ((linePtr->smooth) && (linePtr->numPoints > 2)) { 1360 numPoints = linePtr->smooth->coordProc(canvas, NULL, 1361 linePtr->numPoints, linePtr->splineSteps, NULL, NULL); 1362 if (numPoints <= MAX_STATIC_POINTS) { 1363 linePoints = staticSpace; 1364 } else { 1365 linePoints = (double *) ckalloc((unsigned) 1366 (2*numPoints*sizeof(double))); 1367 } 1368 numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, 1369 linePtr->numPoints, linePtr->splineSteps, NULL, linePoints); 1370 } else { 1371 numPoints = linePtr->numPoints; 1372 linePoints = linePtr->coordPtr; 1373 } 1374 1375 if (width < 1.0) { 1376 width = 1.0; 1377 } 1378 1379 if (!numPoints || itemPtr->state==TK_STATE_HIDDEN) { 1380 return bestDist; 1381 } else if (numPoints == 1) { 1382 bestDist = hypot(linePoints[0]-pointPtr[0], linePoints[1]-pointPtr[1]) 1383 - width/2.0; 1384 if (bestDist < 0) bestDist = 0; 1385 return bestDist; 1386 } 1387 1388 /* 1389 * The overall idea is to iterate through all of the edges of the line, 1390 * computing a polygon for each edge and testing the point against that 1391 * polygon. In addition, there are additional tests to deal with rounded 1392 * joints and caps. 1393 */ 1394 1395 changedMiterToBevel = 0; 1396 for (count = numPoints, coordPtr = linePoints; count >= 2; 1397 count--, coordPtr += 2) { 1398 /* 1399 * If rounding is done around the first point then compute the 1400 * distance between the point and the point. 1401 */ 1402 1403 if (((linePtr->capStyle == CapRound) && (count == numPoints)) 1404 || ((linePtr->joinStyle == JoinRound) 1405 && (count != numPoints))) { 1406 dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) 1407 - width/2.0; 1408 if (dist <= 0.0) { 1409 bestDist = 0.0; 1410 goto done; 1411 } else if (dist < bestDist) { 1412 bestDist = dist; 1413 } 1414 } 1415 1416 /* 1417 * Compute the polygonal shape corresponding to this edge, consisting 1418 * of two points for the first point of the edge and two points for 1419 * the last point of the edge. 1420 */ 1421 1422 if (count == numPoints) { 1423 TkGetButtPoints(coordPtr+2, coordPtr, width, 1424 linePtr->capStyle == CapProjecting, poly, poly+2); 1425 } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) { 1426 poly[0] = poly[6]; 1427 poly[1] = poly[7]; 1428 poly[2] = poly[4]; 1429 poly[3] = poly[5]; 1430 } else { 1431 TkGetButtPoints(coordPtr+2, coordPtr, width, 0, poly, poly+2); 1432 1433 /* 1434 * If this line uses beveled joints, then check the distance to a 1435 * polygon comprising the last two points of the previous polygon 1436 * and the first two from this polygon; this checks the wedges 1437 * that fill the mitered joint. 1438 */ 1439 1440 if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) { 1441 poly[8] = poly[0]; 1442 poly[9] = poly[1]; 1443 dist = TkPolygonToPoint(poly, 5, pointPtr); 1444 if (dist <= 0.0) { 1445 bestDist = 0.0; 1446 goto done; 1447 } else if (dist < bestDist) { 1448 bestDist = dist; 1449 } 1450 changedMiterToBevel = 0; 1451 } 1452 } 1453 if (count == 2) { 1454 TkGetButtPoints(coordPtr, coordPtr+2, width, 1455 linePtr->capStyle == CapProjecting, poly+4, poly+6); 1456 } else if (linePtr->joinStyle == JoinMiter) { 1457 if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, 1458 width, poly+4, poly+6) == 0) { 1459 changedMiterToBevel = 1; 1460 TkGetButtPoints(coordPtr, coordPtr+2, width, 0, 1461 poly+4, poly+6); 1462 } 1463 } else { 1464 TkGetButtPoints(coordPtr, coordPtr+2, width, 0, 1465 poly+4, poly+6); 1466 } 1467 poly[8] = poly[0]; 1468 poly[9] = poly[1]; 1469 dist = TkPolygonToPoint(poly, 5, pointPtr); 1470 if (dist <= 0.0) { 1471 bestDist = 0.0; 1472 goto done; 1473 } else if (dist < bestDist) { 1474 bestDist = dist; 1475 } 1476 } 1477 1478 /* 1479 * If caps are rounded, check the distance to the cap around the final end 1480 * point of the line. 1481 */ 1482 1483 if (linePtr->capStyle == CapRound) { 1484 dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) 1485 - width/2.0; 1486 if (dist <= 0.0) { 1487 bestDist = 0.0; 1488 goto done; 1489 } else if (dist < bestDist) { 1490 bestDist = dist; 1491 } 1492 } 1493 1494 /* 1495 * If there are arrowheads, check the distance to the arrowheads. 1496 */ 1497 1498 if (linePtr->arrow != ARROWS_NONE) { 1499 if (linePtr->arrow != ARROWS_LAST) { 1500 dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW, 1501 pointPtr); 1502 if (dist <= 0.0) { 1503 bestDist = 0.0; 1504 goto done; 1505 } else if (dist < bestDist) { 1506 bestDist = dist; 1507 } 1508 } 1509 if (linePtr->arrow != ARROWS_FIRST) { 1510 dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW, 1511 pointPtr); 1512 if (dist <= 0.0) { 1513 bestDist = 0.0; 1514 goto done; 1515 } else if (dist < bestDist) { 1516 bestDist = dist; 1517 } 1518 } 1519 } 1520 1521 done: 1522 if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { 1523 ckfree((char *) linePoints); 1524 } 1525 return bestDist; 1526} 1527 1528/* 1529 *-------------------------------------------------------------- 1530 * 1531 * LineToArea -- 1532 * 1533 * This function is called to determine whether an item lies entirely 1534 * inside, entirely outside, or overlapping a given rectangular area. 1535 * 1536 * Results: 1537 * -1 is returned if the item is entirely outside the area, 0 if it 1538 * overlaps, and 1 if it is entirely inside the given area. 1539 * 1540 * Side effects: 1541 * None. 1542 * 1543 *-------------------------------------------------------------- 1544 */ 1545 1546 /* ARGSUSED */ 1547static int 1548LineToArea( 1549 Tk_Canvas canvas, /* Canvas containing item. */ 1550 Tk_Item *itemPtr, /* Item to check against line. */ 1551 double *rectPtr) 1552{ 1553 LineItem *linePtr = (LineItem *) itemPtr; 1554 double staticSpace[2*MAX_STATIC_POINTS]; 1555 double *linePoints; 1556 int numPoints, result; 1557 double radius, width; 1558 Tk_State state = itemPtr->state; 1559 1560 if(state == TK_STATE_NULL) { 1561 state = ((TkCanvas *)canvas)->canvas_state; 1562 } 1563 width = linePtr->outline.width; 1564 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 1565 if (linePtr->outline.activeWidth>width) { 1566 width = linePtr->outline.activeWidth; 1567 } 1568 } else if (state==TK_STATE_DISABLED) { 1569 if (linePtr->outline.disabledWidth>0) { 1570 width = linePtr->outline.disabledWidth; 1571 } 1572 } 1573 1574 radius = (width+1.0)/2.0; 1575 1576 if ((state==TK_STATE_HIDDEN) || !linePtr->numPoints) { 1577 return -1; 1578 } else if (linePtr->numPoints == 1) { 1579 double oval[4]; 1580 1581 oval[0] = linePtr->coordPtr[0]-radius; 1582 oval[1] = linePtr->coordPtr[1]-radius; 1583 oval[2] = linePtr->coordPtr[0]+radius; 1584 oval[3] = linePtr->coordPtr[1]+radius; 1585 return TkOvalToArea(oval, rectPtr); 1586 } 1587 1588 /* 1589 * Handle smoothed lines by generating an expanded set of points against 1590 * which to do the check. 1591 */ 1592 1593 if ((linePtr->smooth) && (linePtr->numPoints > 2)) { 1594 numPoints = linePtr->smooth->coordProc(canvas, NULL, 1595 linePtr->numPoints, linePtr->splineSteps, NULL, NULL); 1596 if (numPoints <= MAX_STATIC_POINTS) { 1597 linePoints = staticSpace; 1598 } else { 1599 linePoints = (double *) ckalloc((unsigned) 1600 (2*numPoints*sizeof(double))); 1601 } 1602 numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, 1603 linePtr->numPoints, linePtr->splineSteps, NULL, linePoints); 1604 } else { 1605 numPoints = linePtr->numPoints; 1606 linePoints = linePtr->coordPtr; 1607 } 1608 1609 /* 1610 * Check the segments of the line. 1611 */ 1612 1613 if (width < 1.0) { 1614 width = 1.0; 1615 } 1616 1617 result = TkThickPolyLineToArea(linePoints, numPoints, 1618 width, linePtr->capStyle, linePtr->joinStyle, 1619 rectPtr); 1620 if (result == 0) { 1621 goto done; 1622 } 1623 1624 /* 1625 * Check arrowheads, if any. 1626 */ 1627 1628 if (linePtr->arrow != ARROWS_NONE) { 1629 if (linePtr->arrow != ARROWS_LAST) { 1630 if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW, 1631 rectPtr) != result) { 1632 result = 0; 1633 goto done; 1634 } 1635 } 1636 if (linePtr->arrow != ARROWS_FIRST) { 1637 if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW, 1638 rectPtr) != result) { 1639 result = 0; 1640 goto done; 1641 } 1642 } 1643 } 1644 1645 done: 1646 if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { 1647 ckfree((char *) linePoints); 1648 } 1649 return result; 1650} 1651 1652/* 1653 *-------------------------------------------------------------- 1654 * 1655 * ScaleLine -- 1656 * 1657 * This function is invoked to rescale a line item. 1658 * 1659 * Results: 1660 * None. 1661 * 1662 * Side effects: 1663 * The line referred to by itemPtr is rescaled so that the following 1664 * transformation is applied to all point coordinates: 1665 * x' = originX + scaleX*(x-originX) 1666 * y' = originY + scaleY*(y-originY) 1667 * 1668 *-------------------------------------------------------------- 1669 */ 1670 1671static void 1672ScaleLine( 1673 Tk_Canvas canvas, /* Canvas containing line. */ 1674 Tk_Item *itemPtr, /* Line to be scaled. */ 1675 double originX, double originY, 1676 /* Origin about which to scale rect. */ 1677 double scaleX, /* Amount to scale in X direction. */ 1678 double scaleY) /* Amount to scale in Y direction. */ 1679{ 1680 LineItem *linePtr = (LineItem *) itemPtr; 1681 double *coordPtr; 1682 int i; 1683 1684 /* 1685 * Delete any arrowheads before scaling all the points (so that the 1686 * end-points of the line get restored). 1687 */ 1688 1689 if (linePtr->firstArrowPtr != NULL) { 1690 linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; 1691 linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; 1692 ckfree((char *) linePtr->firstArrowPtr); 1693 linePtr->firstArrowPtr = NULL; 1694 } 1695 if (linePtr->lastArrowPtr != NULL) { 1696 int i; 1697 1698 i = 2*(linePtr->numPoints-1); 1699 linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; 1700 linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; 1701 ckfree((char *) linePtr->lastArrowPtr); 1702 linePtr->lastArrowPtr = NULL; 1703 } 1704 for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; 1705 i++, coordPtr += 2) { 1706 coordPtr[0] = originX + scaleX*(*coordPtr - originX); 1707 coordPtr[1] = originY + scaleY*(coordPtr[1] - originY); 1708 } 1709 if (linePtr->arrow != ARROWS_NONE) { 1710 ConfigureArrows(canvas, linePtr); 1711 } 1712 ComputeLineBbox(canvas, linePtr); 1713} 1714 1715/* 1716 *-------------------------------------------------------------- 1717 * 1718 * GetLineIndex -- 1719 * 1720 * Parse an index into a line item and return either its value or an 1721 * error. 1722 * 1723 * Results: 1724 * A standard Tcl result. If all went well, then *indexPtr is filled in 1725 * with the index (into itemPtr) corresponding to string. Otherwise an 1726 * error message is left in interp->result. 1727 * 1728 * Side effects: 1729 * None. 1730 * 1731 *-------------------------------------------------------------- 1732 */ 1733 1734static int 1735GetLineIndex( 1736 Tcl_Interp *interp, /* Used for error reporting. */ 1737 Tk_Canvas canvas, /* Canvas containing item. */ 1738 Tk_Item *itemPtr, /* Item for which the index is being 1739 * specified. */ 1740 Tcl_Obj *obj, /* Specification of a particular coord in 1741 * itemPtr's line. */ 1742 int *indexPtr) /* Where to store converted index. */ 1743{ 1744 LineItem *linePtr = (LineItem *) itemPtr; 1745 int length; 1746 char *string = Tcl_GetStringFromObj(obj, &length); 1747 1748 if (string[0] == 'e') { 1749 if (strncmp(string, "end", (unsigned) length) == 0) { 1750 *indexPtr = 2*linePtr->numPoints; 1751 } else { 1752 /* 1753 * Some of the paths here leave messages in interp->result, so we 1754 * have to clear it out before storing our own message. 1755 */ 1756 1757 badIndex: 1758 Tcl_SetResult(interp, NULL, TCL_STATIC); 1759 Tcl_AppendResult(interp, "bad index \"", string, "\"", NULL); 1760 return TCL_ERROR; 1761 } 1762 } else if (string[0] == '@') { 1763 int i; 1764 double x ,y, bestDist, dist, *coordPtr; 1765 char *end, *p; 1766 1767 p = string+1; 1768 x = strtod(p, &end); 1769 if ((end == p) || (*end != ',')) { 1770 goto badIndex; 1771 } 1772 p = end+1; 1773 y = strtod(p, &end); 1774 if ((end == p) || (*end != 0)) { 1775 goto badIndex; 1776 } 1777 bestDist = 1.0e36; 1778 coordPtr = linePtr->coordPtr; 1779 *indexPtr = 0; 1780 for(i=0; i<linePtr->numPoints; i++) { 1781 dist = hypot(coordPtr[0] - x, coordPtr[1] - y); 1782 if (dist<bestDist) { 1783 bestDist = dist; 1784 *indexPtr = 2*i; 1785 } 1786 coordPtr += 2; 1787 } 1788 } else { 1789 if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) { 1790 goto badIndex; 1791 } 1792 *indexPtr &= -2; /* if index is odd, make it even */ 1793 if (*indexPtr < 0){ 1794 *indexPtr = 0; 1795 } else if (*indexPtr > (2*linePtr->numPoints)) { 1796 *indexPtr = (2*linePtr->numPoints); 1797 } 1798 } 1799 return TCL_OK; 1800} 1801 1802/* 1803 *-------------------------------------------------------------- 1804 * 1805 * TranslateLine -- 1806 * 1807 * This function is called to move a line by a given amount. 1808 * 1809 * Results: 1810 * None. 1811 * 1812 * Side effects: 1813 * The position of the line is offset by (xDelta, yDelta), and the 1814 * bounding box is updated in the generic part of the item structure. 1815 * 1816 *-------------------------------------------------------------- 1817 */ 1818 1819static void 1820TranslateLine( 1821 Tk_Canvas canvas, /* Canvas containing item. */ 1822 Tk_Item *itemPtr, /* Item that is being moved. */ 1823 double deltaX, double deltaY) 1824 /* Amount by which item is to be moved. */ 1825{ 1826 LineItem *linePtr = (LineItem *) itemPtr; 1827 double *coordPtr; 1828 int i; 1829 1830 for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; 1831 i++, coordPtr += 2) { 1832 coordPtr[0] += deltaX; 1833 coordPtr[1] += deltaY; 1834 } 1835 if (linePtr->firstArrowPtr != NULL) { 1836 for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; 1837 i++, coordPtr += 2) { 1838 coordPtr[0] += deltaX; 1839 coordPtr[1] += deltaY; 1840 } 1841 } 1842 if (linePtr->lastArrowPtr != NULL) { 1843 for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; 1844 i++, coordPtr += 2) { 1845 coordPtr[0] += deltaX; 1846 coordPtr[1] += deltaY; 1847 } 1848 } 1849 ComputeLineBbox(canvas, linePtr); 1850} 1851 1852/* 1853 *-------------------------------------------------------------- 1854 * 1855 * ParseArrowShape -- 1856 * 1857 * This function is called back during option parsing to parse arrow 1858 * shape information. 1859 * 1860 * Results: 1861 * The return value is a standard Tcl result: TCL_OK means that the arrow 1862 * shape information was parsed ok, and TCL_ERROR means it couldn't be 1863 * parsed. 1864 * 1865 * Side effects: 1866 * Arrow information in recordPtr is updated. 1867 * 1868 *-------------------------------------------------------------- 1869 */ 1870 1871 /* ARGSUSED */ 1872static int 1873ParseArrowShape( 1874 ClientData clientData, /* Not used. */ 1875 Tcl_Interp *interp, /* Used for error reporting. */ 1876 Tk_Window tkwin, /* Not used. */ 1877 CONST char *value, /* Textual specification of arrow shape. */ 1878 char *recordPtr, /* Pointer to item record in which to store 1879 * arrow information. */ 1880 int offset) /* Offset of shape information in widget 1881 * record. */ 1882{ 1883 LineItem *linePtr = (LineItem *) recordPtr; 1884 double a, b, c; 1885 int argc; 1886 CONST char **argv = NULL; 1887 1888 if (offset != Tk_Offset(LineItem, arrowShapeA)) { 1889 Tcl_Panic("ParseArrowShape received bogus offset"); 1890 } 1891 1892 if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) { 1893 syntaxError: 1894 Tcl_ResetResult(interp); 1895 Tcl_AppendResult(interp, "bad arrow shape \"", value, 1896 "\": must be list with three numbers", NULL); 1897 if (argv != NULL) { 1898 ckfree((char *) argv); 1899 } 1900 return TCL_ERROR; 1901 } 1902 if (argc != 3) { 1903 goto syntaxError; 1904 } 1905 if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK) 1906 || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b) 1907 != TCL_OK) 1908 || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c) 1909 != TCL_OK)) { 1910 goto syntaxError; 1911 } 1912 linePtr->arrowShapeA = (float)a; 1913 linePtr->arrowShapeB = (float)b; 1914 linePtr->arrowShapeC = (float)c; 1915 ckfree((char *) argv); 1916 return TCL_OK; 1917} 1918 1919/* 1920 *-------------------------------------------------------------- 1921 * 1922 * PrintArrowShape -- 1923 * 1924 * This function is a callback invoked by the configuration code to 1925 * return a printable value describing an arrow shape. 1926 * 1927 * Results: 1928 * None. 1929 * 1930 * Side effects: 1931 * None. 1932 * 1933 *-------------------------------------------------------------- 1934 */ 1935 1936 /* ARGSUSED */ 1937static char * 1938PrintArrowShape( 1939 ClientData clientData, /* Not used. */ 1940 Tk_Window tkwin, /* Window associated with linePtr's widget. */ 1941 char *recordPtr, /* Pointer to item record containing current 1942 * shape information. */ 1943 int offset, /* Offset of arrow information in record. */ 1944 Tcl_FreeProc **freeProcPtr) /* Store address of function to call to free 1945 * string here. */ 1946{ 1947 LineItem *linePtr = (LineItem *) recordPtr; 1948 char *buffer; 1949 1950 buffer = (char *) ckalloc(120); 1951 sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA, 1952 linePtr->arrowShapeB, linePtr->arrowShapeC); 1953 *freeProcPtr = TCL_DYNAMIC; 1954 return buffer; 1955} 1956 1957 1958/* 1959 *-------------------------------------------------------------- 1960 * 1961 * ArrowParseProc -- 1962 * 1963 * This function is invoked during option processing to handle the 1964 * "-arrow" option. 1965 * 1966 * Results: 1967 * A standard Tcl return value. 1968 * 1969 * Side effects: 1970 * The arrow for a given item gets replaced by the arrow indicated in the 1971 * value argument. 1972 * 1973 *-------------------------------------------------------------- 1974 */ 1975 1976static int 1977ArrowParseProc( 1978 ClientData clientData, /* some flags.*/ 1979 Tcl_Interp *interp, /* Used for reporting errors. */ 1980 Tk_Window tkwin, /* Window containing canvas widget. */ 1981 CONST char *value, /* Value of option. */ 1982 char *widgRec, /* Pointer to record for item. */ 1983 int offset) /* Offset into item. */ 1984{ 1985 int c; 1986 size_t length; 1987 1988 register Arrows *arrowPtr = (Arrows *) (widgRec + offset); 1989 1990 if(value == NULL || *value == 0) { 1991 *arrowPtr = ARROWS_NONE; 1992 return TCL_OK; 1993 } 1994 1995 c = value[0]; 1996 length = strlen(value); 1997 1998 if ((c == 'n') && (strncmp(value, "none", length) == 0)) { 1999 *arrowPtr = ARROWS_NONE; 2000 return TCL_OK; 2001 } 2002 if ((c == 'f') && (strncmp(value, "first", length) == 0)) { 2003 *arrowPtr = ARROWS_FIRST; 2004 return TCL_OK; 2005 } 2006 if ((c == 'l') && (strncmp(value, "last", length) == 0)) { 2007 *arrowPtr = ARROWS_LAST; 2008 return TCL_OK; 2009 } 2010 if ((c == 'b') && (strncmp(value, "both", length) == 0)) { 2011 *arrowPtr = ARROWS_BOTH; 2012 return TCL_OK; 2013 } 2014 2015 Tcl_AppendResult(interp, "bad arrow spec \"", value, 2016 "\": must be none, first, last, or both", NULL); 2017 *arrowPtr = ARROWS_NONE; 2018 return TCL_ERROR; 2019} 2020 2021/* 2022 *-------------------------------------------------------------- 2023 * 2024 * ArrowPrintProc -- 2025 * 2026 * This function is invoked by the Tk configuration code to produce a 2027 * printable string for the "-arrow" configuration option. 2028 * 2029 * Results: 2030 * The return value is a string describing the arrows for the item 2031 * referred to by "widgRec". In addition, *freeProcPtr is filled in with 2032 * the address of a function to call to free the result string when it's 2033 * no longer needed (or NULL to indicate that the string doesn't need to 2034 * be freed). 2035 * 2036 * Side effects: 2037 * None. 2038 * 2039 *-------------------------------------------------------------- 2040 */ 2041 2042static char * 2043ArrowPrintProc( 2044 ClientData clientData, /* Ignored. */ 2045 Tk_Window tkwin, /* Window containing canvas widget. */ 2046 char *widgRec, /* Pointer to record for item. */ 2047 int offset, /* Offset into item. */ 2048 Tcl_FreeProc **freeProcPtr) /* Pointer to variable to fill in with 2049 * information about how to reclaim storage 2050 * for return string. */ 2051{ 2052 register Arrows *arrowPtr = (Arrows *) (widgRec + offset); 2053 2054 switch (*arrowPtr) { 2055 case ARROWS_FIRST: 2056 return "first"; 2057 case ARROWS_LAST: 2058 return "last"; 2059 case ARROWS_BOTH: 2060 return "both"; 2061 default: 2062 return "none"; 2063 } 2064} 2065 2066/* 2067 *-------------------------------------------------------------- 2068 * 2069 * ConfigureArrows -- 2070 * 2071 * If arrowheads have been requested for a line, this function makes 2072 * arrangements for the arrowheads. 2073 * 2074 * Results: 2075 * Always returns TCL_OK. 2076 * 2077 * Side effects: 2078 * Information in linePtr is set up for one or two arrowheads. The 2079 * firstArrowPtr and lastArrowPtr polygons are allocated and initialized, 2080 * if need be, and the end points of the line are adjusted so that a 2081 * thick line doesn't stick out past the arrowheads. 2082 * 2083 *-------------------------------------------------------------- 2084 */ 2085 2086 /* ARGSUSED */ 2087static int 2088ConfigureArrows( 2089 Tk_Canvas canvas, /* Canvas in which arrows will be displayed 2090 * (interp and tkwin fields are needed). */ 2091 LineItem *linePtr) /* Item to configure for arrows. */ 2092{ 2093 double *poly, *coordPtr; 2094 double dx, dy, length, sinTheta, cosTheta, temp; 2095 double fracHeight; /* Line width as fraction of arrowhead 2096 * width. */ 2097 double backup; /* Distance to backup end points so the line 2098 * ends in the middle of the arrowhead. */ 2099 double vertX, vertY; /* Position of arrowhead vertex. */ 2100 double shapeA, shapeB, shapeC; 2101 /* Adjusted coordinates (see explanation 2102 * below). */ 2103 double width; 2104 Tk_State state = linePtr->header.state; 2105 2106 if (linePtr->numPoints <2) { 2107 return TCL_OK; 2108 } 2109 2110 if(state == TK_STATE_NULL) { 2111 state = ((TkCanvas *)canvas)->canvas_state; 2112 } 2113 2114 width = linePtr->outline.width; 2115 if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { 2116 if (linePtr->outline.activeWidth>width) { 2117 width = linePtr->outline.activeWidth; 2118 } 2119 } else if (state==TK_STATE_DISABLED) { 2120 if (linePtr->outline.disabledWidth>0) { 2121 width = linePtr->outline.disabledWidth; 2122 } 2123 } 2124 2125 /* 2126 * The code below makes a tiny increase in the shape parameters for the 2127 * line. This is a bit of a hack, but it seems to result in displays that 2128 * more closely approximate the specified parameters. Without the 2129 * adjustment, the arrows come out smaller than expected. 2130 */ 2131 2132 shapeA = linePtr->arrowShapeA + 0.001; 2133 shapeB = linePtr->arrowShapeB + 0.001; 2134 shapeC = linePtr->arrowShapeC + width/2.0 + 0.001; 2135 2136 /* 2137 * If there's an arrowhead on the first point of the line, compute its 2138 * polygon and adjust the first point of the line so that the line doesn't 2139 * stick out past the leading edge of the arrowhead. 2140 */ 2141 2142 fracHeight = (width/2.0)/shapeC; 2143 backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0; 2144 if (linePtr->arrow != ARROWS_LAST) { 2145 poly = linePtr->firstArrowPtr; 2146 if (poly == NULL) { 2147 poly = (double *) ckalloc((unsigned) 2148 (2*PTS_IN_ARROW*sizeof(double))); 2149 poly[0] = poly[10] = linePtr->coordPtr[0]; 2150 poly[1] = poly[11] = linePtr->coordPtr[1]; 2151 linePtr->firstArrowPtr = poly; 2152 } 2153 dx = poly[0] - linePtr->coordPtr[2]; 2154 dy = poly[1] - linePtr->coordPtr[3]; 2155 length = hypot(dx, dy); 2156 if (length == 0) { 2157 sinTheta = cosTheta = 0.0; 2158 } else { 2159 sinTheta = dy/length; 2160 cosTheta = dx/length; 2161 } 2162 vertX = poly[0] - shapeA*cosTheta; 2163 vertY = poly[1] - shapeA*sinTheta; 2164 temp = shapeC*sinTheta; 2165 poly[2] = poly[0] - shapeB*cosTheta + temp; 2166 poly[8] = poly[2] - 2*temp; 2167 temp = shapeC*cosTheta; 2168 poly[3] = poly[1] - shapeB*sinTheta - temp; 2169 poly[9] = poly[3] + 2*temp; 2170 poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight); 2171 poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight); 2172 poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight); 2173 poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight); 2174 2175 /* 2176 * Polygon done. Now move the first point towards the second so that 2177 * the corners at the end of the line are inside the arrowhead. 2178 */ 2179 2180 linePtr->coordPtr[0] = poly[0] - backup*cosTheta; 2181 linePtr->coordPtr[1] = poly[1] - backup*sinTheta; 2182 } 2183 2184 /* 2185 * Similar arrowhead calculation for the last point of the line. 2186 */ 2187 2188 if (linePtr->arrow != ARROWS_FIRST) { 2189 coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2); 2190 poly = linePtr->lastArrowPtr; 2191 if (poly == NULL) { 2192 poly = (double *) 2193 ckalloc((unsigned) (2*PTS_IN_ARROW*sizeof(double))); 2194 poly[0] = poly[10] = coordPtr[2]; 2195 poly[1] = poly[11] = coordPtr[3]; 2196 linePtr->lastArrowPtr = poly; 2197 } 2198 dx = poly[0] - coordPtr[0]; 2199 dy = poly[1] - coordPtr[1]; 2200 length = hypot(dx, dy); 2201 if (length == 0) { 2202 sinTheta = cosTheta = 0.0; 2203 } else { 2204 sinTheta = dy/length; 2205 cosTheta = dx/length; 2206 } 2207 vertX = poly[0] - shapeA*cosTheta; 2208 vertY = poly[1] - shapeA*sinTheta; 2209 temp = shapeC * sinTheta; 2210 poly[2] = poly[0] - shapeB*cosTheta + temp; 2211 poly[8] = poly[2] - 2*temp; 2212 temp = shapeC * cosTheta; 2213 poly[3] = poly[1] - shapeB*sinTheta - temp; 2214 poly[9] = poly[3] + 2*temp; 2215 poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight); 2216 poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight); 2217 poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight); 2218 poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight); 2219 coordPtr[2] = poly[0] - backup*cosTheta; 2220 coordPtr[3] = poly[1] - backup*sinTheta; 2221 } 2222 2223 return TCL_OK; 2224} 2225 2226/* 2227 *-------------------------------------------------------------- 2228 * 2229 * LineToPostscript -- 2230 * 2231 * This function is called to generate Postscript for line items. 2232 * 2233 * Results: 2234 * The return value is a standard Tcl result. If an error occurs in 2235 * generating Postscript then an error message is left in the interp's 2236 * result, replacing whatever used to be there. If no error occurs, then 2237 * Postscript for the item is appended to the result. 2238 * 2239 * Side effects: 2240 * None. 2241 * 2242 *-------------------------------------------------------------- 2243 */ 2244 2245static int 2246LineToPostscript( 2247 Tcl_Interp *interp, /* Leave Postscript or error message here. */ 2248 Tk_Canvas canvas, /* Information about overall canvas. */ 2249 Tk_Item *itemPtr, /* Item for which Postscript is wanted. */ 2250 int prepass) /* 1 means this is a prepass to collect font 2251 * information; 0 means final Postscript is 2252 * being created. */ 2253{ 2254 LineItem *linePtr = (LineItem *) itemPtr; 2255 char buffer[64 + TCL_INTEGER_SPACE]; 2256 char *style; 2257 2258 double width; 2259 XColor *color; 2260 Pixmap stipple; 2261 Tk_State state = itemPtr->state; 2262 2263 if(state == TK_STATE_NULL) { 2264 state = ((TkCanvas *)canvas)->canvas_state; 2265 } 2266 2267 width = linePtr->outline.width; 2268 color = linePtr->outline.color; 2269 stipple = linePtr->outline.stipple; 2270 if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { 2271 if (linePtr->outline.activeWidth>width) { 2272 width = linePtr->outline.activeWidth; 2273 } 2274 if (linePtr->outline.activeColor!=NULL) { 2275 color = linePtr->outline.activeColor; 2276 } 2277 if (linePtr->outline.activeStipple!=None) { 2278 stipple = linePtr->outline.activeStipple; 2279 } 2280 } else if (state==TK_STATE_DISABLED) { 2281 if (linePtr->outline.disabledWidth>0) { 2282 width = linePtr->outline.disabledWidth; 2283 } 2284 if (linePtr->outline.disabledColor!=NULL) { 2285 color = linePtr->outline.disabledColor; 2286 } 2287 if (linePtr->outline.disabledStipple!=None) { 2288 stipple = linePtr->outline.disabledStipple; 2289 } 2290 } 2291 2292 if (color == NULL || linePtr->numPoints<1 || linePtr->coordPtr==NULL) { 2293 return TCL_OK; 2294 } 2295 2296 if (linePtr->numPoints==1) { 2297 sprintf(buffer, "%.15g %.15g translate %.15g %.15g", 2298 linePtr->coordPtr[0], Tk_CanvasPsY(canvas, linePtr->coordPtr[1]), 2299 width/2.0, width/2.0); 2300 Tcl_AppendResult(interp, "matrix currentmatrix\n",buffer, 2301 " scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", NULL); 2302 if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) { 2303 return TCL_ERROR; 2304 } 2305 if (stipple != None) { 2306 Tcl_AppendResult(interp, "clip ", NULL); 2307 if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) { 2308 return TCL_ERROR; 2309 } 2310 } else { 2311 Tcl_AppendResult(interp, "fill\n", NULL); 2312 } 2313 return TCL_OK; 2314 } 2315 /* 2316 * Generate a path for the line's center-line (do this differently for 2317 * straight lines and smoothed lines). 2318 */ 2319 2320 if ((!linePtr->smooth) || (linePtr->numPoints < 3)) { 2321 Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints); 2322 } else { 2323 if ((stipple == None) && linePtr->smooth->postscriptProc) { 2324 linePtr->smooth->postscriptProc(interp, canvas, 2325 linePtr->coordPtr, linePtr->numPoints, linePtr->splineSteps); 2326 } else { 2327 /* 2328 * Special hack: Postscript printers don't appear to be able to 2329 * turn a path drawn with "curveto"s into a clipping path without 2330 * exceeding resource limits, so TkMakeBezierPostscript won't work 2331 * for stippled curves. Instead, generate all of the intermediate 2332 * points here and output them into the Postscript file with 2333 * "lineto"s instead. 2334 */ 2335 2336 double staticPoints[2*MAX_STATIC_POINTS]; 2337 double *pointPtr; 2338 int numPoints; 2339 2340 numPoints = linePtr->smooth->coordProc(canvas, NULL, 2341 linePtr->numPoints, linePtr->splineSteps, NULL, NULL); 2342 pointPtr = staticPoints; 2343 if (numPoints > MAX_STATIC_POINTS) { 2344 pointPtr = (double *) ckalloc((unsigned) 2345 (numPoints * 2 * sizeof(double))); 2346 } 2347 numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, 2348 linePtr->numPoints, linePtr->splineSteps, NULL, pointPtr); 2349 Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints); 2350 if (pointPtr != staticPoints) { 2351 ckfree((char *) pointPtr); 2352 } 2353 } 2354 } 2355 2356 /* 2357 * Set other line-drawing parameters and stroke out the line. 2358 */ 2359 2360 style = "0 setlinecap\n"; 2361 if (linePtr->capStyle == CapRound) { 2362 style = "1 setlinecap\n"; 2363 } else if (linePtr->capStyle == CapProjecting) { 2364 style = "2 setlinecap\n"; 2365 } 2366 Tcl_AppendResult(interp, style, NULL); 2367 style = "0 setlinejoin\n"; 2368 if (linePtr->joinStyle == JoinRound) { 2369 style = "1 setlinejoin\n"; 2370 } else if (linePtr->joinStyle == JoinBevel) { 2371 style = "2 setlinejoin\n"; 2372 } 2373 Tcl_AppendResult(interp, style, NULL); 2374 2375 if (Tk_CanvasPsOutline(canvas, itemPtr, &(linePtr->outline)) != TCL_OK) { 2376 return TCL_ERROR; 2377 } 2378 2379 /* 2380 * Output polygons for the arrowheads, if there are any. 2381 */ 2382 2383 if (linePtr->firstArrowPtr != NULL) { 2384 if (stipple != None) { 2385 Tcl_AppendResult(interp, "grestore gsave\n", NULL); 2386 } 2387 if (ArrowheadPostscript(interp, canvas, linePtr, 2388 linePtr->firstArrowPtr) != TCL_OK) { 2389 return TCL_ERROR; 2390 } 2391 } 2392 if (linePtr->lastArrowPtr != NULL) { 2393 if (stipple != None) { 2394 Tcl_AppendResult(interp, "grestore gsave\n", NULL); 2395 } 2396 if (ArrowheadPostscript(interp, canvas, linePtr, 2397 linePtr->lastArrowPtr) != TCL_OK) { 2398 return TCL_ERROR; 2399 } 2400 } 2401 return TCL_OK; 2402} 2403 2404/* 2405 *-------------------------------------------------------------- 2406 * 2407 * ArrowheadPostscript -- 2408 * 2409 * This function is called to generate Postscript for an arrowhead for a 2410 * line item. 2411 * 2412 * Results: 2413 * The return value is a standard Tcl result. If an error occurs in 2414 * generating Postscript then an error message is left in the interp's 2415 * result, replacing whatever used to be there. If no error occurs, then 2416 * Postscript for the arrowhead is appended to the result. 2417 * 2418 * Side effects: 2419 * None. 2420 * 2421 *-------------------------------------------------------------- 2422 */ 2423 2424static int 2425ArrowheadPostscript( 2426 Tcl_Interp *interp, /* Leave Postscript or error message here. */ 2427 Tk_Canvas canvas, /* Information about overall canvas. */ 2428 LineItem *linePtr, /* Line item for which Postscript is being 2429 * generated. */ 2430 double *arrowPtr) /* Pointer to first of five points describing 2431 * arrowhead polygon. */ 2432{ 2433 Pixmap stipple; 2434 Tk_State state = linePtr->header.state; 2435 2436 if(state == TK_STATE_NULL) { 2437 state = ((TkCanvas *)canvas)->canvas_state; 2438 } 2439 2440 stipple = linePtr->outline.stipple; 2441 if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { 2442 if (linePtr->outline.activeStipple!=None) { 2443 stipple = linePtr->outline.activeStipple; 2444 } 2445 } else if (state==TK_STATE_DISABLED) { 2446 if (linePtr->outline.activeStipple!=None) { 2447 stipple = linePtr->outline.disabledStipple; 2448 } 2449 } 2450 2451 Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW); 2452 if (stipple != None) { 2453 Tcl_AppendResult(interp, "clip ", NULL); 2454 if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) { 2455 return TCL_ERROR; 2456 } 2457 } else { 2458 Tcl_AppendResult(interp, "fill\n", NULL); 2459 } 2460 return TCL_OK; 2461} 2462 2463/* 2464 * Local Variables: 2465 * mode: c 2466 * c-basic-offset: 4 2467 * fill-column: 78 2468 * End: 2469 */ 2470