]> git.zerfleddert.de Git - micropolis/blame_incremental - src/tk/tkcvline.c
XINCLUDE: use /usr/X11R6/include everywhere
[micropolis] / src / tk / tkcvline.c
... / ...
CommitLineData
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
17static 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
30typedef 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
77static void ComputeLineBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr,
78 LineItem *linePtr));
79static int ConfigureLine _ANSI_ARGS_((
80 Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc,
81 char **argv, int flags));
82static int ConfigureArrows _ANSI_ARGS_((Tk_Canvas *canvasPtr,
83 LineItem *linePtr));
84static int CreateLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
85 struct Tk_Item *itemPtr, int argc, char **argv));
86static void DeleteLine _ANSI_ARGS_((Tk_Item *itemPtr));
87static void DisplayLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
88 Tk_Item *itemPtr, Drawable dst));
89static int LineCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr,
90 Tk_Item *itemPtr, int argc, char **argv));
91static int LineToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
92 Tk_Item *itemPtr, double *rectPtr));
93static double LineToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr,
94 Tk_Item *itemPtr, double *coordPtr));
95static int ParseArrowShape _ANSI_ARGS_((ClientData clientData,
96 Tcl_Interp *interp, Tk_Window tkwin, char *value,
97 char *recordPtr, int offset));
98static char * PrintArrowShape _ANSI_ARGS_((ClientData clientData,
99 Tk_Window tkwin, char *recordPtr, int offset,
100 Tcl_FreeProc **freeProcPtr));
101static void ScaleLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
102 Tk_Item *itemPtr, double originX, double originY,
103 double scaleX, double scaleY));
104static 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
113static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
114 PrintArrowShape, (ClientData) NULL};
115
116static 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
147Tk_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
174static Tk_Uid noneUid = NULL;
175static Tk_Uid firstUid = NULL;
176static Tk_Uid lastUid = NULL;
177static 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\f
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
208static int
209CreateLine(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\f
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
299static int
300LineCoords(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\f
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
368static int
369ConfigureLine(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\f
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
488static void
489DeleteLine(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\f
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
532static void
533ComputeLineBbox(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\f
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
633static void
634DisplayLine(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\f
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 */
730static double
731LineToPoint(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\f
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 */
932static int
933LineToArea(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\f
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
1134static void
1135ScaleLine(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\f
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
1186static void
1187TranslateLine(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\f
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 */
1239static int
1240ParseArrowShape(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\f
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 */
1302static char *
1303PrintArrowShape(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\f
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 */
1345static int
1346ConfigureArrows(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}
Impressum, Datenschutz