1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 * @title: AdgArrowStyle
24 * @short_description: Arrow rendering related stuff
26 * Contains parameters on how to draw arrows, providing a way to register a
27 * custom rendering callback.
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
38 #include "adg-arrow-style.h"
39 #include "adg-arrow-style-private.h"
40 #include "adg-context.h"
55 static void get_property (GObject
*object
,
59 static void set_property (GObject
*object
,
63 static GPtrArray
* get_pool (void);
64 static void arrow_renderer (AdgArrowStyle
*arrow_style
,
66 CpmlSegment
*segment
);
67 static void triangle_renderer (AdgArrowStyle
*arrow_style
,
69 CpmlSegment
*segment
);
70 static void dot_renderer (AdgArrowStyle
*arrow_style
,
72 CpmlSegment
*segment
);
73 static void circle_renderer (AdgArrowStyle
*arrow_style
,
75 CpmlSegment
*segment
);
76 static void block_renderer (AdgArrowStyle
*arrow_style
,
78 CpmlSegment
*segment
);
79 static void square_renderer (AdgArrowStyle
*arrow_style
,
81 CpmlSegment
*segment
);
82 static void tick_renderer (AdgArrowStyle
*arrow_style
,
84 CpmlSegment
*segment
);
85 static void draw_triangle (cairo_t
*cr
,
86 AdgArrowStyle
*arrow_style
,
87 CpmlSegment
*segment
);
88 static void draw_circle (cairo_t
*cr
,
89 AdgArrowStyle
*arrow_style
,
90 CpmlSegment
*segment
);
93 G_DEFINE_TYPE(AdgArrowStyle
, adg_arrow_style
, ADG_TYPE_STYLE
)
97 adg_arrow_style_class_init(AdgArrowStyleClass
*klass
)
99 GObjectClass
*gobject_class
;
100 AdgStyleClass
*style_class
;
103 gobject_class
= (GObjectClass
*) klass
;
104 style_class
= (AdgStyleClass
*) klass
;
106 g_type_class_add_private(klass
, sizeof(AdgArrowStylePrivate
));
108 gobject_class
->get_property
= get_property
;
109 gobject_class
->set_property
= set_property
;
111 style_class
->get_pool
= get_pool
;
113 param
= g_param_spec_double("size",
116 ("The size of the arrow, a renderer dependent parameter"),
117 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
119 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
121 param
= g_param_spec_double("angle",
124 ("The angle of the arrow, a renderer dependent parameter"),
125 -G_MAXDOUBLE
, G_MAXDOUBLE
, G_PI
/ 6.,
127 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
129 param
= g_param_spec_double("margin",
132 ("The margin of the arrow, a renderer dependent parameter"),
133 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
135 g_object_class_install_property(gobject_class
, PROP_MARGIN
, param
);
137 param
= g_param_spec_pointer("renderer",
138 P_("Renderer Callback"),
140 ("The callback to call to renderer this arrow type"),
142 g_object_class_install_property(gobject_class
, PROP_RENDERER
, param
);
146 adg_arrow_style_init(AdgArrowStyle
*arrow_style
)
148 AdgArrowStylePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(arrow_style
,
149 ADG_TYPE_ARROW_STYLE
,
150 AdgArrowStylePrivate
);
153 data
->angle
= G_PI
/ 6.;
155 data
->renderer
= NULL
;
157 arrow_style
->data
= data
;
161 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
163 AdgArrowStylePrivate
*data
= ((AdgArrowStyle
*) object
)->data
;
167 g_value_set_double(value
, data
->size
);
170 g_value_set_double(value
, data
->angle
);
173 g_value_set_double(value
, data
->margin
);
176 g_value_set_pointer(value
, data
->renderer
);
179 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
185 set_property(GObject
*object
,
186 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
188 AdgArrowStylePrivate
*data
= ((AdgArrowStyle
*) object
)->data
;
192 data
->size
= g_value_get_double(value
);
195 data
->angle
= g_value_get_double(value
);
198 data
->margin
= g_value_get_double(value
);
201 data
->renderer
= g_value_get_pointer(value
);
204 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
211 * adg_arrow_style_get_slot:
213 * Gets the slot id for this style class.
215 * Return value: the slot
218 adg_arrow_style_get_slot(void)
220 static AdgStyleSlot slot
= -1;
222 if (G_UNLIKELY(slot
< 0))
223 slot
= adg_context_get_slot(ADG_TYPE_ARROW_STYLE
);
229 * adg_arrow_style_new:
231 * Constructs a new arrow style initialized with default params.
233 * Return value: a new arrow style
236 adg_arrow_style_new(void)
238 return g_object_new(ADG_TYPE_ARROW_STYLE
, NULL
);
242 * adg_arrow_style_render:
243 * @arrow_style: an #AdgArrowStyle instance
244 * @cr: the cairo context to use
245 * @segment: the #CpmlSegment where the arrow must be rendered
247 * Renders an arrow on @cr at the beginning of @segment in the way
248 * specified by @arrow_style.
251 adg_arrow_style_render(AdgArrowStyle
*arrow_style
,
252 cairo_t
*cr
, CpmlSegment
*segment
)
254 AdgStyleClass
*style_class
;
255 AdgArrowStylePrivate
*data
;
257 g_return_if_fail(arrow_style
!= NULL
);
258 g_return_if_fail(cr
!= NULL
);
259 g_return_if_fail(segment
!= NULL
);
261 data
= arrow_style
->data
;
264 if (data
->renderer
== NULL
)
267 style_class
= (AdgStyleClass
*) adg_arrow_style_parent_class
;
269 if (style_class
->apply
!= NULL
)
270 style_class
->apply((AdgStyle
*) arrow_style
, cr
);
272 data
->renderer(arrow_style
, cr
, segment
);
276 * adg_arrow_style_get_size:
277 * @arrow_style: an #AdgArrowStyle object
279 * Gets the size (in paper units) of the arrow (renderer dependent value).
281 * Return value: the size value
284 adg_arrow_style_get_size(AdgArrowStyle
*arrow_style
)
286 AdgArrowStylePrivate
*data
;
288 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
290 data
= arrow_style
->data
;
296 * adg_arrow_style_set_size:
297 * @arrow_style: an #AdgArrowStyle object
298 * @size: the new size
303 adg_arrow_style_set_size(AdgArrowStyle
*arrow_style
, gdouble size
)
305 AdgArrowStylePrivate
*data
;
307 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
309 data
= arrow_style
->data
;
312 g_object_notify((GObject
*) arrow_style
, "size");
316 * adg_arrow_style_get_angle:
317 * @arrow_style: an #AdgArrowStyle object
319 * Gets the angle (in degree) of the arrow (renderer dependent value).
321 * Return value: the angle value
324 adg_arrow_style_get_angle(AdgArrowStyle
*arrow_style
)
326 AdgArrowStylePrivate
*data
;
328 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
330 data
= arrow_style
->data
;
336 * adg_arrow_style_set_angle:
337 * @arrow_style: an #AdgArrowStyle object
338 * @angle: the new angle
343 adg_arrow_style_set_angle(AdgArrowStyle
*arrow_style
, gdouble angle
)
345 AdgArrowStylePrivate
*data
;
347 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
349 data
= arrow_style
->data
;
352 g_object_notify((GObject
*) arrow_style
, "angle");
356 * adg_arrow_style_get_margin:
357 * @arrow_style: an #AdgArrowStyle object
359 * Gets the margin (in paper units) of this arrow (renderer dependent value).
360 * The margin is also used to trim the baseline of this amount.
362 * Return value: the margin value
365 adg_arrow_style_get_margin(AdgArrowStyle
*arrow_style
)
367 AdgArrowStylePrivate
*data
;
369 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
371 data
= arrow_style
->data
;
377 * adg_arrow_style_set_margin:
378 * @arrow_style: an #AdgArrowStyle object
379 * @margin: the new margin
384 adg_arrow_style_set_margin(AdgArrowStyle
*arrow_style
, gdouble margin
)
386 AdgArrowStylePrivate
*data
;
388 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
390 data
= arrow_style
->data
;
391 data
->margin
= margin
;
393 g_object_notify((GObject
*) arrow_style
, "margin");
397 * adg_arrow_style_get_renderer:
398 * @arrow_style: an #AdgArrowStyle object
400 * Gets the renderer of @arrow_style.
402 * Return value: the renderer value
405 adg_arrow_style_get_renderer(AdgArrowStyle
*arrow_style
)
407 AdgArrowStylePrivate
*data
;
409 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), NULL
);
411 data
= arrow_style
->data
;
413 return data
->renderer
;
417 * adg_arrow_style_set_renderer:
418 * @arrow_style: an #AdgArrowStyle object
419 * @renderer: the new renderer
421 * Sets a new renderer.
424 adg_arrow_style_set_renderer(AdgArrowStyle
*arrow_style
,
425 AdgArrowRenderer renderer
)
427 AdgArrowStylePrivate
*data
;
429 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
431 data
= arrow_style
->data
;
432 data
->renderer
= renderer
;
434 g_object_notify((GObject
*) arrow_style
, "renderer");
441 static GPtrArray
*pool
= NULL
;
443 if (G_UNLIKELY(pool
== NULL
)) {
444 pool
= g_ptr_array_sized_new(ADG_ARROW_STYLE_LAST
);
446 pool
->pdata
[ADG_ARROW_STYLE_ARROW
] =
447 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer", arrow_renderer
,
449 pool
->pdata
[ADG_ARROW_STYLE_TRIANGLE
] =
450 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer",
451 triangle_renderer
, NULL
);
452 pool
->pdata
[ADG_ARROW_STYLE_DOT
] =
453 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 5., "angle", 0.,
454 "margin", 2.5, "renderer", dot_renderer
, NULL
);
455 pool
->pdata
[ADG_ARROW_STYLE_CIRCLE
] =
456 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
457 "margin", 5., "renderer", circle_renderer
, NULL
);
458 pool
->pdata
[ADG_ARROW_STYLE_BLOCK
] =
459 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
460 "margin", 5., "renderer", block_renderer
, NULL
);
461 pool
->pdata
[ADG_ARROW_STYLE_SQUARE
] =
462 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
463 "margin", -0.1, "renderer", square_renderer
,
465 pool
->pdata
[ADG_ARROW_STYLE_TICK
] =
466 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 20., "angle",
467 G_PI
/ 3., "margin", 0., "renderer",
468 tick_renderer
, NULL
);
470 pool
->len
= ADG_ARROW_STYLE_LAST
;
477 arrow_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
479 draw_triangle(cr
, arrow_style
, segment
);
484 triangle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
486 draw_triangle(cr
, arrow_style
, segment
);
491 dot_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
493 draw_circle(cr
, arrow_style
, segment
);
498 circle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
500 draw_circle(cr
, arrow_style
, segment
);
505 block_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
511 square_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
517 tick_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
523 draw_triangle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
525 AdgArrowStylePrivate
*data
;
526 double length
, height_2
;
529 CpmlPair tail
, tail1
, tail2
;
532 data
= arrow_style
->data
;
534 height_2
= tan(data
->angle
/ 2.0) * length
;
535 cairo_device_to_user_distance(cr
, &length
, &height_2
);
536 org_x
= segment
->data
[1].point
.x
;
537 org_y
= segment
->data
[1].point
.y
;
539 switch (segment
->data
[2].header
.type
) {
540 case CAIRO_PATH_LINE_TO
:
541 vector
.x
= segment
->data
[3].point
.x
- org_x
;
542 vector
.y
= segment
->data
[3].point
.y
- org_y
;
543 cpml_vector_set_length(&vector
, 1.);
545 tail
.x
= vector
.x
* length
+ org_x
;
546 tail
.y
= vector
.y
* length
+ org_y
;
549 vector
.x
= -vector
.y
* height_2
;
550 vector
.y
= tmp
* height_2
;
552 tail1
.x
= tail
.x
+ vector
.x
;
553 tail1
.y
= tail
.y
+ vector
.y
;
555 tail2
.x
= tail
.x
- vector
.x
;
556 tail2
.y
= tail
.y
- vector
.y
;
558 cairo_move_to(cr
, org_x
, org_y
);
559 cairo_line_to(cr
, tail1
.x
, tail1
.y
);
560 cairo_line_to(cr
, tail2
.x
, tail2
.y
);
561 cairo_close_path(cr
);
564 case CAIRO_PATH_CURVE_TO
:
568 g_assert_not_reached();
573 draw_circle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
575 AdgArrowStylePrivate
*data
;
579 data
= arrow_style
->data
;
580 radius
= data
->size
/ 2.;
583 cairo_device_to_user_distance(cr
, &radius
, &dummy
);
586 segment
->data
[1].point
.x
, segment
->data
[1].point
.y
,