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.
22 * SECTION:adg-arrow-style
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.
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
38 * ADG_SLOT_ARROW_STYLE:
40 * Gets the slot id for this style class.
42 * Return value: the requested slot id
46 #include "adg-arrow-style.h"
47 #include "adg-arrow-style-private.h"
48 #include "adg-context.h"
63 static void get_property (GObject
*object
,
67 static void set_property (GObject
*object
,
71 static GPtrArray
* get_pool (void);
72 static void arrow_renderer (AdgArrowStyle
*arrow_style
,
74 CpmlSegment
*segment
);
75 static void triangle_renderer (AdgArrowStyle
*arrow_style
,
77 CpmlSegment
*segment
);
78 static void dot_renderer (AdgArrowStyle
*arrow_style
,
80 CpmlSegment
*segment
);
81 static void circle_renderer (AdgArrowStyle
*arrow_style
,
83 CpmlSegment
*segment
);
84 static void block_renderer (AdgArrowStyle
*arrow_style
,
86 CpmlSegment
*segment
);
87 static void square_renderer (AdgArrowStyle
*arrow_style
,
89 CpmlSegment
*segment
);
90 static void tick_renderer (AdgArrowStyle
*arrow_style
,
92 CpmlSegment
*segment
);
93 static void draw_triangle (cairo_t
*cr
,
94 AdgArrowStyle
*arrow_style
,
95 CpmlSegment
*segment
);
96 static void draw_circle (cairo_t
*cr
,
97 AdgArrowStyle
*arrow_style
,
98 CpmlSegment
*segment
);
101 G_DEFINE_TYPE(AdgArrowStyle
, adg_arrow_style
, ADG_TYPE_STYLE
)
105 adg_arrow_style_class_init(AdgArrowStyleClass
*klass
)
107 GObjectClass
*gobject_class
;
108 AdgStyleClass
*style_class
;
111 gobject_class
= (GObjectClass
*) klass
;
112 style_class
= (AdgStyleClass
*) klass
;
114 g_type_class_add_private(klass
, sizeof(AdgArrowStylePrivate
));
116 gobject_class
->get_property
= get_property
;
117 gobject_class
->set_property
= set_property
;
119 style_class
->get_pool
= get_pool
;
121 param
= g_param_spec_double("size",
124 ("The size of the arrow, a renderer dependent parameter"),
125 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
127 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
129 param
= g_param_spec_double("angle",
132 ("The angle of the arrow, a renderer dependent parameter"),
133 -G_MAXDOUBLE
, G_MAXDOUBLE
, G_PI
/ 6.,
135 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
137 param
= g_param_spec_double("margin",
140 ("The margin of the arrow, a renderer dependent parameter"),
141 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
143 g_object_class_install_property(gobject_class
, PROP_MARGIN
, param
);
145 param
= g_param_spec_pointer("renderer",
146 P_("Renderer Callback"),
148 ("The callback to call to renderer this arrow type"),
150 g_object_class_install_property(gobject_class
, PROP_RENDERER
, param
);
154 adg_arrow_style_init(AdgArrowStyle
*arrow_style
)
156 AdgArrowStylePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(arrow_style
,
157 ADG_TYPE_ARROW_STYLE
,
158 AdgArrowStylePrivate
);
161 data
->angle
= G_PI
/ 6.;
163 data
->renderer
= NULL
;
165 arrow_style
->data
= data
;
169 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
171 AdgArrowStylePrivate
*data
= ((AdgArrowStyle
*) object
)->data
;
175 g_value_set_double(value
, data
->size
);
178 g_value_set_double(value
, data
->angle
);
181 g_value_set_double(value
, data
->margin
);
184 g_value_set_pointer(value
, data
->renderer
);
187 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
193 set_property(GObject
*object
,
194 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
196 AdgArrowStylePrivate
*data
= ((AdgArrowStyle
*) object
)->data
;
200 data
->size
= g_value_get_double(value
);
203 data
->angle
= g_value_get_double(value
);
206 data
->margin
= g_value_get_double(value
);
209 data
->renderer
= g_value_get_pointer(value
);
212 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
219 _adg_arrow_style_get_slot(void)
221 static AdgStyleSlot slot
= -1;
223 if (G_UNLIKELY(slot
< 0))
224 slot
= adg_context_get_slot(ADG_TYPE_ARROW_STYLE
);
230 * adg_arrow_style_new:
232 * Constructs a new arrow style initialized with default params.
234 * Return value: a new arrow style
237 adg_arrow_style_new(void)
239 return g_object_new(ADG_TYPE_ARROW_STYLE
, NULL
);
243 * adg_arrow_style_render:
244 * @arrow_style: an #AdgArrowStyle instance
245 * @cr: the cairo context to use
246 * @segment: the #CpmlSegment where the arrow must be rendered
248 * Renders an arrow on @cr at the beginning of @segment in the way
249 * specified by @arrow_style.
252 adg_arrow_style_render(AdgArrowStyle
*arrow_style
,
253 cairo_t
*cr
, CpmlSegment
*segment
)
255 AdgStyleClass
*style_class
;
256 AdgArrowStylePrivate
*data
;
258 g_return_if_fail(arrow_style
!= NULL
);
259 g_return_if_fail(cr
!= NULL
);
260 g_return_if_fail(segment
!= NULL
);
262 data
= arrow_style
->data
;
265 if (data
->renderer
== NULL
)
268 style_class
= (AdgStyleClass
*) adg_arrow_style_parent_class
;
270 if (style_class
->apply
!= NULL
)
271 style_class
->apply((AdgStyle
*) arrow_style
, cr
);
273 data
->renderer(arrow_style
, cr
, segment
);
277 * adg_arrow_style_get_size:
278 * @arrow_style: an #AdgArrowStyle object
280 * Gets the size (in global space) of the arrow (renderer dependent value).
282 * Return value: the size value
285 adg_arrow_style_get_size(AdgArrowStyle
*arrow_style
)
287 AdgArrowStylePrivate
*data
;
289 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
291 data
= arrow_style
->data
;
297 * adg_arrow_style_set_size:
298 * @arrow_style: an #AdgArrowStyle object
299 * @size: the new size
304 adg_arrow_style_set_size(AdgArrowStyle
*arrow_style
, gdouble size
)
306 AdgArrowStylePrivate
*data
;
308 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
310 data
= arrow_style
->data
;
313 g_object_notify((GObject
*) arrow_style
, "size");
317 * adg_arrow_style_get_angle:
318 * @arrow_style: an #AdgArrowStyle object
320 * Gets the angle (in degree) of the arrow (renderer dependent value).
322 * Return value: the angle value
325 adg_arrow_style_get_angle(AdgArrowStyle
*arrow_style
)
327 AdgArrowStylePrivate
*data
;
329 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
331 data
= arrow_style
->data
;
337 * adg_arrow_style_set_angle:
338 * @arrow_style: an #AdgArrowStyle object
339 * @angle: the new angle
344 adg_arrow_style_set_angle(AdgArrowStyle
*arrow_style
, gdouble angle
)
346 AdgArrowStylePrivate
*data
;
348 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
350 data
= arrow_style
->data
;
353 g_object_notify((GObject
*) arrow_style
, "angle");
357 * adg_arrow_style_get_margin:
358 * @arrow_style: an #AdgArrowStyle object
360 * Gets the margin (in global space) of this arrow (renderer dependent value).
361 * The margin is also used to trim the baseline of this amount.
363 * Return value: the margin value
366 adg_arrow_style_get_margin(AdgArrowStyle
*arrow_style
)
368 AdgArrowStylePrivate
*data
;
370 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
372 data
= arrow_style
->data
;
378 * adg_arrow_style_set_margin:
379 * @arrow_style: an #AdgArrowStyle object
380 * @margin: the new margin
385 adg_arrow_style_set_margin(AdgArrowStyle
*arrow_style
, gdouble margin
)
387 AdgArrowStylePrivate
*data
;
389 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
391 data
= arrow_style
->data
;
392 data
->margin
= margin
;
394 g_object_notify((GObject
*) arrow_style
, "margin");
398 * adg_arrow_style_get_renderer:
399 * @arrow_style: an #AdgArrowStyle object
401 * Gets the renderer of @arrow_style.
403 * Return value: the renderer value
406 adg_arrow_style_get_renderer(AdgArrowStyle
*arrow_style
)
408 AdgArrowStylePrivate
*data
;
410 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), NULL
);
412 data
= arrow_style
->data
;
414 return data
->renderer
;
418 * adg_arrow_style_set_renderer:
419 * @arrow_style: an #AdgArrowStyle object
420 * @renderer: the new renderer
422 * Sets a new renderer.
425 adg_arrow_style_set_renderer(AdgArrowStyle
*arrow_style
,
426 AdgArrowRenderer renderer
)
428 AdgArrowStylePrivate
*data
;
430 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
432 data
= arrow_style
->data
;
433 data
->renderer
= renderer
;
435 g_object_notify((GObject
*) arrow_style
, "renderer");
442 static GPtrArray
*pool
= NULL
;
444 if (G_UNLIKELY(pool
== NULL
)) {
445 pool
= g_ptr_array_sized_new(ADG_ARROW_STYLE_LAST
);
447 pool
->pdata
[ADG_ARROW_STYLE_ARROW
] =
448 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer", arrow_renderer
,
450 pool
->pdata
[ADG_ARROW_STYLE_TRIANGLE
] =
451 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer",
452 triangle_renderer
, NULL
);
453 pool
->pdata
[ADG_ARROW_STYLE_DOT
] =
454 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 5., "angle", 0.,
455 "margin", 2.5, "renderer", dot_renderer
, NULL
);
456 pool
->pdata
[ADG_ARROW_STYLE_CIRCLE
] =
457 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
458 "margin", 5., "renderer", circle_renderer
, NULL
);
459 pool
->pdata
[ADG_ARROW_STYLE_BLOCK
] =
460 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
461 "margin", 5., "renderer", block_renderer
, NULL
);
462 pool
->pdata
[ADG_ARROW_STYLE_SQUARE
] =
463 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
464 "margin", -0.1, "renderer", square_renderer
,
466 pool
->pdata
[ADG_ARROW_STYLE_TICK
] =
467 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 20., "angle",
468 G_PI
/ 3., "margin", 0., "renderer",
469 tick_renderer
, NULL
);
471 pool
->len
= ADG_ARROW_STYLE_LAST
;
478 arrow_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
480 draw_triangle(cr
, arrow_style
, segment
);
485 triangle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
487 draw_triangle(cr
, arrow_style
, segment
);
492 dot_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
494 draw_circle(cr
, arrow_style
, segment
);
499 circle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
501 draw_circle(cr
, arrow_style
, segment
);
506 block_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
512 square_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
518 tick_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
524 draw_triangle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
526 AdgArrowStylePrivate
*data
;
527 double length
, height_2
;
530 CpmlPair tail
, tail1
, tail2
;
533 data
= arrow_style
->data
;
535 height_2
= tan(data
->angle
/ 2.0) * length
;
536 cairo_device_to_user_distance(cr
, &length
, &height_2
);
537 org_x
= segment
->data
[1].point
.x
;
538 org_y
= segment
->data
[1].point
.y
;
540 switch (segment
->data
[2].header
.type
) {
541 case CAIRO_PATH_LINE_TO
:
542 vector
.x
= segment
->data
[3].point
.x
- org_x
;
543 vector
.y
= segment
->data
[3].point
.y
- org_y
;
544 cpml_vector_set_length(&vector
, 1.);
546 tail
.x
= vector
.x
* length
+ org_x
;
547 tail
.y
= vector
.y
* length
+ org_y
;
550 vector
.x
= -vector
.y
* height_2
;
551 vector
.y
= tmp
* height_2
;
553 tail1
.x
= tail
.x
+ vector
.x
;
554 tail1
.y
= tail
.y
+ vector
.y
;
556 tail2
.x
= tail
.x
- vector
.x
;
557 tail2
.y
= tail
.y
- vector
.y
;
559 cairo_move_to(cr
, org_x
, org_y
);
560 cairo_line_to(cr
, tail1
.x
, tail1
.y
);
561 cairo_line_to(cr
, tail2
.x
, tail2
.y
);
562 cairo_close_path(cr
);
565 case CAIRO_PATH_CURVE_TO
:
569 g_assert_not_reached();
574 draw_circle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
576 AdgArrowStylePrivate
*data
;
580 data
= arrow_style
->data
;
581 radius
= data
->size
/ 2.;
584 cairo_device_to_user_distance(cr
, &radius
, &dummy
);
587 segment
->data
[1].point
.x
, segment
->data
[1].point
.y
,