1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, 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.
22 * @title: AdgArrowStyle
23 * @short_description: Arrow rendering related stuff
25 * Contains parameters on how to draw arrows, providing a way to register a
26 * custom rendering callback.
29 #include "adg-arrow-style.h"
30 #include "adg-arrow-style-private.h"
35 #define PARENT_CLASS ((AdgStyleClass *) adg_arrow_style_parent_class)
47 static void get_property (GObject
*object
,
51 static void set_property (GObject
*object
,
55 static GPtrArray
* get_pool (void);
56 static void arrow_renderer (AdgArrowStyle
*arrow_style
,
59 static void triangle_renderer (AdgArrowStyle
*arrow_style
,
62 static void dot_renderer (AdgArrowStyle
*arrow_style
,
65 static void circle_renderer (AdgArrowStyle
*arrow_style
,
68 static void block_renderer (AdgArrowStyle
*arrow_style
,
71 static void square_renderer (AdgArrowStyle
*arrow_style
,
74 static void tick_renderer (AdgArrowStyle
*arrow_style
,
77 static void draw_triangle (cairo_t
*cr
,
78 AdgArrowStyle
*arrow_style
,
80 static void draw_circle (cairo_t
*cr
,
81 AdgArrowStyle
*arrow_style
,
85 G_DEFINE_TYPE(AdgArrowStyle
, adg_arrow_style
, ADG_TYPE_STYLE
)
89 adg_arrow_style_class_init(AdgArrowStyleClass
*klass
)
91 GObjectClass
*gobject_class
;
92 AdgStyleClass
*style_class
;
95 gobject_class
= (GObjectClass
*) klass
;
96 style_class
= (AdgStyleClass
*) klass
;
98 g_type_class_add_private(klass
, sizeof(AdgArrowStylePrivate
));
100 gobject_class
->get_property
= get_property
;
101 gobject_class
->set_property
= set_property
;
103 style_class
->get_pool
= get_pool
;
105 param
= g_param_spec_double("size",
108 ("The size of the arrow, a renderer dependent parameter"),
109 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
111 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
113 param
= g_param_spec_double("angle",
116 ("The angle of the arrow, a renderer dependent parameter"),
117 -G_MAXDOUBLE
, G_MAXDOUBLE
, G_PI
/ 6.,
119 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
121 param
= g_param_spec_double("margin",
124 ("The margin of the arrow, a renderer dependent parameter"),
125 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
127 g_object_class_install_property(gobject_class
, PROP_MARGIN
, param
);
129 param
= g_param_spec_pointer("renderer",
130 P_("Renderer Callback"),
132 ("The callback to call to renderer this arrow type"),
134 g_object_class_install_property(gobject_class
, PROP_RENDERER
, param
);
138 adg_arrow_style_init(AdgArrowStyle
*arrow_style
)
140 AdgArrowStylePrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE(arrow_style
,
141 ADG_TYPE_ARROW_STYLE
,
142 AdgArrowStylePrivate
);
145 priv
->angle
= G_PI
/ 6.;
147 priv
->renderer
= NULL
;
149 arrow_style
->priv
= priv
;
153 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
155 AdgArrowStyle
*arrow_style
= (AdgArrowStyle
*) object
;
159 g_value_set_double(value
, arrow_style
->priv
->size
);
162 g_value_set_double(value
, arrow_style
->priv
->angle
);
165 g_value_set_double(value
, arrow_style
->priv
->margin
);
168 g_value_set_pointer(value
, arrow_style
->priv
->renderer
);
171 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
177 set_property(GObject
*object
,
178 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
180 AdgArrowStyle
*arrow_style
= (AdgArrowStyle
*) object
;
184 arrow_style
->priv
->size
= g_value_get_double(value
);
187 arrow_style
->priv
->angle
= g_value_get_double(value
);
190 arrow_style
->priv
->margin
= g_value_get_double(value
);
193 arrow_style
->priv
->renderer
= g_value_get_pointer(value
);
196 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
203 * adg_arrow_style_get_slot:
205 * Gets the slot id for this style class.
207 * Return value: the slot
210 adg_arrow_style_get_slot(void)
212 static AdgStyleSlot slot
= -1;
214 if (G_UNLIKELY(slot
< 0))
215 slot
= adg_context_get_slot(ADG_TYPE_ARROW_STYLE
);
221 * adg_arrow_style_new:
223 * Constructs a new arrow style initialized with default params.
225 * Return value: a new arrow style
228 adg_arrow_style_new(void)
230 return g_object_new(ADG_TYPE_ARROW_STYLE
, NULL
);
234 * adg_arrow_style_render:
235 * @arrow_style: an #AdgArrowStyle instance
236 * @cr: the cairo context to use
237 * @segment: the #CpmlPath where the arrow must be rendered
239 * Renders an arrow on @cr at the beginning of @segment in the way
240 * specified by @arrow_style.
243 adg_arrow_style_render(AdgArrowStyle
*arrow_style
,
244 cairo_t
*cr
, CpmlPath
*segment
)
246 g_return_if_fail(arrow_style
!= NULL
);
247 g_return_if_fail(cr
!= NULL
);
248 g_return_if_fail(segment
!= NULL
);
251 if (arrow_style
->priv
->renderer
== NULL
)
254 PARENT_CLASS
->apply((AdgStyle
*) arrow_style
, cr
);
255 arrow_style
->priv
->renderer(arrow_style
, cr
, segment
);
259 * adg_arrow_style_get_size:
260 * @arrow_style: an #AdgArrowStyle object
262 * Gets the size (in paper units) of the arrow (renderer dependent value).
264 * Return value: the size value
267 adg_arrow_style_get_size(AdgArrowStyle
*arrow_style
)
269 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
271 return arrow_style
->priv
->size
;
275 * adg_arrow_style_set_size:
276 * @arrow_style: an #AdgArrowStyle object
277 * @size: the new size
282 adg_arrow_style_set_size(AdgArrowStyle
*arrow_style
, gdouble size
)
284 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
286 arrow_style
->priv
->size
= size
;
287 g_object_notify((GObject
*) arrow_style
, "size");
291 * adg_arrow_style_get_angle:
292 * @arrow_style: an #AdgArrowStyle object
294 * Gets the angle (in degree) of the arrow (renderer dependent value).
296 * Return value: the angle value
299 adg_arrow_style_get_angle(AdgArrowStyle
*arrow_style
)
301 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
303 return arrow_style
->priv
->angle
;
307 * adg_arrow_style_set_angle:
308 * @arrow_style: an #AdgArrowStyle object
309 * @angle: the new angle
314 adg_arrow_style_set_angle(AdgArrowStyle
*arrow_style
, gdouble angle
)
316 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
318 arrow_style
->priv
->angle
= angle
;
319 g_object_notify((GObject
*) arrow_style
, "angle");
323 * adg_arrow_style_get_margin:
324 * @arrow_style: an #AdgArrowStyle object
326 * Gets the margin (in paper units) of this arrow (renderer dependent value).
327 * The margin is also used to trim the baseline of this amount.
329 * Return value: the margin value
332 adg_arrow_style_get_margin(AdgArrowStyle
*arrow_style
)
334 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
336 return arrow_style
->priv
->margin
;
340 * adg_arrow_style_set_margin:
341 * @arrow_style: an #AdgArrowStyle object
342 * @margin: the new margin
347 adg_arrow_style_set_margin(AdgArrowStyle
*arrow_style
, gdouble margin
)
349 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
351 arrow_style
->priv
->margin
= margin
;
352 g_object_notify((GObject
*) arrow_style
, "margin");
356 * adg_arrow_style_get_renderer:
357 * @arrow_style: an #AdgArrowStyle object
359 * Gets the renderer of @arrow_style.
361 * Return value: the renderer value
363 const AdgArrowRenderer
364 adg_arrow_style_get_renderer(AdgArrowStyle
*arrow_style
)
366 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), NULL
);
368 return arrow_style
->priv
->renderer
;
372 * adg_arrow_style_set_renderer:
373 * @arrow_style: an #AdgArrowStyle object
374 * @renderer: the new renderer
376 * Sets a new renderer.
379 adg_arrow_style_set_renderer(AdgArrowStyle
*arrow_style
,
380 AdgArrowRenderer renderer
)
382 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
384 arrow_style
->priv
->renderer
= renderer
;
385 g_object_notify((GObject
*) arrow_style
, "renderer");
392 static GPtrArray
*pool
= NULL
;
394 if (G_UNLIKELY(pool
== NULL
)) {
395 pool
= g_ptr_array_sized_new(ADG_ARROW_STYLE_LAST
);
397 pool
->pdata
[ADG_ARROW_STYLE_ARROW
] =
398 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer", arrow_renderer
,
400 pool
->pdata
[ADG_ARROW_STYLE_TRIANGLE
] =
401 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer",
402 triangle_renderer
, NULL
);
403 pool
->pdata
[ADG_ARROW_STYLE_DOT
] =
404 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 5., "angle", 0.,
405 "margin", 2.5, "renderer", dot_renderer
, NULL
);
406 pool
->pdata
[ADG_ARROW_STYLE_CIRCLE
] =
407 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
408 "margin", 5., "renderer", circle_renderer
, NULL
);
409 pool
->pdata
[ADG_ARROW_STYLE_BLOCK
] =
410 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
411 "margin", 5., "renderer", block_renderer
, NULL
);
412 pool
->pdata
[ADG_ARROW_STYLE_SQUARE
] =
413 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
414 "margin", -0.1, "renderer", square_renderer
,
416 pool
->pdata
[ADG_ARROW_STYLE_TICK
] =
417 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 20., "angle",
418 G_PI
/ 3., "margin", 0., "renderer",
419 tick_renderer
, NULL
);
421 pool
->len
= ADG_ARROW_STYLE_LAST
;
428 arrow_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
430 draw_triangle(cr
, arrow_style
, segment
);
435 triangle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
437 draw_triangle(cr
, arrow_style
, segment
);
442 dot_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
444 draw_circle(cr
, arrow_style
, segment
);
449 circle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
451 draw_circle(cr
, arrow_style
, segment
);
456 block_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
462 square_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
468 tick_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlPath
*segment
)
474 draw_triangle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlPath
*segment
)
476 double length
, height_2
;
478 CpmlPair tail
, tail1
, tail2
;
481 length
= arrow_style
->priv
->size
;
482 height_2
= tan(arrow_style
->priv
->angle
/ 2.0) * length
;
483 cairo_device_to_user_distance(cr
, &length
, &height_2
);
485 switch (segment
->cairo_path
.data
[0].header
.type
) {
486 case CAIRO_PATH_LINE_TO
:
487 cpml_primitive_get_pair(segment
, &vector
, CPML_FIRST
);
488 vector
.x
-= segment
->org
.x
;
489 vector
.y
-= segment
->org
.y
;
490 cpml_vector_from_pair(&vector
, &vector
);
492 tail
.x
= vector
.x
* length
+ segment
->org
.x
;
493 tail
.y
= vector
.y
* length
+ segment
->org
.y
;
496 vector
.x
= -vector
.y
* height_2
;
497 vector
.y
= tmp
* height_2
;
499 tail1
.x
= tail
.x
+ vector
.x
;
500 tail1
.y
= tail
.y
+ vector
.y
;
502 tail2
.x
= tail
.x
- vector
.x
;
503 tail2
.y
= tail
.y
- vector
.y
;
505 cairo_move_to(cr
, segment
->org
.x
, segment
->org
.y
);
506 cairo_line_to(cr
, tail1
.x
, tail1
.y
);
507 cairo_line_to(cr
, tail2
.x
, tail2
.y
);
508 cairo_close_path(cr
);
511 case CAIRO_PATH_CURVE_TO
:
515 g_assert_not_reached();
520 draw_circle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlPath
*segment
)
522 double radius
= arrow_style
->priv
->size
/ 2.;
525 cairo_device_to_user_distance(cr
, &radius
, &dummy
);
527 cairo_arc(cr
, segment
->org
.x
, segment
->org
.y
, radius
, 0., M_PI
);