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