OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkCanvLine.c] - Blame information for rev 579

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.