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 | |
---|
66 | enum { |
---|
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 | |
---|
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, |
---|
92 | guint param_id, |
---|
93 | const GValue *value, |
---|
94 | GParamSpec *pspec); |
---|
95 | static void gnome_canvas_line_get_property (GObject *object, |
---|
96 | guint param_id, |
---|
97 | GValue *value, |
---|
98 | GParamSpec *pspec); |
---|
99 | |
---|
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); |
---|
109 | |
---|
110 | |
---|
111 | static GnomeCanvasItemClass *parent_class; |
---|
112 | |
---|
113 | |
---|
114 | GType |
---|
115 | gnome_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 | |
---|
140 | static void |
---|
141 | gnome_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 | |
---|
274 | static void |
---|
275 | gnome_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 | |
---|
287 | static void |
---|
288 | gnome_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 | */ |
---|
334 | static void |
---|
335 | get_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 | */ |
---|
406 | static void |
---|
407 | get_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 */ |
---|
429 | static void |
---|
430 | reconfigure_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 */ |
---|
591 | static void |
---|
592 | set_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 */ |
---|
604 | static void |
---|
605 | set_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 */ |
---|
625 | static void |
---|
626 | set_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 | |
---|
644 | static void |
---|
645 | gnome_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 | */ |
---|
833 | static GnomeCanvasPoints * |
---|
834 | get_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 | |
---|
871 | static void |
---|
872 | gnome_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 | |
---|
969 | static void |
---|
970 | gnome_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 | |
---|
988 | static ArtSVP * |
---|
989 | svp_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 | |
---|
1022 | static void |
---|
1023 | gnome_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 | |
---|
1099 | static void |
---|
1100 | gnome_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 | |
---|
1116 | static void |
---|
1117 | gnome_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 | |
---|
1130 | static void |
---|
1131 | item_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 | |
---|
1178 | static void |
---|
1179 | gnome_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 | |
---|
1231 | static double |
---|
1232 | gnome_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 | |
---|
1405 | done: |
---|
1406 | |
---|
1407 | if ((line_points != static_points) && (line_points != line->coords)) |
---|
1408 | g_free (line_points); |
---|
1409 | |
---|
1410 | return best; |
---|
1411 | } |
---|
1412 | |
---|
1413 | static void |
---|
1414 | gnome_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 | } |
---|