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