2
* GooCanvas. Copyright (C) 2005 Damon Chaplin.
3
* Released under the GNU LGPL license. See COPYING for details.
5
* goocanvaspolyline.c - polyline item, with optional arrows.
9
* SECTION:goocanvaspolyline
10
* @Title: GooCanvasPolyline
11
* @Short_Description: a polyline item (a series of lines with optional arrows).
13
* GooCanvasPolyline represents a polyline item, which is a series of one or
14
* more lines, with optional arrows at either end.
16
* It is a subclass of #GooCanvasItemSimple and so inherits all of the style
17
* properties such as "stroke-color", "fill-color" and "line-width".
19
* It also implements the #GooCanvasItem interface, so you can use the
20
* #GooCanvasItem functions such as goo_canvas_item_raise() and
21
* goo_canvas_item_rotate().
23
* To create a #GooCanvasPolyline use goo_canvas_polyline_new(), or
24
* goo_canvas_polyline_new_line() for a simple line between two points.
26
* To get or set the properties of an existing #GooCanvasPolyline, use
27
* g_object_get() and g_object_set().
33
#include <glib/gi18n-lib.h>
35
#include "goocanvaspolyline.h"
36
#include "goocanvas.h"
40
* goo_canvas_points_new:
41
* @num_points: the number of points to create space for.
43
* Creates a new #GooCanvasPoints struct with space for the given number of
44
* points. It should be freed with goo_canvas_points_unref().
46
* Returns: a new #GooCanvasPoints struct.
49
goo_canvas_points_new (int num_points)
51
GooCanvasPoints *points;
53
points = g_slice_new (GooCanvasPoints);
54
points->num_points = num_points;
55
points->coords = g_slice_alloc (num_points * 2 * sizeof (double));
56
points->ref_count = 1;
63
* goo_canvas_points_ref:
64
* @points: a #GooCanvasPoints struct.
66
* Increments the reference count of the given #GooCanvasPoints struct.
68
* Returns: the #GooCanvasPoints struct.
71
goo_canvas_points_ref (GooCanvasPoints *points)
79
* goo_canvas_points_unref:
80
* @points: a #GooCanvasPoints struct.
82
* Decrements the reference count of the given #GooCanvasPoints struct,
83
* freeing it if the reference count falls to zero.
86
goo_canvas_points_unref (GooCanvasPoints *points)
88
if (--points->ref_count == 0)
90
g_slice_free1 (points->num_points * 2 * sizeof (double), points->coords);
91
g_slice_free (GooCanvasPoints, points);
97
goo_canvas_points_get_type (void)
99
static GType type_canvas_points = 0;
101
if (!type_canvas_points)
102
type_canvas_points = g_boxed_type_register_static
104
(GBoxedCopyFunc) goo_canvas_points_ref,
105
(GBoxedFreeFunc) goo_canvas_points_unref);
107
return type_canvas_points;
120
PROP_ARROW_TIP_LENGTH,
129
static void canvas_item_interface_init (GooCanvasItemIface *iface);
131
G_DEFINE_TYPE_WITH_CODE (GooCanvasPolyline, goo_canvas_polyline,
132
GOO_TYPE_CANVAS_ITEM_SIMPLE,
133
G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
134
canvas_item_interface_init))
138
goo_canvas_polyline_install_common_properties (GObjectClass *gobject_class)
140
g_object_class_install_property (gobject_class, PROP_POINTS,
141
g_param_spec_boxed ("points",
143
_("The array of points"),
144
GOO_TYPE_CANVAS_POINTS,
147
g_object_class_install_property (gobject_class, PROP_CLOSE_PATH,
148
g_param_spec_boolean ("close-path",
150
_("If the last point should be connected to the first"),
154
g_object_class_install_property (gobject_class, PROP_START_ARROW,
155
g_param_spec_boolean ("start-arrow",
157
_("If an arrow should be displayed at the start of the polyline"),
161
g_object_class_install_property (gobject_class, PROP_END_ARROW,
162
g_param_spec_boolean ("end-arrow",
164
_("If an arrow should be displayed at the end of the polyline"),
168
g_object_class_install_property (gobject_class, PROP_ARROW_LENGTH,
169
g_param_spec_double ("arrow-length",
171
_("The length of the arrows, as a multiple of the line width"),
172
0.0, G_MAXDOUBLE, 5.0,
175
g_object_class_install_property (gobject_class, PROP_ARROW_WIDTH,
176
g_param_spec_double ("arrow-width",
178
_("The width of the arrows, as a multiple of the line width"),
179
0.0, G_MAXDOUBLE, 4.0,
182
g_object_class_install_property (gobject_class, PROP_ARROW_TIP_LENGTH,
183
g_param_spec_double ("arrow-tip-length",
184
_("Arrow Tip Length"),
185
_("The length of the arrow tip, as a multiple of the line width"),
186
0.0, G_MAXDOUBLE, 4.0,
189
g_object_class_install_property (gobject_class, PROP_X,
190
g_param_spec_double ("x",
192
_("The x coordinate of the left-most point of the polyline"),
197
g_object_class_install_property (gobject_class, PROP_Y,
198
g_param_spec_double ("y",
200
_("The y coordinate of the top-most point of the polyline"),
205
g_object_class_install_property (gobject_class, PROP_WIDTH,
206
g_param_spec_double ("width",
208
_("The width of the polyline"),
209
0.0, G_MAXDOUBLE, 0.0,
212
g_object_class_install_property (gobject_class, PROP_HEIGHT,
213
g_param_spec_double ("height",
215
_("The height of the polyline"),
216
0.0, G_MAXDOUBLE, 0.0,
222
goo_canvas_polyline_init (GooCanvasPolyline *polyline)
224
polyline->polyline_data = g_slice_new0 (GooCanvasPolylineData);
229
goo_canvas_polyline_finalize (GObject *object)
231
GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
232
GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
234
/* Free our data if we didn't have a model. (If we had a model it would
235
have been reset in dispose() and simple_data will be NULL.) */
236
if (simple->simple_data)
238
g_slice_free1 (polyline->polyline_data->num_points * 2 * sizeof (gdouble),
239
polyline->polyline_data->coords);
240
g_slice_free (GooCanvasPolylineArrowData, polyline->polyline_data->arrow_data);
241
g_slice_free (GooCanvasPolylineData, polyline->polyline_data);
243
polyline->polyline_data = NULL;
245
G_OBJECT_CLASS (goo_canvas_polyline_parent_class)->finalize (object);
250
goo_canvas_polyline_get_extent (GooCanvasPolylineData *polyline_data,
251
GooCanvasBounds *bounds)
255
if (polyline_data->num_points == 0)
257
bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.0;
261
bounds->x1 = bounds->x2 = polyline_data->coords[0];
262
bounds->y1 = bounds->y2 = polyline_data->coords[1];
264
for (i = 1; i < polyline_data->num_points; i++)
266
bounds->x1 = MIN (bounds->x1, polyline_data->coords[2 * i]);
267
bounds->y1 = MIN (bounds->y1, polyline_data->coords[2 * i + 1]);
268
bounds->x2 = MAX (bounds->x2, polyline_data->coords[2 * i]);
269
bounds->y2 = MAX (bounds->y2, polyline_data->coords[2 * i + 1]);
276
goo_canvas_polyline_get_common_property (GObject *object,
277
GooCanvasPolylineData *polyline_data,
282
GooCanvasPoints *points;
283
GooCanvasBounds extent;
288
if (polyline_data->num_points == 0)
290
g_value_set_boxed (value, NULL);
294
points = goo_canvas_points_new (polyline_data->num_points);
295
memcpy (points->coords, polyline_data->coords,
296
polyline_data->num_points * 2 * sizeof (double));
297
g_value_set_boxed (value, points);
298
goo_canvas_points_unref (points);
301
case PROP_CLOSE_PATH:
302
g_value_set_boolean (value, polyline_data->close_path);
304
case PROP_START_ARROW:
305
g_value_set_boolean (value, polyline_data->start_arrow);
308
g_value_set_boolean (value, polyline_data->end_arrow);
310
case PROP_ARROW_LENGTH:
311
g_value_set_double (value, polyline_data->arrow_data
312
? polyline_data->arrow_data->arrow_length : 5.0);
314
case PROP_ARROW_WIDTH:
315
g_value_set_double (value, polyline_data->arrow_data
316
? polyline_data->arrow_data->arrow_width : 4.0);
318
case PROP_ARROW_TIP_LENGTH:
319
g_value_set_double (value, polyline_data->arrow_data
320
? polyline_data->arrow_data->arrow_tip_length : 4.0);
323
goo_canvas_polyline_get_extent (polyline_data, &extent);
324
g_value_set_double (value, extent.x1);
327
goo_canvas_polyline_get_extent (polyline_data, &extent);
328
g_value_set_double (value, extent.y1);
331
goo_canvas_polyline_get_extent (polyline_data, &extent);
332
g_value_set_double (value, extent.x2 - extent.x1);
335
goo_canvas_polyline_get_extent (polyline_data, &extent);
336
g_value_set_double (value, extent.y2 - extent.y1);
339
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346
goo_canvas_polyline_get_property (GObject *object,
351
GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
353
goo_canvas_polyline_get_common_property (object, polyline->polyline_data,
354
prop_id, value, pspec);
359
ensure_arrow_data (GooCanvasPolylineData *polyline_data)
361
if (!polyline_data->arrow_data)
363
polyline_data->arrow_data = g_slice_new (GooCanvasPolylineArrowData);
365
/* These seem like reasonable defaults for the arrow dimensions.
366
They are specified in multiples of the line width so they scale OK. */
367
polyline_data->arrow_data->arrow_width = 4.0;
368
polyline_data->arrow_data->arrow_length = 5.0;
369
polyline_data->arrow_data->arrow_tip_length = 4.0;
373
#define GOO_CANVAS_EPSILON 1e-10
376
reconfigure_arrow (GooCanvasPolylineData *polyline_data,
380
gdouble *line_coords,
381
gdouble *arrow_coords)
383
GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
384
double dx, dy, length, sin_theta, cos_theta;
385
double half_arrow_width, arrow_length, arrow_tip_length;
386
double arrow_end_center_x, arrow_end_center_y;
387
double arrow_tip_center_x, arrow_tip_center_y;
388
double x_offset, y_offset, half_line_width, line_trim;
390
dx = polyline_data->coords[prev_point] - polyline_data->coords[end_point];
391
dy = polyline_data->coords[prev_point + 1] - polyline_data->coords[end_point + 1];
392
length = sqrt (dx * dx + dy * dy);
394
if (length < GOO_CANVAS_EPSILON)
396
/* The length is too short to reliably get the angle so just guess. */
402
/* Calculate a unit vector moving from the arrow point along the line. */
403
sin_theta = dy / length;
404
cos_theta = dx / length;
407
/* These are all specified in multiples of the line width, so convert. */
408
half_arrow_width = arrow->arrow_width * line_width / 2;
409
arrow_length = arrow->arrow_length * line_width;
410
arrow_tip_length = arrow->arrow_tip_length * line_width;
412
/* The arrow tip is at the end point of the line. */
413
arrow_coords[0] = polyline_data->coords[end_point];
414
arrow_coords[1] = polyline_data->coords[end_point + 1];
416
/* Calculate the end of the arrow, along the center line. */
417
arrow_end_center_x = arrow_coords[0] + (arrow_length * cos_theta);
418
arrow_end_center_y = arrow_coords[1] + (arrow_length * sin_theta);
420
/* Now calculate the 2 end points of the arrow either side of the line. */
421
x_offset = half_arrow_width * sin_theta;
422
y_offset = half_arrow_width * cos_theta;
424
arrow_coords[2] = arrow_end_center_x + x_offset;
425
arrow_coords[3] = arrow_end_center_y - y_offset;
427
arrow_coords[8] = arrow_end_center_x - x_offset;
428
arrow_coords[9] = arrow_end_center_y + y_offset;
430
/* Calculate the end of the arrow tip, along the center line. */
431
arrow_tip_center_x = arrow_coords[0] + (arrow_tip_length * cos_theta);
432
arrow_tip_center_y = arrow_coords[1] + (arrow_tip_length * sin_theta);
434
/* Now calculate the 2 arrow points on either edge of the line. */
435
half_line_width = line_width / 2.0;
436
x_offset = half_line_width * sin_theta;
437
y_offset = half_line_width * cos_theta;
439
arrow_coords[4] = arrow_tip_center_x + x_offset;
440
arrow_coords[5] = arrow_tip_center_y - y_offset;
442
arrow_coords[6] = arrow_tip_center_x - x_offset;
443
arrow_coords[7] = arrow_tip_center_y + y_offset;
445
/* Calculate the new end of the line. We have to move it back slightly so
446
it doesn't draw over the arrow tip. But we overlap the arrow by a small
447
fraction of the line width to avoid a tiny gap. */
448
line_trim = arrow_tip_length - (line_width / 10.0);
449
line_coords[0] = arrow_coords[0] + (line_trim * cos_theta);
450
line_coords[1] = arrow_coords[1] + (line_trim * sin_theta);
454
/* Recalculates the arrow polygons for the line */
456
goo_canvas_polyline_reconfigure_arrows (GooCanvasPolyline *polyline)
458
GooCanvasItemSimple *simple = (GooCanvasItemSimple*) polyline;
459
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
462
if (!polyline_data->reconfigure_arrows)
465
polyline_data->reconfigure_arrows = FALSE;
467
if (polyline_data->num_points < 2
468
|| (!polyline_data->start_arrow && !polyline_data->end_arrow))
471
line_width = goo_canvas_item_simple_get_line_width (simple);
472
ensure_arrow_data (polyline_data);
474
if (polyline_data->start_arrow)
475
reconfigure_arrow (polyline_data, 0, 2, line_width,
476
polyline_data->arrow_data->line_start,
477
polyline_data->arrow_data->start_arrow_coords);
479
if (polyline_data->end_arrow)
481
int end_point, prev_point;
483
if (polyline_data->close_path)
486
prev_point = polyline_data->num_points - 1;
490
end_point = polyline_data->num_points - 1;
491
prev_point = polyline_data->num_points - 2;
494
reconfigure_arrow (polyline_data, end_point * 2, prev_point * 2,
495
line_width, polyline_data->arrow_data->line_end,
496
polyline_data->arrow_data->end_arrow_coords);
502
goo_canvas_polyline_set_common_property (GObject *object,
503
GooCanvasPolylineData *polyline_data,
508
GooCanvasPoints *points;
509
GooCanvasBounds extent;
510
gdouble x_offset, y_offset, x_scale, y_scale;
516
points = g_value_get_boxed (value);
518
if (polyline_data->coords)
520
g_slice_free1 (polyline_data->num_points * 2 * sizeof (double), polyline_data->coords);
521
polyline_data->coords = NULL;
526
polyline_data->num_points = 0;
530
polyline_data->num_points = points->num_points;
531
polyline_data->coords = g_slice_alloc (polyline_data->num_points * 2 * sizeof (double));
532
memcpy (polyline_data->coords, points->coords,
533
polyline_data->num_points * 2 * sizeof (double));
535
polyline_data->reconfigure_arrows = TRUE;
536
g_object_notify (object, "x");
537
g_object_notify (object, "y");
538
g_object_notify (object, "width");
539
g_object_notify (object, "height");
541
case PROP_CLOSE_PATH:
542
polyline_data->close_path = g_value_get_boolean (value);
543
polyline_data->reconfigure_arrows = TRUE;
545
case PROP_START_ARROW:
546
polyline_data->start_arrow = g_value_get_boolean (value);
547
polyline_data->reconfigure_arrows = TRUE;
550
polyline_data->end_arrow = g_value_get_boolean (value);
551
polyline_data->reconfigure_arrows = TRUE;
553
case PROP_ARROW_LENGTH:
554
ensure_arrow_data (polyline_data);
555
polyline_data->arrow_data->arrow_length = g_value_get_double (value);
556
polyline_data->reconfigure_arrows = TRUE;
558
case PROP_ARROW_WIDTH:
559
ensure_arrow_data (polyline_data);
560
polyline_data->arrow_data->arrow_width = g_value_get_double (value);
561
polyline_data->reconfigure_arrows = TRUE;
563
case PROP_ARROW_TIP_LENGTH:
564
ensure_arrow_data (polyline_data);
565
polyline_data->arrow_data->arrow_tip_length = g_value_get_double (value);
566
polyline_data->reconfigure_arrows = TRUE;
569
if (polyline_data->num_points > 0)
571
/* Calculate the x offset from the current position. */
572
goo_canvas_polyline_get_extent (polyline_data, &extent);
573
x_offset = g_value_get_double (value) - extent.x1;
575
/* Add the offset to all the x coordinates. */
576
for (i = 0; i < polyline_data->num_points; i++)
577
polyline_data->coords[2 * i] += x_offset;
579
g_object_notify (object, "points");
583
if (polyline_data->num_points > 0)
585
/* Calculate the y offset from the current position. */
586
goo_canvas_polyline_get_extent (polyline_data, &extent);
587
y_offset = g_value_get_double (value) - extent.y1;
589
/* Add the offset to all the y coordinates. */
590
for (i = 0; i < polyline_data->num_points; i++)
591
polyline_data->coords[2 * i + 1] += y_offset;
593
g_object_notify (object, "points");
597
if (polyline_data->num_points >= 2)
599
goo_canvas_polyline_get_extent (polyline_data, &extent);
600
if (extent.x2 - extent.x1 != 0.0)
602
/* Calculate the amount to scale the polyline. */
603
x_scale = g_value_get_double (value) / (extent.x2 - extent.x1);
605
/* Scale the x coordinates, relative to the left-most point. */
606
for (i = 0; i < polyline_data->num_points; i++)
607
polyline_data->coords[2 * i] = extent.x1 + (polyline_data->coords[2 * i] - extent.x1) * x_scale;
609
g_object_notify (object, "points");
614
if (polyline_data->num_points >= 2)
616
goo_canvas_polyline_get_extent (polyline_data, &extent);
617
if (extent.y2 - extent.y1 != 0.0)
619
/* Calculate the amount to scale the polyline. */
620
y_scale = g_value_get_double (value) / (extent.y2 - extent.y1);
622
/* Scale the y coordinates, relative to the top-most point. */
623
for (i = 0; i < polyline_data->num_points; i++)
624
polyline_data->coords[2 * i + 1] = extent.y1 + (polyline_data->coords[2 * i + 1] - extent.y1) * y_scale;
626
g_object_notify (object, "points");
631
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
638
goo_canvas_polyline_set_property (GObject *object,
643
GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
644
GooCanvasPolyline *polyline = (GooCanvasPolyline*) object;
648
g_warning ("Can't set property of a canvas item with a model - set the model property instead");
652
goo_canvas_polyline_set_common_property (object, polyline->polyline_data,
653
prop_id, value, pspec);
654
goo_canvas_item_simple_changed (simple, TRUE);
659
* goo_canvas_polyline_new:
660
* @parent: the parent item, or %NULL. If a parent is specified, it will assume
661
* ownership of the item, and the item will automatically be freed when it is
662
* removed from the parent. Otherwise call g_object_unref() to free it.
663
* @close_path: if the last point should be connected to the first.
664
* @num_points: the number of points in the polyline.
665
* @...: the pairs of coordinates for each point in the line, followed by
666
* optional pairs of property names and values, and a terminating %NULL.
668
* Creates a new polyline item.
672
* Here's an example showing how to create a filled triangle with vertices
673
* at (100,100), (300,100), and (200,300).
675
* <informalexample><programlisting>
676
* GooCanvasItem *polyline = goo_canvas_polyline_new (mygroup, TRUE, 3,
680
* "stroke-color", "red",
682
* "fill-color", "blue",
684
* </programlisting></informalexample>
686
* Returns: a new polyline item.
689
goo_canvas_polyline_new (GooCanvasItem *parent,
695
GooCanvasPolyline *polyline;
696
GooCanvasPolylineData *polyline_data;
697
const char *first_property;
701
item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
702
polyline = (GooCanvasPolyline*) item;
704
polyline_data = polyline->polyline_data;
705
polyline_data->close_path = close_path;
706
polyline_data->num_points = num_points;
708
polyline_data->coords = g_slice_alloc (num_points * 2 * sizeof (gdouble));
710
va_start (var_args, num_points);
711
for (i = 0; i < num_points * 2; i++)
712
polyline_data->coords[i] = va_arg (var_args, gdouble);
714
first_property = va_arg (var_args, char*);
716
g_object_set_valist ((GObject*) item, first_property, var_args);
721
goo_canvas_item_add_child (parent, item, -1);
722
g_object_unref (item);
730
* goo_canvas_polyline_new_line:
731
* @parent: the parent item, or %NULL.
732
* @x1: the x coordinate of the start of the line.
733
* @y1: the y coordinate of the start of the line.
734
* @x2: the x coordinate of the end of the line.
735
* @y2: the y coordinate of the end of the line.
736
* @...: optional pairs of property names and values, and a terminating %NULL.
738
* Creates a new polyline item with a single line.
742
* Here's an example showing how to create a line from (100,100) to (300,300).
744
* <informalexample><programlisting>
745
* GooCanvasItem *polyline = goo_canvas_polyline_new_line (mygroup,
748
* "stroke-color", "red",
751
* </programlisting></informalexample>
753
* Returns: a new polyline item.
756
goo_canvas_polyline_new_line (GooCanvasItem *parent,
764
GooCanvasPolyline *polyline;
765
GooCanvasPolylineData *polyline_data;
766
const char *first_property;
769
item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
770
polyline = (GooCanvasPolyline*) item;
772
polyline_data = polyline->polyline_data;
773
polyline_data->close_path = FALSE;
774
polyline_data->num_points = 2;
775
polyline_data->coords = g_slice_alloc (4 * sizeof (gdouble));
776
polyline_data->coords[0] = x1;
777
polyline_data->coords[1] = y1;
778
polyline_data->coords[2] = x2;
779
polyline_data->coords[3] = y2;
781
va_start (var_args, y2);
782
first_property = va_arg (var_args, char*);
784
g_object_set_valist ((GObject*) item, first_property, var_args);
789
goo_canvas_item_add_child (parent, item, -1);
790
g_object_unref (item);
798
goo_canvas_polyline_create_path (GooCanvasPolyline *polyline,
801
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
802
GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
807
if (polyline_data->num_points == 0)
810
/* If there is an arrow at the start of the polyline, we need to move the
811
start of the line slightly to avoid drawing over the arrow tip. */
812
if (polyline_data->start_arrow && polyline_data->num_points >= 2)
813
cairo_move_to (cr, arrow->line_start[0], arrow->line_start[1]);
815
cairo_move_to (cr, polyline_data->coords[0], polyline_data->coords[1]);
817
if (polyline_data->end_arrow && polyline_data->num_points >= 2)
819
gint last_point = polyline_data->num_points - 1;
821
if (!polyline_data->close_path)
824
for (i = 1; i <= last_point; i++)
825
cairo_line_to (cr, polyline_data->coords[i * 2],
826
polyline_data->coords[i * 2 + 1]);
828
cairo_line_to (cr, arrow->line_end[0], arrow->line_end[1]);
832
for (i = 1; i < polyline_data->num_points; i++)
833
cairo_line_to (cr, polyline_data->coords[i * 2],
834
polyline_data->coords[i * 2 + 1]);
836
if (polyline_data->close_path)
837
cairo_close_path (cr);
843
goo_canvas_polyline_create_start_arrow_path (GooCanvasPolyline *polyline,
846
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
847
GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
852
if (polyline_data->num_points < 2)
855
cairo_move_to (cr, arrow->start_arrow_coords[0],
856
arrow->start_arrow_coords[1]);
857
for (i = 1; i < NUM_ARROW_POINTS; i++)
859
cairo_line_to (cr, arrow->start_arrow_coords[i * 2],
860
arrow->start_arrow_coords[i * 2 + 1]);
862
cairo_close_path (cr);
867
goo_canvas_polyline_create_end_arrow_path (GooCanvasPolyline *polyline,
870
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
871
GooCanvasPolylineArrowData *arrow = polyline_data->arrow_data;
876
if (polyline_data->num_points < 2)
879
cairo_move_to (cr, arrow->end_arrow_coords[0],
880
arrow->end_arrow_coords[1]);
881
for (i = 1; i < NUM_ARROW_POINTS; i++)
883
cairo_line_to (cr, arrow->end_arrow_coords[i * 2],
884
arrow->end_arrow_coords[i * 2 + 1]);
886
cairo_close_path (cr);
891
goo_canvas_polyline_is_item_at (GooCanvasItemSimple *simple,
895
gboolean is_pointer_event)
897
GooCanvasItemSimpleData *simple_data = simple->simple_data;
898
GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
899
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
900
GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL;
903
if (polyline_data->num_points == 0)
906
/* Check if the item should receive events. */
907
if (is_pointer_event)
908
pointer_events = simple_data->pointer_events;
910
/* If the path isn't closed, we never check the fill. */
911
if (!(polyline_data->close_path && polyline_data->num_points > 2))
912
pointer_events &= ~GOO_CANVAS_EVENTS_FILL_MASK;
914
goo_canvas_polyline_create_path (polyline, cr);
915
if (goo_canvas_item_simple_check_in_path (simple, x, y, cr, pointer_events))
918
/* Check the arrows, if the polyline has them. */
919
if ((polyline_data->start_arrow || polyline_data->end_arrow)
920
&& polyline_data->num_points >= 2
921
&& (pointer_events & GOO_CANVAS_EVENTS_STROKE_MASK))
923
/* We use the stroke pattern to match the style of the line. */
924
do_stroke = goo_canvas_style_set_stroke_options (simple_data->style, cr);
925
if (!(pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK) || do_stroke)
927
if (polyline_data->start_arrow)
929
goo_canvas_polyline_create_start_arrow_path (polyline, cr);
930
if (cairo_in_fill (cr, x, y))
934
if (polyline_data->end_arrow)
936
goo_canvas_polyline_create_end_arrow_path (polyline, cr);
937
if (cairo_in_fill (cr, x, y))
948
goo_canvas_polyline_compute_bounds (GooCanvasPolyline *polyline,
950
GooCanvasBounds *bounds)
952
GooCanvasItemSimple *simple = (GooCanvasItemSimple*) polyline;
953
GooCanvasItemSimpleData *simple_data = simple->simple_data;
954
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
955
GooCanvasBounds tmp_bounds;
956
cairo_matrix_t transform;
958
if (polyline_data->num_points == 0)
960
bounds->x1 = bounds->x2 = bounds->y1 = bounds->y2 = 0.0;
964
/* Use the identity matrix to get the bounds completely in user space. */
965
cairo_get_matrix (cr, &transform);
966
cairo_identity_matrix (cr);
968
goo_canvas_polyline_create_path (polyline, cr);
969
goo_canvas_item_simple_get_path_bounds (simple, cr, bounds);
971
/* Add on the arrows, if required. */
972
if ((polyline_data->start_arrow || polyline_data->end_arrow)
973
&& polyline_data->num_points >= 2)
975
/* We use the stroke pattern to match the style of the line. */
976
goo_canvas_style_set_stroke_options (simple_data->style, cr);
978
if (polyline_data->start_arrow)
980
goo_canvas_polyline_create_start_arrow_path (polyline, cr);
981
cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1,
982
&tmp_bounds.x2, &tmp_bounds.y2);
983
bounds->x1 = MIN (bounds->x1, tmp_bounds.x1);
984
bounds->y1 = MIN (bounds->y1, tmp_bounds.y1);
985
bounds->x2 = MAX (bounds->x2, tmp_bounds.x2);
986
bounds->y2 = MAX (bounds->y2, tmp_bounds.y2);
989
if (polyline_data->end_arrow)
991
goo_canvas_polyline_create_end_arrow_path (polyline, cr);
992
cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1,
993
&tmp_bounds.x2, &tmp_bounds.y2);
994
bounds->x1 = MIN (bounds->x1, tmp_bounds.x1);
995
bounds->y1 = MIN (bounds->y1, tmp_bounds.y1);
996
bounds->x2 = MAX (bounds->x2, tmp_bounds.x2);
997
bounds->y2 = MAX (bounds->y2, tmp_bounds.y2);
1001
cairo_set_matrix (cr, &transform);
1006
goo_canvas_polyline_update (GooCanvasItemSimple *simple,
1009
GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
1010
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
1012
if (polyline_data->reconfigure_arrows)
1013
goo_canvas_polyline_reconfigure_arrows (polyline);
1015
/* Compute the new bounds. */
1016
goo_canvas_polyline_compute_bounds (polyline, cr, &simple->bounds);
1021
goo_canvas_polyline_paint (GooCanvasItemSimple *simple,
1023
const GooCanvasBounds *bounds)
1025
GooCanvasItemSimpleData *simple_data = simple->simple_data;
1026
GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple;
1027
GooCanvasPolylineData *polyline_data = polyline->polyline_data;
1029
if (polyline_data->num_points == 0)
1032
goo_canvas_polyline_create_path (polyline, cr);
1033
goo_canvas_item_simple_paint_path (simple, cr);
1035
/* Paint the arrows, if required. */
1036
if ((polyline_data->start_arrow || polyline_data->end_arrow)
1037
&& polyline_data->num_points >= 2)
1039
/* We use the stroke pattern to match the style of the line. */
1040
goo_canvas_style_set_stroke_options (simple_data->style, cr);
1042
if (polyline_data->start_arrow)
1044
goo_canvas_polyline_create_start_arrow_path (polyline, cr);
1048
if (polyline_data->end_arrow)
1050
goo_canvas_polyline_create_end_arrow_path (polyline, cr);
1058
goo_canvas_polyline_set_model (GooCanvasItem *item,
1059
GooCanvasItemModel *model)
1061
GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
1062
GooCanvasPolyline *polyline = (GooCanvasPolyline*) item;
1063
GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) model;
1065
/* If our polyline_data was allocated, free it. */
1067
g_slice_free (GooCanvasPolylineData, polyline->polyline_data);
1069
/* Now use the new model's polyline_data instead. */
1070
polyline->polyline_data = &pmodel->polyline_data;
1072
/* Let the parent GooCanvasItemSimple code do the rest. */
1073
goo_canvas_item_simple_set_model (simple, model);
1078
canvas_item_interface_init (GooCanvasItemIface *iface)
1080
iface->set_model = goo_canvas_polyline_set_model;
1085
goo_canvas_polyline_class_init (GooCanvasPolylineClass *klass)
1087
GObjectClass *gobject_class = (GObjectClass*) klass;
1088
GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
1090
gobject_class->finalize = goo_canvas_polyline_finalize;
1092
gobject_class->get_property = goo_canvas_polyline_get_property;
1093
gobject_class->set_property = goo_canvas_polyline_set_property;
1095
simple_class->simple_update = goo_canvas_polyline_update;
1096
simple_class->simple_paint = goo_canvas_polyline_paint;
1097
simple_class->simple_is_item_at = goo_canvas_polyline_is_item_at;
1099
goo_canvas_polyline_install_common_properties (gobject_class);
1104
* SECTION:goocanvaspolylinemodel
1105
* @Title: GooCanvasPolylineModel
1106
* @Short_Description: a model for polyline items (a series of lines with
1109
* GooCanvasPolylineModel represents a model for polyline items, which are a
1110
* series of one or more lines, with optional arrows at either end.
1112
* It is a subclass of #GooCanvasItemModelSimple and so inherits all of the
1113
* style properties such as "stroke-color", "fill-color" and "line-width".
1115
* It also implements the #GooCanvasItemModel interface, so you can use the
1116
* #GooCanvasItemModel functions such as goo_canvas_item_model_raise() and
1117
* goo_canvas_item_model_rotate().
1119
* To create a #GooCanvasPolylineModel use goo_canvas_polyline_model_new(), or
1120
* goo_canvas_polyline_model_new_line() for a simple line between two points.
1122
* To get or set the properties of an existing #GooCanvasPolylineModel, use
1123
* g_object_get() and g_object_set().
1125
* To respond to events such as mouse clicks on the polyline you must connect
1126
* to the signal handlers of the corresponding #GooCanvasPolyline objects.
1127
* (See goo_canvas_get_item() and #GooCanvas::item-created.)
1130
static void item_model_interface_init (GooCanvasItemModelIface *iface);
1131
static void goo_canvas_polyline_model_finalize (GObject *object);
1132
static void goo_canvas_polyline_model_get_property (GObject *object,
1136
static void goo_canvas_polyline_model_set_property (GObject *object,
1138
const GValue *value,
1141
G_DEFINE_TYPE_WITH_CODE (GooCanvasPolylineModel, goo_canvas_polyline_model,
1142
GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE,
1143
G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_MODEL,
1144
item_model_interface_init))
1148
goo_canvas_polyline_model_class_init (GooCanvasPolylineModelClass *klass)
1150
GObjectClass *gobject_class = (GObjectClass*) klass;
1152
gobject_class->finalize = goo_canvas_polyline_model_finalize;
1154
gobject_class->get_property = goo_canvas_polyline_model_get_property;
1155
gobject_class->set_property = goo_canvas_polyline_model_set_property;
1157
goo_canvas_polyline_install_common_properties (gobject_class);
1162
goo_canvas_polyline_model_init (GooCanvasPolylineModel *pmodel)
1169
* goo_canvas_polyline_model_new:
1170
* @parent: the parent model, or %NULL. If a parent is specified, it will
1171
* assume ownership of the item, and the item will automatically be freed when
1172
* it is removed from the parent. Otherwise call g_object_unref() to free it.
1173
* @close_path: if the last point should be connected to the first.
1174
* @num_points: the number of points in the polyline.
1175
* @...: the pairs of coordinates for each point in the line, followed by
1176
* optional pairs of property names and values, and a terminating %NULL.
1178
* Creates a new polyline model.
1182
* Here's an example showing how to create a filled triangle with vertices
1183
* at (100,100), (300,100), and (200,300).
1185
* <informalexample><programlisting>
1186
* GooCanvasItemModel *polyline = goo_canvas_polyline_model_new (mygroup, TRUE, 3,
1190
* "stroke-color", "red",
1191
* "line-width", 5.0,
1192
* "fill-color", "blue",
1194
* </programlisting></informalexample>
1196
* Returns: a new polyline model.
1199
goo_canvas_polyline_model_new (GooCanvasItemModel *parent,
1200
gboolean close_path,
1204
GooCanvasItemModel *model;
1205
GooCanvasPolylineModel *pmodel;
1206
GooCanvasPolylineData *polyline_data;
1207
const char *first_property;
1211
model = g_object_new (GOO_TYPE_CANVAS_POLYLINE_MODEL, NULL);
1212
pmodel = (GooCanvasPolylineModel*) model;
1214
polyline_data = &pmodel->polyline_data;
1215
polyline_data->close_path = close_path;
1216
polyline_data->num_points = num_points;
1218
polyline_data->coords = g_slice_alloc (num_points * 2 * sizeof (gdouble));
1220
va_start (var_args, num_points);
1221
for (i = 0; i < num_points * 2; i++)
1222
polyline_data->coords[i] = va_arg (var_args, gdouble);
1224
first_property = va_arg (var_args, char*);
1226
g_object_set_valist ((GObject*) model, first_property, var_args);
1231
goo_canvas_item_model_add_child (parent, model, -1);
1232
g_object_unref (model);
1240
* goo_canvas_polyline_model_new_line:
1241
* @parent: the parent model, or %NULL.
1242
* @x1: the x coordinate of the start of the line.
1243
* @y1: the y coordinate of the start of the line.
1244
* @x2: the x coordinate of the end of the line.
1245
* @y2: the y coordinate of the end of the line.
1246
* @...: optional pairs of property names and values, and a terminating %NULL.
1248
* Creates a new polyline model with a single line.
1252
* Here's an example showing how to create a line from (100,100) to (300,300).
1254
* <informalexample><programlisting>
1255
* GooCanvasItemModel *polyline = goo_canvas_polyline_model_new_line (mygroup,
1258
* "stroke-color", "red",
1259
* "line-width", 5.0,
1261
* </programlisting></informalexample>
1263
* Returns: a new polyline model.
1266
goo_canvas_polyline_model_new_line (GooCanvasItemModel *parent,
1273
GooCanvasItemModel *model;
1274
GooCanvasPolylineModel *pmodel;
1275
GooCanvasPolylineData *polyline_data;
1276
const char *first_property;
1279
model = g_object_new (GOO_TYPE_CANVAS_POLYLINE_MODEL, NULL);
1280
pmodel = (GooCanvasPolylineModel*) model;
1282
polyline_data = &pmodel->polyline_data;
1283
polyline_data->close_path = FALSE;
1284
polyline_data->num_points = 2;
1285
polyline_data->coords = g_slice_alloc (4 * sizeof (gdouble));
1286
polyline_data->coords[0] = x1;
1287
polyline_data->coords[1] = y1;
1288
polyline_data->coords[2] = x2;
1289
polyline_data->coords[3] = y2;
1291
va_start (var_args, y2);
1292
first_property = va_arg (var_args, char*);
1294
g_object_set_valist ((GObject*) model, first_property, var_args);
1299
goo_canvas_item_model_add_child (parent, model, -1);
1300
g_object_unref (model);
1308
goo_canvas_polyline_model_finalize (GObject *object)
1310
GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
1312
g_slice_free1 (pmodel->polyline_data.num_points * 2 * sizeof (gdouble),
1313
pmodel->polyline_data.coords);
1314
g_slice_free (GooCanvasPolylineArrowData, pmodel->polyline_data.arrow_data);
1316
G_OBJECT_CLASS (goo_canvas_polyline_model_parent_class)->finalize (object);
1321
goo_canvas_polyline_model_get_property (GObject *object,
1326
GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
1328
goo_canvas_polyline_get_common_property (object, &pmodel->polyline_data,
1329
prop_id, value, pspec);
1334
goo_canvas_polyline_model_set_property (GObject *object,
1336
const GValue *value,
1339
GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) object;
1341
goo_canvas_polyline_set_common_property (object, &pmodel->polyline_data,
1342
prop_id, value, pspec);
1343
g_signal_emit_by_name (pmodel, "changed", TRUE);
1347
static GooCanvasItem*
1348
goo_canvas_polyline_model_create_item (GooCanvasItemModel *model,
1351
GooCanvasItem *item;
1353
item = g_object_new (GOO_TYPE_CANVAS_POLYLINE, NULL);
1354
goo_canvas_item_set_model (item, model);
1361
item_model_interface_init (GooCanvasItemModelIface *iface)
1363
iface->create_item = goo_canvas_polyline_model_create_item;