2 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
5 * This file is part of the Gnome Library.
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.
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.
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.
26 /* Line/curve item type for GnomeCanvas widget
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.
32 * Author: Federico Mena <federico@nuclecu.unam.mx>
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"
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 */
51 #define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
88 static void gnome_canvas_line_class_init (GnomeCanvasLineClass *class);
89 static void gnome_canvas_line_init (GnomeCanvasLine *line);
90 static void gnome_canvas_line_destroy (GtkObject *object);
91 static void gnome_canvas_line_set_property (GObject *object,
95 static void gnome_canvas_line_get_property (GObject *object,
100 static void gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
101 static void gnome_canvas_line_realize (GnomeCanvasItem *item);
102 static void gnome_canvas_line_unrealize (GnomeCanvasItem *item);
103 static void gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
104 int x, int y, int width, int height);
105 static double gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
106 int cx, int cy, GnomeCanvasItem **actual_item);
107 static void gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
108 static void gnome_canvas_line_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
111 static GnomeCanvasItemClass *parent_class;
115 gnome_canvas_line_get_type (void)
117 static GType line_type;
120 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),
129 (GInstanceInitFunc) gnome_canvas_line_init,
130 NULL /* value_table */
133 line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
141 gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
143 GObjectClass *gobject_class;
144 GtkObjectClass *object_class;
145 GnomeCanvasItemClass *item_class;
147 gobject_class = (GObjectClass *) class;
148 object_class = (GtkObjectClass *) class;
149 item_class = (GnomeCanvasItemClass *) class;
151 parent_class = g_type_class_peek_parent (class);
153 gobject_class->set_property = gnome_canvas_line_set_property;
154 gobject_class->get_property = gnome_canvas_line_get_property;
156 g_object_class_install_property
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
165 g_param_spec_string ("fill_color", NULL, NULL,
167 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
168 g_object_class_install_property
171 g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
173 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
174 g_object_class_install_property
176 PROP_FILL_COLOR_RGBA,
177 g_param_spec_uint ("fill_color_rgba", NULL, NULL,
179 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180 g_object_class_install_property
183 g_param_spec_object ("fill_stipple", NULL, NULL,
185 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
186 g_object_class_install_property
189 g_param_spec_uint ("width_pixels", NULL, NULL,
191 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
192 g_object_class_install_property
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
201 g_param_spec_enum ("cap_style", NULL, NULL,
204 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
205 g_object_class_install_property
208 g_param_spec_enum ("join_style", NULL, NULL,
211 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
212 g_object_class_install_property
215 g_param_spec_enum ("line_style", NULL, NULL,
218 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
219 g_object_class_install_property
221 PROP_FIRST_ARROWHEAD,
222 g_param_spec_boolean ("first_arrowhead", NULL, NULL,
224 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
225 g_object_class_install_property
228 g_param_spec_boolean ("last_arrowhead", NULL, NULL,
230 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
231 g_object_class_install_property
234 g_param_spec_boolean ("smooth", NULL, NULL,
236 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
237 g_object_class_install_property
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
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
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
258 g_param_spec_double ("arrow_shape_c", NULL, NULL,
259 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
260 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
262 object_class->destroy = gnome_canvas_line_destroy;
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;
271 item_class->render = gnome_canvas_line_render;
275 gnome_canvas_line_init (GnomeCanvasLine *line)
278 line->cap = GDK_CAP_BUTT;
279 line->join = GDK_JOIN_MITER;
280 line->line_style = GDK_LINE_SOLID;
284 line->spline_steps = DEFAULT_SPLINE_STEPS;
288 gnome_canvas_line_destroy (GtkObject *object)
290 GnomeCanvasLine *line;
292 g_return_if_fail (object != NULL);
293 g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
295 line = GNOME_CANVAS_LINE (object);
297 /* remember, destroy can be run multiple times! */
300 g_free (line->coords);
303 if (line->first_coords)
304 g_free (line->first_coords);
305 line->first_coords = NULL;
307 if (line->last_coords)
308 g_free (line->last_coords);
309 line->last_coords = NULL;
312 g_object_unref (line->stipple);
313 line->stipple = NULL;
316 art_svp_free (line->fill_svp);
317 line->fill_svp = NULL;
320 art_svp_free (line->first_svp);
321 line->first_svp = NULL;
324 art_svp_free (line->last_svp);
325 line->last_svp = NULL;
327 if (GTK_OBJECT_CLASS (parent_class)->destroy)
328 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
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.
335 get_bounds (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
338 double x1, y1, x2, y2;
343 *bx1 = *by1 = *bx2 = *by2 = 0.0;
347 /* Find bounding box of line's points */
349 x1 = x2 = line->coords[0];
350 y1 = y2 = line->coords[1];
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]);
355 /* Add possible over-estimate for wide lines */
357 if (line->width_pixels)
358 width = line->width / line->item.canvas->pixels_per_unit;
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.
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;
375 if (gnome_canvas_get_miter_points (coords[0], coords[1],
376 coords[2], coords[3],
377 coords[4], coords[5],
379 &mx1, &my1, &mx2, &my2)) {
380 GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
381 GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
385 /* Add the arrow points, if any */
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]);
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]);
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.
407 get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
409 /* It would be possible to tighten the bounds somewhat by transforming the individual points before
410 aggregating them into the bbox. But it hardly seems worth it. */
412 ArtDRect bbox_canvas;
414 get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
416 art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
417 /* include 1 pixel of fudge */
418 *bx1 = bbox_canvas.x0 - 1;
419 *by1 = bbox_canvas.y0 - 1;
420 *bx2 = bbox_canvas.x1 + 1;
421 *by2 = bbox_canvas.y1 + 1;
424 /* Recalculates the arrow polygons for the line */
426 reconfigure_arrows (GnomeCanvasLine *line)
428 double *poly, *coords;
429 double dx, dy, length;
430 double sin_theta, cos_theta, tmp;
431 double frac_height; /* Line width as fraction of arrowhead width */
432 double backup; /* Distance to backup end points so the line ends in the middle of the arrowhead */
433 double vx, vy; /* Position of arrowhead vertex */
434 double shape_a, shape_b, shape_c;
438 if (line->num_points == 0)
443 if (line->first_arrow) {
444 if (line->first_coords) {
445 line->coords[0] = line->first_coords[0];
446 line->coords[1] = line->first_coords[1];
448 line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
449 } else if (line->first_coords) {
450 line->coords[0] = line->first_coords[0];
451 line->coords[1] = line->first_coords[1];
453 g_free (line->first_coords);
454 line->first_coords = NULL;
457 i = 2 * (line->num_points - 1);
459 if (line->last_arrow) {
460 if (line->last_coords) {
461 line->coords[i] = line->last_coords[0];
462 line->coords[i + 1] = line->last_coords[1];
464 line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
465 } else if (line->last_coords) {
466 line->coords[i] = line->last_coords[0];
467 line->coords[i + 1] = line->last_coords[1];
469 g_free (line->last_coords);
470 line->last_coords = NULL;
473 if (!line->first_arrow && !line->last_arrow)
476 if (line->width_pixels)
477 width = line->width / line->item.canvas->pixels_per_unit;
481 /* Add fudge value for better-looking results */
483 shape_a = line->shape_a;
484 shape_b = line->shape_b;
485 shape_c = line->shape_c + width / 2.0;
487 if (line->width_pixels) {
488 shape_a /= line->item.canvas->pixels_per_unit;
489 shape_b /= line->item.canvas->pixels_per_unit;
490 shape_c /= line->item.canvas->pixels_per_unit;
497 /* Compute the polygon for the first arrowhead and adjust the first point in the line so
498 * that the line does not stick out past the leading edge of the arrowhead.
501 frac_height = (line->width / 2.0) / shape_c;
502 backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
504 if (line->first_arrow) {
505 poly = line->first_coords;
506 poly[0] = poly[10] = line->coords[0];
507 poly[1] = poly[11] = line->coords[1];
509 dx = poly[0] - line->coords[2];
510 dy = poly[1] - line->coords[3];
511 length = sqrt (dx * dx + dy * dy);
512 if (length < GNOME_CANVAS_EPSILON)
513 sin_theta = cos_theta = 0.0;
515 sin_theta = dy / length;
516 cos_theta = dx / length;
519 vx = poly[0] - shape_a * cos_theta;
520 vy = poly[1] - shape_a * sin_theta;
522 tmp = shape_c * sin_theta;
524 poly[2] = poly[0] - shape_b * cos_theta + tmp;
525 poly[8] = poly[2] - 2.0 * tmp;
527 tmp = shape_c * cos_theta;
529 poly[3] = poly[1] - shape_b * sin_theta - tmp;
530 poly[9] = poly[3] + 2.0 * tmp;
532 poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
533 poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
534 poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
535 poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
537 /* Move the first point towards the second so that the corners at the end of the
538 * line are inside the arrowhead.
541 line->coords[0] = poly[0] - backup * cos_theta;
542 line->coords[1] = poly[1] - backup * sin_theta;
545 /* Same process for last arrowhead */
547 if (line->last_arrow) {
548 coords = line->coords + 2 * (line->num_points - 2);
549 poly = line->last_coords;
550 poly[0] = poly[10] = coords[2];
551 poly[1] = poly[11] = coords[3];
553 dx = poly[0] - coords[0];
554 dy = poly[1] - coords[1];
555 length = sqrt (dx * dx + dy * dy);
556 if (length < GNOME_CANVAS_EPSILON)
557 sin_theta = cos_theta = 0.0;
559 sin_theta = dy / length;
560 cos_theta = dx / length;
563 vx = poly[0] - shape_a * cos_theta;
564 vy = poly[1] - shape_a * sin_theta;
566 tmp = shape_c * sin_theta;
568 poly[2] = poly[0] - shape_b * cos_theta + tmp;
569 poly[8] = poly[2] - 2.0 * tmp;
571 tmp = shape_c * cos_theta;
573 poly[3] = poly[1] - shape_b * sin_theta - tmp;
574 poly[9] = poly[3] + 2.0 * tmp;
576 poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
577 poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
578 poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
579 poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
581 coords[2] = poly[0] - backup * cos_theta;
582 coords[3] = poly[1] - backup * sin_theta;
586 /* Convenience function to set the line's GC's foreground color */
588 set_line_gc_foreground (GnomeCanvasLine *line)
595 c.pixel = line->fill_pixel;
596 gdk_gc_set_foreground (line->gc, &c);
599 /* Recalculate the line's width and set it in its GC */
601 set_line_gc_width (GnomeCanvasLine *line)
608 if (line->width_pixels)
609 width = (int) line->width;
611 width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
613 gdk_gc_set_line_attributes (line->gc,
616 (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
620 /* Sets the stipple pattern for the line */
622 set_stipple (GnomeCanvasLine *line, GdkBitmap *stipple, int reconfigure)
624 if (line->stipple && !reconfigure)
625 g_object_unref (line->stipple);
627 line->stipple = stipple;
628 if (stipple && !reconfigure)
629 g_object_ref (stipple);
633 gdk_gc_set_stipple (line->gc, stipple);
634 gdk_gc_set_fill (line->gc, GDK_STIPPLED);
636 gdk_gc_set_fill (line->gc, GDK_SOLID);
641 gnome_canvas_line_set_property (GObject *object,
646 GnomeCanvasItem *item;
647 GnomeCanvasLine *line;
648 GnomeCanvasPoints *points;
649 GdkColor color = { 0, 0, 0, 0, };
651 gboolean color_changed;
654 g_return_if_fail (object != NULL);
655 g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
657 item = GNOME_CANVAS_ITEM (object);
658 line = GNOME_CANVAS_LINE (object);
660 color_changed = FALSE;
665 points = g_value_get_boxed (value);
668 g_free (line->coords);
673 line->num_points = 0;
675 line->num_points = points->num_points;
676 line->coords = g_new (double, 2 * line->num_points);
677 memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
680 /* Drop the arrowhead polygons if they exist -- they will be regenerated */
682 if (line->first_coords) {
683 g_free (line->first_coords);
684 line->first_coords = NULL;
687 if (line->last_coords) {
688 g_free (line->last_coords);
689 line->last_coords = NULL;
692 /* Since the line's points have changed, we need to re-generate arrowheads in
693 * addition to recalculating the bounds.
695 gnome_canvas_item_request_update (item);
698 case PROP_FILL_COLOR:
699 if (g_value_get_string (value))
700 gdk_color_parse (g_value_get_string (value), &color);
701 line->fill_rgba = ((color.red & 0xff00) << 16 |
702 (color.green & 0xff00) << 8 |
703 (color.blue & 0xff00) |
705 color_changed = TRUE;
708 case PROP_FILL_COLOR_GDK:
709 pcolor = g_value_get_boxed (value);
711 GdkColormap *colormap;
714 colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
715 gdk_rgb_find_color (colormap, &color);
720 line->fill_rgba = ((color.red & 0xff00) << 16 |
721 (color.green & 0xff00) << 8 |
722 (color.blue & 0xff00) |
724 color_changed = TRUE;
727 case PROP_FILL_COLOR_RGBA:
728 line->fill_rgba = g_value_get_uint (value);
729 color_changed = TRUE;
732 case PROP_FILL_STIPPLE:
733 set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
734 gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
737 case PROP_WIDTH_PIXELS:
738 line->width = g_value_get_uint (value);
739 line->width_pixels = TRUE;
740 set_line_gc_width (line);
741 gnome_canvas_item_request_update (item);
744 case PROP_WIDTH_UNITS:
745 line->width = fabs (g_value_get_double (value));
746 line->width_pixels = FALSE;
747 set_line_gc_width (line);
748 gnome_canvas_item_request_update (item);
752 line->cap = g_value_get_enum (value);
753 gnome_canvas_item_request_update (item);
756 case PROP_JOIN_STYLE:
757 line->join = g_value_get_enum (value);
758 gnome_canvas_item_request_update (item);
761 case PROP_LINE_STYLE:
762 line->line_style = g_value_get_enum (value);
763 set_line_gc_width (line);
764 gnome_canvas_item_request_update (item);
767 case PROP_FIRST_ARROWHEAD:
768 line->first_arrow = g_value_get_boolean (value);
769 gnome_canvas_item_request_update (item);
772 case PROP_LAST_ARROWHEAD:
773 line->last_arrow = g_value_get_boolean (value);
774 gnome_canvas_item_request_update (item);
781 case PROP_SPLINE_STEPS:
785 case PROP_ARROW_SHAPE_A:
786 line->shape_a = fabs (g_value_get_double (value));
787 gnome_canvas_item_request_update (item);
790 case PROP_ARROW_SHAPE_B:
791 line->shape_b = fabs (g_value_get_double (value));
792 gnome_canvas_item_request_update (item);
795 case PROP_ARROW_SHAPE_C:
796 line->shape_c = fabs (g_value_get_double (value));
797 gnome_canvas_item_request_update (item);
801 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
807 line->fill_pixel = color.pixel;
809 line->fill_pixel = gnome_canvas_get_color_pixel (item->canvas,
812 if (!item->canvas->aa)
813 set_line_gc_foreground (line);
815 gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
818 gnome_canvas_item_request_redraw_svp (item, line->first_svp);
821 gnome_canvas_item_request_redraw_svp (item, line->last_svp);
826 /* Returns a copy of the line's points without the endpoint adjustments for
829 static GnomeCanvasPoints *
830 get_points (GnomeCanvasLine *line)
832 GnomeCanvasPoints *points;
833 int start_ofs, end_ofs;
835 if (line->num_points == 0)
838 start_ofs = end_ofs = 0;
840 points = gnome_canvas_points_new (line->num_points);
842 /* Invariant: if first_coords or last_coords exist, then the line's
843 * endpoints have been adjusted.
846 if (line->first_coords) {
849 points->coords[0] = line->first_coords[0];
850 points->coords[1] = line->first_coords[1];
853 if (line->last_coords) {
856 points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
857 points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
860 memcpy (points->coords + 2 * start_ofs,
861 line->coords + 2 * start_ofs,
862 2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
868 gnome_canvas_line_get_property (GObject *object,
873 GnomeCanvasLine *line;
875 g_return_if_fail (object != NULL);
876 g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
878 line = GNOME_CANVAS_LINE (object);
882 /* get_points returns a copy */
883 g_value_set_boxed_take_ownership (value, get_points (line));
886 case PROP_FILL_COLOR:
887 g_value_take_string (value,
888 g_strdup_printf ("#%02x%02x%02x",
889 line->fill_rgba >> 24,
890 (line->fill_rgba >> 16) & 0xff,
891 (line->fill_rgba >> 8) & 0xff));
894 case PROP_FILL_COLOR_GDK: {
895 GnomeCanvas *canvas = GNOME_CANVAS_ITEM (line)->canvas;
896 GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
899 gdk_colormap_query_color (colormap, line->fill_pixel, &color);
900 g_value_set_boxed (value, &color);
904 case PROP_FILL_COLOR_RGBA:
905 g_value_set_uint (value, line->fill_rgba);
908 case PROP_FILL_STIPPLE:
909 g_value_set_object (value, line->stipple);
912 case PROP_WIDTH_PIXELS:
913 g_value_set_uint (value, line->width);
916 case PROP_WIDTH_UNITS:
917 g_value_set_double (value, line->width);
921 g_value_set_enum (value, line->cap);
924 case PROP_JOIN_STYLE:
925 g_value_set_enum (value, line->join);
928 case PROP_LINE_STYLE:
929 g_value_set_enum (value, line->line_style);
932 case PROP_FIRST_ARROWHEAD:
933 g_value_set_boolean (value, line->first_arrow);
936 case PROP_LAST_ARROWHEAD:
937 g_value_set_boolean (value, line->last_arrow);
941 g_value_set_boolean (value, line->smooth);
944 case PROP_SPLINE_STEPS:
945 g_value_set_uint (value, line->spline_steps);
948 case PROP_ARROW_SHAPE_A:
949 g_value_set_double (value, line->shape_a);
952 case PROP_ARROW_SHAPE_B:
953 g_value_set_double (value, line->shape_b);
956 case PROP_ARROW_SHAPE_C:
957 g_value_set_double (value, line->shape_c);
961 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
967 gnome_canvas_line_render (GnomeCanvasItem *item,
970 GnomeCanvasLine *line;
972 line = GNOME_CANVAS_LINE (item);
974 if (line->fill_svp != NULL)
975 gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
977 if (line->first_svp != NULL)
978 gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
980 if (line->last_svp != NULL)
981 gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
986 svp_from_points (const double *item_coords, int num_points, const double affine[6])
993 vpath = art_new (ArtVpath, num_points + 2);
995 for (i = 0; i < num_points; i++) {
996 vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
997 x = item_coords[i * 2];
998 y = item_coords[i * 2 + 1];
999 vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
1000 vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
1003 vpath[i].code = ART_LINETO;
1004 vpath[i].x = vpath[0].x;
1005 vpath[i].y = vpath[0].y;
1008 vpath[i].code = ART_END;
1012 svp = art_svp_from_vpath (vpath);
1020 gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1022 GnomeCanvasLine *line;
1028 double x1, y1, x2, y2;
1030 line = GNOME_CANVAS_LINE (item);
1032 if (parent_class->update)
1033 (* parent_class->update) (item, affine, clip_path, flags);
1035 reconfigure_arrows (line);
1037 if (item->canvas->aa) {
1038 gnome_canvas_item_reset_bounds (item);
1040 vpath = art_new (ArtVpath, line->num_points + 2);
1042 for (i = 0; i < line->num_points; i++) {
1043 pi.x = line->coords[i * 2];
1044 pi.y = line->coords[i * 2 + 1];
1045 art_affine_point (&pc, &pi, affine);
1046 vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
1050 vpath[i].code = ART_END;
1054 if (line->width_pixels)
1055 width = line->width;
1057 width = line->width * art_affine_expansion (affine);
1062 svp = art_svp_vpath_stroke (vpath,
1063 gnome_canvas_join_gdk_to_art (line->join),
1064 gnome_canvas_cap_gdk_to_art (line->cap),
1070 gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
1072 if (line->first_arrow && line->first_coords) {
1073 svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
1074 gnome_canvas_item_update_svp_clip (item,
1075 &line->first_svp, svp, clip_path);
1079 if (line->last_arrow && line->last_coords) {
1080 svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
1081 gnome_canvas_item_update_svp_clip (item,
1082 &line->last_svp, svp, clip_path);
1087 set_line_gc_foreground (line);
1088 set_line_gc_width (line);
1089 set_stipple (line, line->stipple, TRUE);
1091 get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
1092 gnome_canvas_update_bbox (item, x1, y1, x2, y2);
1097 gnome_canvas_line_realize (GnomeCanvasItem *item)
1099 GnomeCanvasLine *line;
1101 line = GNOME_CANVAS_LINE (item);
1103 if (parent_class->realize)
1104 (* parent_class->realize) (item);
1106 line->gc = gdk_gc_new (item->canvas->layout.bin_window);
1109 (* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
1114 gnome_canvas_line_unrealize (GnomeCanvasItem *item)
1116 GnomeCanvasLine *line;
1118 line = GNOME_CANVAS_LINE (item);
1120 g_object_unref (line->gc);
1123 if (parent_class->unrealize)
1124 (* parent_class->unrealize) (item);
1128 item_to_canvas (GnomeCanvas * canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
1129 int *num_drawn_points, double i2c[6], int x, int y)
1139 art_affine_to_string (str, i2c);
1140 g_print ("line item_to_canvas %s\n", str);
1144 /* the first point is always drawn */
1146 pi.x = item_coords[0];
1147 pi.y = item_coords[1];
1148 art_affine_point (&pc, &pi, i2c);
1149 cx = floor (pc.x + 0.5);
1150 cy = floor (pc.y + 0.5);
1151 canvas_coords->x = cx - x;
1152 canvas_coords->y = cy - y;
1156 *num_drawn_points = 1;
1158 for (i = 1; i < num_points; i++) {
1159 pi.x = item_coords[i * 2];
1160 pi.y = item_coords[i * 2 + 1];
1161 art_affine_point (&pc, &pi, i2c);
1162 cx = floor (pc.x + 0.5);
1163 cy = floor (pc.y + 0.5);
1164 if (old_cx != cx || old_cy != cy) {
1165 canvas_coords->x = cx - x;
1166 canvas_coords->y = cy - y;
1170 (*num_drawn_points)++;
1176 gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
1177 int x, int y, int width, int height)
1179 GnomeCanvasLine *line;
1180 GdkPoint static_points[NUM_STATIC_POINTS];
1182 int actual_num_points_drawn;
1185 line = GNOME_CANVAS_LINE (item);
1187 if (line->num_points == 0)
1190 /* Build array of canvas pixel coordinates */
1192 if (line->num_points <= NUM_STATIC_POINTS)
1193 points = static_points;
1195 points = g_new (GdkPoint, line->num_points);
1198 gnome_canvas_item_i2c_affine (item, i2c);
1200 item_to_canvas (item->canvas, line->coords, points, line->num_points,
1201 &actual_num_points_drawn, i2c, x, y);
1204 gnome_canvas_set_stipple_origin (item->canvas, line->gc);
1206 gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
1208 if (points != static_points)
1211 /* Draw arrowheads */
1213 points = static_points;
1215 if (line->first_arrow) {
1216 item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
1217 &actual_num_points_drawn, i2c, x, y);
1218 gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1221 if (line->last_arrow) {
1222 item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
1223 &actual_num_points_drawn, i2c, x, y);
1224 gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
1229 gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
1230 int cx, int cy, GnomeCanvasItem **actual_item)
1232 GnomeCanvasLine *line;
1233 double *line_points = NULL, *coords;
1234 double static_points[2 * NUM_STATIC_POINTS];
1239 int num_points = 0, i;
1240 int changed_miter_to_bevel;
1243 g_print ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
1246 line = GNOME_CANVAS_LINE (item);
1248 *actual_item = item;
1252 /* Handle smoothed lines by generating an expanded set ot points */
1254 if (line->smooth && (line->num_points > 2)) {
1257 num_points = line->num_points;
1258 line_points = line->coords;
1261 /* Compute a polygon for each edge of the line and test the point against it. The effective
1262 * width of the line is adjusted so that it will be at least one pixel thick (so that zero
1263 * pixel-wide lines can be pickedup as well).
1266 if (line->width_pixels)
1267 width = line->width / item->canvas->pixels_per_unit;
1269 width = line->width;
1271 if (width < (1.0 / item->canvas->pixels_per_unit))
1272 width = 1.0 / item->canvas->pixels_per_unit;
1274 changed_miter_to_bevel = 0;
1276 for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
1277 /* If rounding is done around the first point, then compute distance between the
1278 * point and the first point.
1281 if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
1282 || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
1285 dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1286 if (dist < GNOME_CANVAS_EPSILON) {
1289 } else if (dist < best)
1293 /* Compute the polygonal shape corresponding to this edge, with two points for the
1294 * first point of the edge and two points for the last point of the edge.
1297 if (i == num_points)
1298 gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1299 width, (line->cap == GDK_CAP_PROJECTING),
1300 poly, poly + 1, poly + 2, poly + 3);
1301 else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
1307 gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
1309 poly, poly + 1, poly + 2, poly + 3);
1311 /* If this line uses beveled joints, then check the distance to a polygon
1312 * comprising the last two points of the previous polygon and the first two
1313 * from this polygon; this checks the wedges that fill the mitered point.
1316 if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
1320 dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1321 if (dist < GNOME_CANVAS_EPSILON) {
1324 } else if (dist < best)
1327 changed_miter_to_bevel = FALSE;
1332 gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1333 width, (line->cap == GDK_CAP_PROJECTING),
1334 poly + 4, poly + 5, poly + 6, poly + 7);
1335 else if (line->join == GDK_JOIN_MITER) {
1336 if (!gnome_canvas_get_miter_points (coords[0], coords[1],
1337 coords[2], coords[3],
1338 coords[4], coords[5],
1340 poly + 4, poly + 5, poly + 6, poly + 7)) {
1341 changed_miter_to_bevel = TRUE;
1342 gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1344 poly + 4, poly + 5, poly + 6, poly + 7);
1347 gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
1349 poly + 4, poly + 5, poly + 6, poly + 7);
1354 dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
1355 if (dist < GNOME_CANVAS_EPSILON) {
1358 } else if (dist < best)
1362 /* If caps are rounded, check the distance to the cap around the final end point of the line */
1364 if (line->cap == GDK_CAP_ROUND) {
1367 dist = sqrt (dx * dx + dy * dy) - width / 2.0;
1368 if (dist < GNOME_CANVAS_EPSILON) {
1375 /* sometimes the GnomeCanvasItem::update signal will not have
1376 been processed between deleting the arrow points and a call
1377 to this routine -- this can cause a segfault here */
1378 if ((line->first_arrow && !line->first_coords) ||
1379 (line->last_arrow && !line->last_coords))
1380 reconfigure_arrows(line);
1382 /* If there are arrowheads, check the distance to them */
1384 if (line->first_arrow) {
1385 dist = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
1386 if (dist < GNOME_CANVAS_EPSILON) {
1393 if (line->last_arrow) {
1394 dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
1395 if (dist < GNOME_CANVAS_EPSILON) {
1404 if ((line_points != static_points) && (line_points != line->coords))
1405 g_free (line_points);
1411 gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1413 GnomeCanvasLine *line;
1415 line = GNOME_CANVAS_LINE (item);
1417 if (line->num_points == 0) {
1418 *x1 = *y1 = *x2 = *y2 = 0.0;
1422 get_bounds (line, x1, y1, x2, y2);