source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas-line.c @ 19532

Revision 19532, 37.8 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19531, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
3 * All rights reserved.
4 *
5 * This file is part of the Gnome Library.
6 *
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22/*
23  @NOTATION@
24 */
25
26/* Line/curve item type for GnomeCanvas widget
27 *
28 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
29 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
30 *
31 *
32 * Author: Federico Mena <federico@nuclecu.unam.mx>
33 */
34
35#include <config.h>
36#include <math.h>
37#include <string.h>
38#include "libart_lgpl/art_vpath.h"
39#include "libart_lgpl/art_svp.h"
40#include "libart_lgpl/art_svp_vpath.h"
41#include "libart_lgpl/art_svp_vpath_stroke.h"
42#include "libgnomecanvas.h"
43
44#define noVERBOSE
45
46#define DEFAULT_SPLINE_STEPS 12         /* this is what Tk uses */
47#define NUM_ARROW_POINTS     6          /* number of points in an arrowhead */
48#define NUM_STATIC_POINTS    256        /* number of static points to use to avoid allocating arrays */
49
50
51#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
52        if (x < bx1)                            \
53                bx1 = x;                        \
54                                                \
55        if (x > bx2)                            \
56                bx2 = x;                        \
57                                                \
58        if (y < by1)                            \
59                by1 = y;                        \
60                                                \
61        if (y > by2)                            \
62                by2 = y;                        \
63}
64
65
66enum {
67        PROP_0,
68        PROP_POINTS,
69        PROP_FILL_COLOR,
70        PROP_FILL_COLOR_GDK,
71        PROP_FILL_COLOR_RGBA,
72        PROP_FILL_STIPPLE,
73        PROP_WIDTH_PIXELS,
74        PROP_WIDTH_UNITS,
75        PROP_CAP_STYLE,
76        PROP_JOIN_STYLE,
77        PROP_LINE_STYLE,
78        PROP_FIRST_ARROWHEAD,
79        PROP_LAST_ARROWHEAD,
80        PROP_SMOOTH,
81        PROP_SPLINE_STEPS,
82        PROP_ARROW_SHAPE_A,
83        PROP_ARROW_SHAPE_B,
84        PROP_ARROW_SHAPE_C
85};
86
87
88static void gnome_canvas_line_class_init   (GnomeCanvasLineClass *class);
89static void gnome_canvas_line_init         (GnomeCanvasLine      *line);
90static void gnome_canvas_line_destroy      (GtkObject            *object);
91static void gnome_canvas_line_set_property (GObject              *object,
92                                            guint                 param_id,
93                                            const GValue         *value,
94                                            GParamSpec           *pspec);
95static void gnome_canvas_line_get_property (GObject              *object,
96                                            guint                 param_id,
97                                            GValue               *value,
98                                            GParamSpec           *pspec);
99
100static void   gnome_canvas_line_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
101static void   gnome_canvas_line_realize     (GnomeCanvasItem *item);
102static void   gnome_canvas_line_unrealize   (GnomeCanvasItem *item);
103static void   gnome_canvas_line_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
104                                             int x, int y, int width, int height);
105static double gnome_canvas_line_point       (GnomeCanvasItem *item, double x, double y,
106                                             int cx, int cy, GnomeCanvasItem **actual_item);
107static void   gnome_canvas_line_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
108static void   gnome_canvas_line_render      (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
109
110
111static GnomeCanvasItemClass *parent_class;
112
113
114GType
115gnome_canvas_line_get_type (void)
116{
117        static GType line_type;
118
119        if (!line_type) {
120                static const GTypeInfo object_info = {
121                        sizeof (GnomeCanvasLineClass),
122                        (GBaseInitFunc) NULL,
123                        (GBaseFinalizeFunc) NULL,
124                        (GClassInitFunc) gnome_canvas_line_class_init,
125                        (GClassFinalizeFunc) NULL,
126                        NULL,                   /* class_data */
127                        sizeof (GnomeCanvasLine),
128                        0,                      /* n_preallocs */
129                        (GInstanceInitFunc) gnome_canvas_line_init,
130                        NULL                    /* value_table */
131                };
132
133                line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
134                                                    &object_info, 0);
135        }
136
137        return line_type;
138}
139
140static void
141gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
142{
143        GObjectClass *gobject_class;
144        GtkObjectClass *object_class;
145        GnomeCanvasItemClass *item_class;
146
147        gobject_class = (GObjectClass *) class;
148        object_class = (GtkObjectClass *) class;
149        item_class = (GnomeCanvasItemClass *) class;
150
151        parent_class = g_type_class_peek_parent (class);
152
153        gobject_class->set_property = gnome_canvas_line_set_property;
154        gobject_class->get_property = gnome_canvas_line_get_property;
155
156        g_object_class_install_property
157                (gobject_class,
158                 PROP_POINTS,
159                 g_param_spec_boxed ("points", NULL, NULL,
160                                     GNOME_TYPE_CANVAS_POINTS,
161                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
162        g_object_class_install_property
163                (gobject_class,
164                 PROP_FILL_COLOR,
165                 g_param_spec_string ("fill_color", NULL, NULL,
166                                      NULL,
167                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
168        g_object_class_install_property
169                (gobject_class,
170                 PROP_FILL_COLOR_GDK,
171                 g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
172                                     GDK_TYPE_COLOR,
173                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174        g_object_class_install_property
175                (gobject_class,
176                 PROP_FILL_COLOR_RGBA,
177                 g_param_spec_uint ("fill_color_rgba", NULL, NULL,
178                                    0, G_MAXUINT, 0,
179                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180        g_object_class_install_property
181                (gobject_class,
182                 PROP_FILL_STIPPLE,
183                 g_param_spec_object ("fill_stipple", NULL, NULL,
184                                      GDK_TYPE_DRAWABLE,
185                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186        g_object_class_install_property
187                (gobject_class,
188                 PROP_WIDTH_PIXELS,
189                 g_param_spec_uint ("width_pixels", NULL, NULL,
190                                    0, G_MAXUINT, 0,
191                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192        g_object_class_install_property
193                (gobject_class,
194                 PROP_WIDTH_UNITS,
195                 g_param_spec_double ("width_units", NULL, NULL,
196                                      0.0, G_MAXDOUBLE, 0.0,
197                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
198        g_object_class_install_property
199                (gobject_class,
200                 PROP_CAP_STYLE,
201                 g_param_spec_enum ("cap_style", NULL, NULL,
202                                    GDK_TYPE_CAP_STYLE,
203                                    GDK_CAP_BUTT,
204                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205        g_object_class_install_property
206                (gobject_class,
207                 PROP_JOIN_STYLE,
208                 g_param_spec_enum ("join_style", NULL, NULL,
209                                    GDK_TYPE_JOIN_STYLE,
210                                    GDK_JOIN_MITER,
211                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
212        g_object_class_install_property
213                (gobject_class,
214                 PROP_LINE_STYLE,
215                 g_param_spec_enum ("line_style", NULL, NULL,
216                                    GDK_TYPE_LINE_STYLE,
217                                    GDK_LINE_SOLID,
218                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
219        g_object_class_install_property
220                (gobject_class,
221                 PROP_FIRST_ARROWHEAD,
222                 g_param_spec_boolean ("first_arrowhead", NULL, NULL,
223                                       FALSE,
224                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
225        g_object_class_install_property
226                (gobject_class,
227                 PROP_LAST_ARROWHEAD,
228                 g_param_spec_boolean ("last_arrowhead", NULL, NULL,
229                                       FALSE,
230                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231        g_object_class_install_property
232                (gobject_class,
233                 PROP_SMOOTH,
234                 g_param_spec_boolean ("smooth", NULL, NULL,
235                                       FALSE,
236                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
237        g_object_class_install_property
238                (gobject_class,
239                 PROP_SPLINE_STEPS,
240                 g_param_spec_uint ("spline_steps", NULL, NULL,
241                                    0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
242                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
243        g_object_class_install_property
244                (gobject_class,
245                 PROP_ARROW_SHAPE_A,
246                 g_param_spec_double ("arrow_shape_a", NULL, NULL,
247                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
248                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
249        g_object_class_install_property
250                (gobject_class,
251                 PROP_ARROW_SHAPE_B,
252                 g_param_spec_double ("arrow_shape_b", NULL, NULL,
253                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
254                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
255        g_object_class_install_property
256                (gobject_class,
257                 PROP_ARROW_SHAPE_C,
258                 g_param_spec_double ("arrow_shape_c", NULL, NULL,
259                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
260                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
261
262        object_class->destroy = gnome_canvas_line_destroy;
263
264        item_class->update = gnome_canvas_line_update;
265        item_class->realize = gnome_canvas_line_realize;
266        item_class->unrealize = gnome_canvas_line_unrealize;
267        item_class->draw = gnome_canvas_line_draw;
268        item_class->point = gnome_canvas_line_point;
269        item_class->bounds = gnome_canvas_line_bounds;
270
271        item_class->render = gnome_canvas_line_render;
272}
273
274static void
275gnome_canvas_line_init (GnomeCanvasLine *line)
276{
277        line->width = 0.0;
278        line->cap = GDK_CAP_BUTT;
279        line->join = GDK_JOIN_MITER;
280        line->line_style = GDK_LINE_SOLID;
281        line->shape_a = 0.0;
282        line->shape_b = 0.0;
283        line->shape_c = 0.0;
284        line->spline_steps = DEFAULT_SPLINE_STEPS;
285}
286
287static void
288gnome_canvas_line_destroy (GtkObject *object)
289{
290        GnomeCanvasLine *line;
291
292        g_return_if_fail (object != NULL);
293        g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
294
295        line = GNOME_CANVAS_LINE (object);
296
297        /* remember, destroy can be run multiple times! */
298
299        if (line->coords)
300                g_free (line->coords);
301        line->coords = NULL;
302
303        if (line->first_coords)
304                g_free (line->first_coords);
305        line->first_coords = NULL;
306
307        if (line->last_coords)
308                g_free (line->last_coords);
309        line->last_coords = NULL;
310
311        if (line->stipple)
312                gdk_bitmap_unref (line->stipple);
313        line->stipple = NULL;
314
315        if (line->fill_svp)
316                art_svp_free (line->fill_svp);
317        line->fill_svp = NULL;
318
319        if (line->first_svp)
320                art_svp_free (line->first_svp);
321        line->first_svp = NULL;
322
323        if (line->last_svp)
324                art_svp_free (line->last_svp);
325        line->last_svp = NULL;
326
327        if (GTK_OBJECT_CLASS (parent_class)->destroy)
328                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
329}
330
331/* Computes the bounding box of the line, including its arrow points.  Assumes that the number of
332 * points in the line is not zero.
333 */
334static void
335get_bounds (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
336{
337        double *coords;
338        double x1, y1, x2, y2;
339        double width;
340        int i;
341
342        if (!line->coords) {
343            *bx1 = *by1 = *bx2 = *by2 = 0.0;
344            return;
345        }
346       
347        /* Find bounding box of line's points */
348
349        x1 = x2 = line->coords[0];
350        y1 = y2 = line->coords[1];
351
352        for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
353                GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
354
355        /* Add possible over-estimate for wide lines */
356
357        if (line->width_pixels)
358                width = line->width / line->item.canvas->pixels_per_unit;
359        else
360                width = line->width;
361
362        x1 -= width;
363        y1 -= width;
364        x2 += width;
365        y2 += width;
366
367        /* For mitered lines, make a second pass through all the points.  Compute the location of
368         * the two miter vertex points and add them to the bounding box.
369         */
370
371        if (line->join == GDK_JOIN_MITER)
372                for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
373                        double mx1, my1, mx2, my2;
374
375                        if (gnome_canvas_get_miter_points (coords[0], coords[1],
376                                                           coords[2], coords[3],
377                                                           coords[4], coords[5],
378                                                           width,
379                                                           &mx1, &my1, &mx2, &my2)) {
380                                GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
381                                GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
382                        }
383                }
384
385        /* Add the arrow points, if any */
386
387        if (line->first_arrow && line->first_coords)
388                for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
389                        GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
390
391        if (line->last_arrow && line->last_coords)
392                for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
393                        GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
394
395        /* Done */
396
397        *bx1 = x1;
398        *by1 = y1;
399        *bx2 = x2;
400        *by2 = y2;
401}
402
403/* Computes the bounding box of the line, in canvas coordinates.  Assumes that the number of points in the polygon is
404 * not zero. Affine is the i2c transformation.
405 */
406static void
407get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
408{
409        GnomeCanvasItem *item;
410
411        /* It would be possible to tighten the bounds somewhat by transforming the individual points before
412           aggregating them into the bbox. But it hardly seems worth it. */
413        ArtDRect bbox_world;
414        ArtDRect bbox_canvas;
415
416        item = GNOME_CANVAS_ITEM (line);
417
418        get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
419
420        art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
421        /* include 1 pixel of fudge */
422        *bx1 = bbox_canvas.x0 - 1;
423        *by1 = bbox_canvas.y0 - 1;
424        *bx2 = bbox_canvas.x1 + 1;
425        *by2 = bbox_canvas.y1 + 1;
426}
427
428/* Recalculates the arrow polygons for the line */
429static void
430reconfigure_arrows (GnomeCanvasLine *line)
431{
432        double *poly, *coords;
433        double dx, dy, length;
434        double sin_theta, cos_theta, tmp;
435        double frac_height;     /* Line width as fraction of arrowhead width */
436        double backup;          /* Distance to backup end points so the line ends in the middle of the arrowhead */
437        double vx, vy;          /* Position of arrowhead vertex */
438        double shape_a, shape_b, shape_c;
439        double width;
440        int i;
441
442        if (line->num_points == 0)
443                return;
444
445        /* Set up things */
446
447        if (line->first_arrow) {
448                if (line->first_coords) {
449                        line->coords[0] = line->first_coords[0];
450                        line->coords[1] = line->first_coords[1];
451                } else
452                        line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
453        } else if (line->first_coords) {
454                line->coords[0] = line->first_coords[0];
455                line->coords[1] = line->first_coords[1];
456
457                g_free (line->first_coords);
458                line->first_coords = NULL;
459        }
460
461        i = 2 * (line->num_points - 1);
462
463        if (line->last_arrow) {
464                if (line->last_coords) {
465                        line->coords[i] = line->last_coords[0];
466                        line->coords[i + 1] = line->last_coords[1];
467                } else
468                        line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
469        } else if (line->last_coords) {
470                line->coords[i] = line->last_coords[0];
471                line->coords[i + 1] = line->last_coords[1];
472
473                g_free (line->last_coords);
474                line->last_coords = NULL;
475        }
476
477        if (!line->first_arrow && !line->last_arrow)
478                return;
479
480        if (line->width_pixels)
481                width = line->width / line->item.canvas->pixels_per_unit;
482        else
483                width = line->width;
484
485        /* Add fudge value for better-looking results */
486
487        shape_a = line->shape_a;
488        shape_b = line->shape_b;
489        shape_c = line->shape_c + width / 2.0;
490
491        if (line->width_pixels) {
492                shape_a /= line->item.canvas->pixels_per_unit;
493                shape_b /= line->item.canvas->pixels_per_unit;
494                shape_c /= line->item.canvas->pixels_per_unit;
495        }
496
497        shape_a += 0.001;
498        shape_b += 0.001;
499        shape_c += 0.001;
500
501        /* Compute the polygon for the first arrowhead and adjust the first point in the line so
502         * that the line does not stick out past the leading edge of the arrowhead.
503         */
504
505        frac_height = (line->width / 2.0) / shape_c;
506        backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
507
508        if (line->first_arrow) {
509                poly = line->first_coords;
510                poly[0] = poly[10] = line->coords[0];
511                poly[1] = poly[11] = line->coords[1];
512
513                dx = poly[0] - line->coords[2];
514                dy = poly[1] - line->coords[3];
515                length = sqrt (dx * dx + dy * dy);
516                if (length < GNOME_CANVAS_EPSILON)
517                        sin_theta = cos_theta = 0.0;
518                else {
519                        sin_theta = dy / length;
520                        cos_theta = dx / length;
521                }
522
523                vx = poly[0] - shape_a * cos_theta;
524                vy = poly[1] - shape_a * sin_theta;
525
526                tmp = shape_c * sin_theta;
527
528                poly[2] = poly[0] - shape_b * cos_theta + tmp;
529                poly[8] = poly[2] - 2.0 * tmp;
530
531                tmp = shape_c * cos_theta;
532
533                poly[3] = poly[1] - shape_b * sin_theta - tmp;
534                poly[9] = poly[3] + 2.0 * tmp;
535
536                poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
537                poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
538                poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
539                poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
540
541                /* Move the first point towards the second so that the corners at the end of the
542                 * line are inside the arrowhead.
543                 */
544
545                line->coords[0] = poly[0] - backup * cos_theta;
546                line->coords[1] = poly[1] - backup * sin_theta;
547        }
548
549        /* Same process for last arrowhead */
550
551        if (line->last_arrow) {
552                coords = line->coords + 2 * (line->num_points - 2);
553                poly = line->last_coords;
554                poly[0] = poly[10] = coords[2];
555                poly[1] = poly[11] = coords[3];
556
557                dx = poly[0] - coords[0];
558                dy = poly[1] - coords[1];
559                length = sqrt (dx * dx + dy * dy);
560                if (length < GNOME_CANVAS_EPSILON)
561                        sin_theta = cos_theta = 0.0;
562                else {
563                        sin_theta = dy / length;
564                        cos_theta = dx / length;
565                }
566
567                vx = poly[0] - shape_a * cos_theta;
568                vy = poly[1] - shape_a * sin_theta;
569
570                tmp = shape_c * sin_theta;
571
572                poly[2] = poly[0] - shape_b * cos_theta + tmp;
573                poly[8] = poly[2] - 2.0 * tmp;
574
575                tmp = shape_c * cos_theta;
576
577                poly[3] = poly[1] - shape_b * sin_theta - tmp;
578                poly[9] = poly[3] + 2.0 * tmp;
579
580                poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
581                poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
582                poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
583                poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
584
585                coords[2] = poly[0] - backup * cos_theta;
586                coords[3] = poly[1] - backup * sin_theta;
587        }
588}
589
590/* Convenience function to set the line's GC's foreground color */
591static void
592set_line_gc_foreground (GnomeCanvasLine *line)
593{
594        GdkColor c;
595
596        if (!line->gc)
597                return;
598
599        c.pixel = line->fill_pixel;
600        gdk_gc_set_foreground (line->gc, &c);
601}
602
603/* Recalculate the line's width and set it in its GC */
604static void
605set_line_gc_width (GnomeCanvasLine *line)
606{
607        int width;
608
609        if (!line->gc)
610                return;
611
612        if (line->width_pixels)
613                width = (int) line->width;
614        else
615                width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
616
617        gdk_gc_set_line_attributes (line->gc,
618                                    width,
619                                    line->line_style,
620                                    (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
621                                    line->join);
622}
623
624/* Sets the stipple pattern for the line */
625static void
626set_stipple (GnomeCanvasLine *line, GdkBitmap *stipple, int reconfigure)
627{
628        if (line->stipple && !reconfigure)
629                gdk_bitmap_unref (line->stipple);
630
631        line->stipple = stipple;
632        if (stipple && !reconfigure)
633                gdk_bitmap_ref (stipple);
634
635        if (line->gc) {
636                if (stipple) {
637                        gdk_gc_set_stipple (line->gc, stipple);
638                        gdk_gc_set_fill (line->gc, GDK_STIPPLED);
639                } else
640                        gdk_gc_set_fill (line->gc, GDK_SOLID);
641        }
642}
643
644static void
645gnome_canvas_line_set_property (GObject              *object,
646                                guint                 param_id,
647                                const GValue         *value,
648                                GParamSpec           *pspec)
649{
650        GnomeCanvasItem *item;
651        GnomeCanvasLine *line;
652        GnomeCanvasPoints *points;
653        GdkColor color = { 0, 0, 0, 0, };
654        GdkColor *pcolor;
655        gboolean color_changed;
656        int have_pixel;
657
658        g_return_if_fail (object != NULL);
659        g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
660
661        item = GNOME_CANVAS_ITEM (object);
662        line = GNOME_CANVAS_LINE (object);
663
664        color_changed = FALSE;
665        have_pixel = FALSE;
666
667        switch (param_id) {
668        case PROP_POINTS:
669                points = g_value_get_boxed (value);
670
671                if (line->coords) {
672                        g_free (line->coords);
673                        line->coords = NULL;
674                }
675
676                if (!points)
677                        line->num_points = 0;
678                else {
679                        line->num_points = points->num_points;
680                        line->coords = g_new (double, 2 * line->num_points);
681                        memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
682                }
683
684                /* Drop the arrowhead polygons if they exist -- they will be regenerated */
685
686                if (line->first_coords) {
687                        g_free (line->first_coords);
688                        line->first_coords = NULL;
689                }
690
691                if (line->last_coords) {
692                        g_free (line->last_coords);
693                        line->last_coords = NULL;
694                }
695
696                /* Since the line's points have changed, we need to re-generate arrowheads in
697                 * addition to recalculating the bounds.
698                 */
699                gnome_canvas_item_request_update (item);
700                break;
701
702        case PROP_FILL_COLOR:
703                if (g_value_get_string (value))
704                        gdk_color_parse (g_value_get_string (value), &color);
705                line->fill_rgba = ((color.red & 0xff00) << 16 |
706                                   (color.green & 0xff00) << 8 |
707                                   (color.blue & 0xff00) |
708                                   0xff);
709                color_changed = TRUE;
710                break;
711
712        case PROP_FILL_COLOR_GDK:
713                pcolor = g_value_get_boxed (value);
714                if (pcolor) {
715                        GdkColormap *colormap;
716                        color = *pcolor;
717
718                        colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
719                        gdk_rgb_find_color (colormap, &color);
720
721                        have_pixel = TRUE;
722                }
723
724                line->fill_rgba = ((color.red & 0xff00) << 16 |
725                                   (color.green & 0xff00) << 8 |
726                                   (color.blue & 0xff00) |
727                                   0xff);
728                color_changed = TRUE;
729                break;
730
731        case PROP_FILL_COLOR_RGBA:
732                line->fill_rgba = g_value_get_uint (value);
733                color_changed = TRUE;
734                break;
735
736        case PROP_FILL_STIPPLE:
737                set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
738                gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
739                break;
740
741        case PROP_WIDTH_PIXELS:
742                line->width = g_value_get_uint (value);
743                line->width_pixels = TRUE;
744                set_line_gc_width (line);
745                gnome_canvas_item_request_update (item);
746                break;
747
748        case PROP_WIDTH_UNITS:
749                line->width = fabs (g_value_get_double (value));
750                line->width_pixels = FALSE;
751                set_line_gc_width (line);
752                gnome_canvas_item_request_update (item);
753                break;
754
755        case PROP_CAP_STYLE:
756                line->cap = g_value_get_enum (value);
757                gnome_canvas_item_request_update (item);
758                break;
759
760        case PROP_JOIN_STYLE:
761                line->join = g_value_get_enum (value);
762                gnome_canvas_item_request_update (item);
763                break;
764
765        case PROP_LINE_STYLE:
766                line->line_style = g_value_get_enum (value);
767                set_line_gc_width (line);
768                gnome_canvas_item_request_update (item);
769                break;
770
771        case PROP_FIRST_ARROWHEAD:
772                line->first_arrow = g_value_get_boolean (value);
773                gnome_canvas_item_request_update (item);
774                break;
775
776        case PROP_LAST_ARROWHEAD:
777                line->last_arrow = g_value_get_boolean (value);
778                gnome_canvas_item_request_update (item);
779                break;
780
781        case PROP_SMOOTH:
782                /* FIXME */
783                break;
784
785        case PROP_SPLINE_STEPS:
786                /* FIXME */
787                break;
788
789        case PROP_ARROW_SHAPE_A:
790                line->shape_a = fabs (g_value_get_double (value));
791                gnome_canvas_item_request_update (item);
792                break;
793
794        case PROP_ARROW_SHAPE_B:
795                line->shape_b = fabs (g_value_get_double (value));
796                gnome_canvas_item_request_update (item);
797                break;
798
799        case PROP_ARROW_SHAPE_C:
800                line->shape_c = fabs (g_value_get_double (value));
801                gnome_canvas_item_request_update (item);
802                break;
803
804        default:
805                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
806                break;
807        }
808
809        if (color_changed) {
810                if (have_pixel)
811                        line->fill_pixel = color.pixel;
812                else
813                        line->fill_pixel = gnome_canvas_get_color_pixel (item->canvas,
814                                                                         line->fill_rgba);
815
816                if (!item->canvas->aa)
817                        set_line_gc_foreground (line);
818
819                gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
820
821                if (line->first_svp)
822                        gnome_canvas_item_request_redraw_svp (item, line->first_svp);
823
824                if (line->last_svp)
825                        gnome_canvas_item_request_redraw_svp (item, line->last_svp);
826
827        }
828}
829
830/* Returns a copy of the line's points without the endpoint adjustments for
831 * arrowheads.
832 */
833static GnomeCanvasPoints *
834get_points (GnomeCanvasLine *line)
835{
836        GnomeCanvasPoints *points;
837        int start_ofs, end_ofs;
838
839        if (line->num_points == 0)
840                return NULL;
841
842        start_ofs = end_ofs = 0;
843
844        points = gnome_canvas_points_new (line->num_points);
845
846        /* Invariant:  if first_coords or last_coords exist, then the line's
847         * endpoints have been adjusted.
848         */
849
850        if (line->first_coords) {
851                start_ofs = 1;
852
853                points->coords[0] = line->first_coords[0];
854                points->coords[1] = line->first_coords[1];
855        }
856
857        if (line->last_coords) {
858                end_ofs = 1;
859
860                points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
861                points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
862        }
863
864        memcpy (points->coords + 2 * start_ofs,
865                line->coords + 2 * start_ofs,
866                2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
867
868        return points;
869}
870
871static void
872gnome_canvas_line_get_property (GObject              *object,
873                                guint                 param_id,
874                                GValue               *value,
875                                GParamSpec           *pspec)
876{
877        GnomeCanvasLine *line;
878
879        g_return_if_fail (object != NULL);
880        g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
881
882        line = GNOME_CANVAS_LINE (object);
883
884        switch (param_id) {
885        case PROP_POINTS:
886                g_value_set_boxed (value, get_points (line));
887                break;
888
889        case PROP_FILL_COLOR:
890                g_value_set_string_take_ownership (value,
891                                                   g_strdup_printf ("#%02x%02x%02x",
892                                                                    line->fill_rgba >> 24,
893                                                                    (line->fill_rgba >> 16) & 0xff,
894                                                                    (line->fill_rgba >> 8) & 0xff));
895                break;
896
897        case PROP_FILL_COLOR_GDK: {
898                GnomeCanvas *canvas = GNOME_CANVAS_ITEM (line)->canvas;
899                GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
900                GdkColor color;
901
902                gdk_colormap_query_color (colormap, line->fill_pixel, &color);
903                g_value_set_boxed (value, &color);
904                break;
905        }
906
907        case PROP_FILL_COLOR_RGBA:
908                g_value_set_uint (value, line->fill_rgba);
909                break;
910
911        case PROP_FILL_STIPPLE:
912                g_value_set_object (value, line->stipple);
913                break;
914
915        case PROP_WIDTH_PIXELS:
916                g_value_set_uint (value, line->width);
917                break;
918               
919        case PROP_WIDTH_UNITS:
920                g_value_set_double (value, line->width);
921                break;
922               
923        case PROP_CAP_STYLE:
924                g_value_set_enum (value, line->cap);
925                break;
926
927        case PROP_JOIN_STYLE:
928                g_value_set_enum (value, line->join);
929                break;
930
931        case PROP_LINE_STYLE:
932                g_value_set_enum (value, line->line_style);
933                break;
934
935        case PROP_FIRST_ARROWHEAD:
936                g_value_set_boolean (value, line->first_arrow);
937                break;
938
939        case PROP_LAST_ARROWHEAD:
940                g_value_set_boolean (value, line->last_arrow);
941                break;
942
943        case PROP_SMOOTH:
944                g_value_set_boolean (value, line->smooth);
945                break;
946
947        case PROP_SPLINE_STEPS:
948                g_value_set_uint (value, line->spline_steps);
949                break;
950
951        case PROP_ARROW_SHAPE_A:
952                g_value_set_double (value, line->shape_a);
953                break;
954
955        case PROP_ARROW_SHAPE_B:
956                g_value_set_double (value, line->shape_b);
957                break;
958
959        case PROP_ARROW_SHAPE_C:
960                g_value_set_double (value, line->shape_c);
961                break;
962
963        default:
964                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
965                break;
966        }
967}
968
969static void
970gnome_canvas_line_render (GnomeCanvasItem *item,
971                             GnomeCanvasBuf *buf)
972{
973        GnomeCanvasLine *line;
974
975        line = GNOME_CANVAS_LINE (item);
976
977        if (line->fill_svp != NULL)
978                gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
979
980        if (line->first_svp != NULL)
981                gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
982
983        if (line->last_svp != NULL)
984                gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
985}
986
987
988static ArtSVP *
989svp_from_points (const double *item_coords, int num_points, const double affine[6])
990{
991        ArtVpath *vpath;
992        ArtSVP *svp;
993        double x, y;
994        int i;
995
996        vpath = art_new (ArtVpath, num_points + 2);
997
998        for (i = 0; i < num_points; i++) {
999                vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
1000                x = item_coords[i * 2];
1001                y = item_coords[i * 2 + 1];
1002                vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
1003                vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
1004        }
1005#if 0
1006        vpath[i].code = ART_LINETO;
1007        vpath[i].x = vpath[0].x;
1008        vpath[i].y = vpath[0].y;
1009        i++;
1010#endif
1011        vpath[i].code = ART_END;
1012        vpath[i].x = 0;
1013        vpath[i].y = 0;
1014
1015        svp = art_svp_from_vpath (vpath);
1016
1017        art_free (vpath);
1018
1019        return svp;
1020}
1021
1022static void
1023gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1024{
1025        GnomeCanvasLine *line;
1026        int i;
1027        ArtVpath *vpath;
1028        ArtPoint pi, pc;
1029        double width;
1030        ArtSVP *svp;
1031        double x1, y1, x2, y2;
1032
1033        line = GNOME_CANVAS_LINE (item);
1034
1035        if (parent_class->update)
1036                (* parent_class->update) (item, affine, clip_path, flags);
1037
1038        reconfigure_arrows (line);
1039
1040        if (item->canvas->aa) {
1041                gnome_canvas_item_reset_bounds (item);
1042
1043                vpath = art_new (ArtVpath, line->num_points + 2);
1044
1045                for (i = 0; i < line->num_points; i++) {
1046                        pi.x = line->coords[i * 2];
1047                        pi.y = line->coords[i * 2 + 1];
1048                        art_affine_point (&pc, &pi, affine);
1049                        vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
1050                        vpath[i].x = pc.x;
1051                        vpath[i].y = pc.y;
1052                }
1053                vpath[i].code = ART_END;
1054                vpath[i].x = 0;
1055                vpath[i].y = 0;
1056
1057                if (line->width_pixels)
1058                        width = line->width;
1059                else
1060                        width = line->width * art_affine_expansion (affine);
1061
1062                if (width < 0.5)
1063                        width = 0.5;
1064
1065                svp = art_svp_vpath_stroke (vpath,
1066                                            gnome_canvas_join_gdk_to_art (line->join),
1067                                            gnome_canvas_cap_gdk_to_art (line->cap),
1068                                            width,
1069                                            4,
1070                                            0.25);
1071                art_free (vpath);
1072
1073                gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
1074
1075                if (line->first_arrow && line->first_coords)
1076                        svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
1077                else
1078                        svp = NULL;
1079
1080                gnome_canvas_item_update_svp_clip (item, &line->first_svp, svp, clip_path);
1081
1082                if (line->last_arrow && line->last_coords)
1083                        svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
1084                else
1085                        svp = NULL;
1086
1087                gnome_canvas_item_update_svp_clip (item, &line->last_svp, svp, clip_path);
1088
1089        } else {
1090                set_line_gc_foreground (line);
1091                set_line_gc_width (line);
1092                set_stipple (line, line->stipple, TRUE);
1093
1094                get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
1095                gnome_canvas_update_bbox (item, x1, y1, x2, y2);
1096        }
1097}
1098
1099static void
1100gnome_canvas_line_realize (GnomeCanvasItem *item)
1101{
1102        GnomeCanvasLine *line;
1103
1104        line = GNOME_CANVAS_LINE (item);
1105
1106        if (parent_class->realize)
1107                (* parent_class->realize) (item);
1108
1109        line->gc = gdk_gc_new (item->canvas->layout.bin_window);
1110
1111#if 0
1112        (* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
1113#endif
1114}
1115
1116static void
1117gnome_canvas_line_unrealize (GnomeCanvasItem *item)
1118{
1119        GnomeCanvasLine *line;
1120
1121        line = GNOME_CANVAS_LINE (item);
1122
1123        gdk_gc_unref (line->gc);
1124        line->gc = NULL;
1125
1126        if (parent_class->unrealize)
1127                (* parent_class->unrealize) (item);
1128}
1129
1130static void
1131item_to_canvas (GnomeCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
1132                int *num_drawn_points, double i2c[6], int x, int y)
1133{
1134        int i;
1135        int old_cx, old_cy;
1136        int cx, cy;
1137        ArtPoint pi, pc;
1138
1139#ifdef VERBOSE
1140        {
1141                char str[128];
1142                art_affine_to_string (str, i2c);
1143                g_print ("line item_to_canvas %s\n", str);
1144        }
1145#endif
1146
1147        /* the first point is always drawn */
1148
1149        pi.x = item_coords[0];
1150        pi.y = item_coords[1];
1151        art_affine_point (&pc, &pi, i2c);
1152        cx = floor (pc.x + 0.5);
1153        cy = floor (pc.y + 0.5);
1154        canvas_coords->x = cx - x;
1155        canvas_coords->y = cy - y;
1156        canvas_coords++;
1157        old_cx = cx;
1158        old_cy = cy;
1159        *num_drawn_points = 1;
1160
1161        for (i = 1; i < num_points; i++) {
1162                pi.x = item_coords[i * 2];
1163                pi.y = item_coords[i * 2 + 1];
1164                art_affine_point (&pc, &pi, i2c);
1165                cx = floor (pc.x + 0.5);
1166                cy = floor (pc.y + 0.5);
1167                if (old_cx != cx || old_cy != cy) {
1168                        canvas_coords->x = cx - x;
1169                        canvas_coords->y = cy - y;
1170                        old_cx = cx;
1171                        old_cy = cy;
1172                        canvas_coords++;
1173                        (*num_drawn_points)++;
1174                }
1175        }
1176}
1177
1178static void
1179gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
1180                        int x, int y, int width, int height)
1181{
1182        GnomeCanvasLine *line;
1183        GdkPoint static_points[NUM_STATIC_POINTS];
1184        GdkPoint *points;
1185        int actual_num_points_drawn;
1186        double i2c[6];
1187
1188        line = GNOME_CANVAS_LINE (item);
1189
1190        if (line->num_points == 0)
1191                return;
1192
1193        /* Build array of canvas pixel coordinates */
1194
1195        if (line->num_points <= NUM_STATIC_POINTS)
1196                points = static_points;
1197        else
1198                points = g_new (GdkPoint, line->num_points);
1199
1200
1201        gnome_canvas_item_i2c_affine (item, i2c);
1202
1203        item_to_canvas (item->canvas, line->coords, points, line->num_points,
1204                        &actual_num_points_drawn, i2c, x, y);
1205
1206        if (line->stipple)
1207                gnome_canvas_set_stipple_origin (item->canvas, line->gc);
1208
1209        gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
1210
1211        if (points != static_points)
1212                g_free (points);
1213
1214        /* Draw arrowheads */
1215
1216        points = static_points;
1217
1218        if (line->first_arrow) {
1219                item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
1220                                &actual_num_points_drawn, i2c, x, y);
1221                gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1222        }
1223
1224        if (line->last_arrow) {
1225                item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
1226                                &actual_num_points_drawn, i2c, x, y);
1227                gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1228        }
1229}
1230
1231static double
1232gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
1233                         int cx, int cy, GnomeCanvasItem **actual_item)
1234{
1235        GnomeCanvasLine *line;
1236        double *line_points = NULL, *coords;
1237        double static_points[2 * NUM_STATIC_POINTS];
1238        double poly[10];
1239        double best, dist;
1240        double dx, dy;
1241        double width;
1242        int num_points = 0, i;
1243        int changed_miter_to_bevel;
1244
1245#ifdef VERBOSE
1246        g_print ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
1247#endif
1248
1249        line = GNOME_CANVAS_LINE (item);
1250
1251        *actual_item = item;
1252
1253        best = 1.0e36;
1254
1255        /* Handle smoothed lines by generating an expanded set ot points */
1256
1257        if (line->smooth && (line->num_points > 2)) {
1258                /* FIXME */
1259        } else {
1260                num_points = line->num_points;
1261                line_points = line->coords;
1262        }
1263
1264        /* Compute a polygon for each edge of the line and test the point against it.  The effective
1265         * width of the line is adjusted so that it will be at least one pixel thick (so that zero
1266         * pixel-wide lines can be pickedup as well).
1267         */
1268
1269        if (line->width_pixels)
1270                width = line->width / item->canvas->pixels_per_unit;
1271        else
1272                width = line->width;
1273
1274        if (width < (1.0 / item->canvas->pixels_per_unit))
1275                width = 1.0 / item->canvas->pixels_per_unit;
1276
1277        changed_miter_to_bevel = 0;
1278
1279        for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
1280                /* If rounding is done around the first point, then compute distance between the
1281                 * point and the first point.
1282                 */
1283
1284                if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
1285                    || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
1286                        dx = coords[0] - x;
1287                        dy = coords[1] - y;
1288                        dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1289                        if (dist < GNOME_CANVAS_EPSILON) {
1290                                best = 0.0;
1291                                goto done;
1292                        } else if (dist < best)
1293                                best = dist;
1294                }
1295
1296                /* Compute the polygonal shape corresponding to this edge, with two points for the
1297                 * first point of the edge and two points for the last point of the edge.
1298                 */
1299
1300                if (i == num_points)
1301                        gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1302                                                      width, (line->cap == GDK_CAP_PROJECTING),
1303                                                      poly, poly + 1, poly + 2, poly + 3);
1304                else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
1305                        poly[0] = poly[6];
1306                        poly[1] = poly[7];
1307                        poly[2] = poly[4];
1308                        poly[3] = poly[5];
1309                } else {
1310                        gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1311                                                      width, FALSE,
1312                                                      poly, poly + 1, poly + 2, poly + 3);
1313
1314                        /* If this line uses beveled joints, then check the distance to a polygon
1315                         * comprising the last two points of the previous polygon and the first two
1316                         * from this polygon; this checks the wedges that fill the mitered point.
1317                         */
1318
1319                        if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
1320                                poly[8] = poly[0];
1321                                poly[9] = poly[1];
1322
1323                                dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1324                                if (dist < GNOME_CANVAS_EPSILON) {
1325                                        best = 0.0;
1326                                        goto done;
1327                                } else if (dist < best)
1328                                        best = dist;
1329
1330                                changed_miter_to_bevel = FALSE;
1331                        }
1332                }
1333
1334                if (i == 2)
1335                        gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1336                                                      width, (line->cap == GDK_CAP_PROJECTING),
1337                                                      poly + 4, poly + 5, poly + 6, poly + 7);
1338                else if (line->join == GDK_JOIN_MITER) {
1339                        if (!gnome_canvas_get_miter_points (coords[0], coords[1],
1340                                                            coords[2], coords[3],
1341                                                            coords[4], coords[5],
1342                                                            width,
1343                                                            poly + 4, poly + 5, poly + 6, poly + 7)) {
1344                                changed_miter_to_bevel = TRUE;
1345                                gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1346                                                              width, FALSE,
1347                                                              poly + 4, poly + 5, poly + 6, poly + 7);
1348                        }
1349                } else
1350                        gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1351                                                      width, FALSE,
1352                                                      poly + 4, poly + 5, poly + 6, poly + 7);
1353
1354                poly[8] = poly[0];
1355                poly[9] = poly[1];
1356
1357                dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1358                if (dist < GNOME_CANVAS_EPSILON) {
1359                        best = 0.0;
1360                        goto done;
1361                } else if (dist < best)
1362                        best = dist;
1363        }
1364
1365        /* If caps are rounded, check the distance to the cap around the final end point of the line */
1366
1367        if (line->cap == GDK_CAP_ROUND) {
1368                dx = coords[0] - x;
1369                dy = coords[1] - y;
1370                dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1371                if (dist < GNOME_CANVAS_EPSILON) {
1372                        best = 0.0;
1373                        goto done;
1374                } else
1375                        best = dist;
1376        }
1377
1378        /* sometimes the GnomeCanvasItem::update signal will not have
1379           been processed between deleting the arrow points and a call
1380           to this routine -- this can cause a segfault here */
1381        if ((line->first_arrow && !line->first_coords) ||
1382            (line->last_arrow && !line->last_coords))
1383                reconfigure_arrows(line);
1384
1385        /* If there are arrowheads, check the distance to them */
1386
1387        if (line->first_arrow) {
1388                dist = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
1389                if (dist < GNOME_CANVAS_EPSILON) {
1390                        best = 0.0;
1391                        goto done;
1392                } else
1393                        best = dist;
1394        }
1395
1396        if (line->last_arrow) {
1397                dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
1398                if (dist < GNOME_CANVAS_EPSILON) {
1399                        best = 0.0;
1400                        goto done;
1401                } else
1402                        best = dist;
1403        }
1404
1405done:
1406
1407        if ((line_points != static_points) && (line_points != line->coords))
1408                g_free (line_points);
1409
1410        return best;
1411}
1412
1413static void
1414gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1415{
1416        GnomeCanvasLine *line;
1417
1418        line = GNOME_CANVAS_LINE (item);
1419
1420        if (line->num_points == 0) {
1421                *x1 = *y1 = *x2 = *y2 = 0.0;
1422                return;
1423        }
1424
1425        get_bounds (line, x1, y1, x2, y2);
1426}
Note: See TracBrowser for help on using the repository browser.