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 * @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"
31 #include "adg-context.h"
46 static void get_property (GObject
*object
,
50 static void set_property (GObject
*object
,
54 static GPtrArray
* get_pool (void);
55 static void arrow_renderer (AdgArrowStyle
*arrow_style
,
57 CpmlSegment
*segment
);
58 static void triangle_renderer (AdgArrowStyle
*arrow_style
,
60 CpmlSegment
*segment
);
61 static void dot_renderer (AdgArrowStyle
*arrow_style
,
63 CpmlSegment
*segment
);
64 static void circle_renderer (AdgArrowStyle
*arrow_style
,
66 CpmlSegment
*segment
);
67 static void block_renderer (AdgArrowStyle
*arrow_style
,
69 CpmlSegment
*segment
);
70 static void square_renderer (AdgArrowStyle
*arrow_style
,
72 CpmlSegment
*segment
);
73 static void tick_renderer (AdgArrowStyle
*arrow_style
,
75 CpmlSegment
*segment
);
76 static void draw_triangle (cairo_t
*cr
,
77 AdgArrowStyle
*arrow_style
,
78 CpmlSegment
*segment
);
79 static void draw_circle (cairo_t
*cr
,
80 AdgArrowStyle
*arrow_style
,
81 CpmlSegment
*segment
);
84 G_DEFINE_TYPE(AdgArrowStyle
, adg_arrow_style
, ADG_TYPE_STYLE
)
88 adg_arrow_style_class_init(AdgArrowStyleClass
*klass
)
90 GObjectClass
*gobject_class
;
91 AdgStyleClass
*style_class
;
94 gobject_class
= (GObjectClass
*) klass
;
95 style_class
= (AdgStyleClass
*) klass
;
97 g_type_class_add_private(klass
, sizeof(AdgArrowStylePrivate
));
99 gobject_class
->get_property
= get_property
;
100 gobject_class
->set_property
= set_property
;
102 style_class
->get_pool
= get_pool
;
104 param
= g_param_spec_double("size",
107 ("The size of the arrow, a renderer dependent parameter"),
108 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
110 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
112 param
= g_param_spec_double("angle",
115 ("The angle of the arrow, a renderer dependent parameter"),
116 -G_MAXDOUBLE
, G_MAXDOUBLE
, G_PI
/ 6.,
118 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
120 param
= g_param_spec_double("margin",
123 ("The margin of the arrow, a renderer dependent parameter"),
124 -G_MAXDOUBLE
, G_MAXDOUBLE
, 14.,
126 g_object_class_install_property(gobject_class
, PROP_MARGIN
, param
);
128 param
= g_param_spec_pointer("renderer",
129 P_("Renderer Callback"),
131 ("The callback to call to renderer this arrow type"),
133 g_object_class_install_property(gobject_class
, PROP_RENDERER
, param
);
137 adg_arrow_style_init(AdgArrowStyle
*arrow_style
)
139 AdgArrowStylePrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE(arrow_style
,
140 ADG_TYPE_ARROW_STYLE
,
141 AdgArrowStylePrivate
);
144 priv
->angle
= G_PI
/ 6.;
146 priv
->renderer
= NULL
;
148 arrow_style
->priv
= priv
;
152 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
154 AdgArrowStyle
*arrow_style
= (AdgArrowStyle
*) object
;
158 g_value_set_double(value
, arrow_style
->priv
->size
);
161 g_value_set_double(value
, arrow_style
->priv
->angle
);
164 g_value_set_double(value
, arrow_style
->priv
->margin
);
167 g_value_set_pointer(value
, arrow_style
->priv
->renderer
);
170 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
176 set_property(GObject
*object
,
177 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
179 AdgArrowStyle
*arrow_style
= (AdgArrowStyle
*) object
;
183 arrow_style
->priv
->size
= g_value_get_double(value
);
186 arrow_style
->priv
->angle
= g_value_get_double(value
);
189 arrow_style
->priv
->margin
= g_value_get_double(value
);
192 arrow_style
->priv
->renderer
= g_value_get_pointer(value
);
195 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
202 * adg_arrow_style_get_slot:
204 * Gets the slot id for this style class.
206 * Return value: the slot
209 adg_arrow_style_get_slot(void)
211 static AdgStyleSlot slot
= -1;
213 if (G_UNLIKELY(slot
< 0))
214 slot
= adg_context_get_slot(ADG_TYPE_ARROW_STYLE
);
220 * adg_arrow_style_new:
222 * Constructs a new arrow style initialized with default params.
224 * Return value: a new arrow style
227 adg_arrow_style_new(void)
229 return g_object_new(ADG_TYPE_ARROW_STYLE
, NULL
);
233 * adg_arrow_style_render:
234 * @arrow_style: an #AdgArrowStyle instance
235 * @cr: the cairo context to use
236 * @segment: the #CpmlSegment where the arrow must be rendered
238 * Renders an arrow on @cr at the beginning of @segment in the way
239 * specified by @arrow_style.
242 adg_arrow_style_render(AdgArrowStyle
*arrow_style
,
243 cairo_t
*cr
, CpmlSegment
*segment
)
245 AdgStyleClass
*style_class
;
247 g_return_if_fail(arrow_style
!= NULL
);
248 g_return_if_fail(cr
!= NULL
);
249 g_return_if_fail(segment
!= NULL
);
252 if (arrow_style
->priv
->renderer
== NULL
)
255 style_class
= (AdgStyleClass
*) adg_arrow_style_parent_class
;
257 if (style_class
->apply
!= NULL
)
258 style_class
->apply((AdgStyle
*) arrow_style
, cr
);
260 arrow_style
->priv
->renderer(arrow_style
, cr
, segment
);
264 * adg_arrow_style_get_size:
265 * @arrow_style: an #AdgArrowStyle object
267 * Gets the size (in paper units) of the arrow (renderer dependent value).
269 * Return value: the size value
272 adg_arrow_style_get_size(AdgArrowStyle
*arrow_style
)
274 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
276 return arrow_style
->priv
->size
;
280 * adg_arrow_style_set_size:
281 * @arrow_style: an #AdgArrowStyle object
282 * @size: the new size
287 adg_arrow_style_set_size(AdgArrowStyle
*arrow_style
, gdouble size
)
289 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
291 arrow_style
->priv
->size
= size
;
292 g_object_notify((GObject
*) arrow_style
, "size");
296 * adg_arrow_style_get_angle:
297 * @arrow_style: an #AdgArrowStyle object
299 * Gets the angle (in degree) of the arrow (renderer dependent value).
301 * Return value: the angle value
304 adg_arrow_style_get_angle(AdgArrowStyle
*arrow_style
)
306 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
308 return arrow_style
->priv
->angle
;
312 * adg_arrow_style_set_angle:
313 * @arrow_style: an #AdgArrowStyle object
314 * @angle: the new angle
319 adg_arrow_style_set_angle(AdgArrowStyle
*arrow_style
, gdouble angle
)
321 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
323 arrow_style
->priv
->angle
= angle
;
324 g_object_notify((GObject
*) arrow_style
, "angle");
328 * adg_arrow_style_get_margin:
329 * @arrow_style: an #AdgArrowStyle object
331 * Gets the margin (in paper units) of this arrow (renderer dependent value).
332 * The margin is also used to trim the baseline of this amount.
334 * Return value: the margin value
337 adg_arrow_style_get_margin(AdgArrowStyle
*arrow_style
)
339 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), 0.);
341 return arrow_style
->priv
->margin
;
345 * adg_arrow_style_set_margin:
346 * @arrow_style: an #AdgArrowStyle object
347 * @margin: the new margin
352 adg_arrow_style_set_margin(AdgArrowStyle
*arrow_style
, gdouble margin
)
354 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
356 arrow_style
->priv
->margin
= margin
;
357 g_object_notify((GObject
*) arrow_style
, "margin");
361 * adg_arrow_style_get_renderer:
362 * @arrow_style: an #AdgArrowStyle object
364 * Gets the renderer of @arrow_style.
366 * Return value: the renderer value
369 adg_arrow_style_get_renderer(AdgArrowStyle
*arrow_style
)
371 g_return_val_if_fail(ADG_IS_ARROW_STYLE(arrow_style
), NULL
);
373 return arrow_style
->priv
->renderer
;
377 * adg_arrow_style_set_renderer:
378 * @arrow_style: an #AdgArrowStyle object
379 * @renderer: the new renderer
381 * Sets a new renderer.
384 adg_arrow_style_set_renderer(AdgArrowStyle
*arrow_style
,
385 AdgArrowRenderer renderer
)
387 g_return_if_fail(ADG_IS_ARROW_STYLE(arrow_style
));
389 arrow_style
->priv
->renderer
= renderer
;
390 g_object_notify((GObject
*) arrow_style
, "renderer");
397 static GPtrArray
*pool
= NULL
;
399 if (G_UNLIKELY(pool
== NULL
)) {
400 pool
= g_ptr_array_sized_new(ADG_ARROW_STYLE_LAST
);
402 pool
->pdata
[ADG_ARROW_STYLE_ARROW
] =
403 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer", arrow_renderer
,
405 pool
->pdata
[ADG_ARROW_STYLE_TRIANGLE
] =
406 g_object_new(ADG_TYPE_ARROW_STYLE
, "renderer",
407 triangle_renderer
, NULL
);
408 pool
->pdata
[ADG_ARROW_STYLE_DOT
] =
409 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 5., "angle", 0.,
410 "margin", 2.5, "renderer", dot_renderer
, NULL
);
411 pool
->pdata
[ADG_ARROW_STYLE_CIRCLE
] =
412 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
413 "margin", 5., "renderer", circle_renderer
, NULL
);
414 pool
->pdata
[ADG_ARROW_STYLE_BLOCK
] =
415 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
416 "margin", 5., "renderer", block_renderer
, NULL
);
417 pool
->pdata
[ADG_ARROW_STYLE_SQUARE
] =
418 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 10., "angle", 0.,
419 "margin", -0.1, "renderer", square_renderer
,
421 pool
->pdata
[ADG_ARROW_STYLE_TICK
] =
422 g_object_new(ADG_TYPE_ARROW_STYLE
, "size", 20., "angle",
423 G_PI
/ 3., "margin", 0., "renderer",
424 tick_renderer
, NULL
);
426 pool
->len
= ADG_ARROW_STYLE_LAST
;
433 arrow_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
435 draw_triangle(cr
, arrow_style
, segment
);
440 triangle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
442 draw_triangle(cr
, arrow_style
, segment
);
447 dot_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
449 draw_circle(cr
, arrow_style
, segment
);
454 circle_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
456 draw_circle(cr
, arrow_style
, segment
);
461 block_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
467 square_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
473 tick_renderer(AdgArrowStyle
*arrow_style
, cairo_t
*cr
, CpmlSegment
*segment
)
479 draw_triangle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
481 double length
, height_2
;
484 CpmlPair tail
, tail1
, tail2
;
487 length
= arrow_style
->priv
->size
;
488 height_2
= tan(arrow_style
->priv
->angle
/ 2.0) * length
;
489 cairo_device_to_user_distance(cr
, &length
, &height_2
);
490 org_x
= segment
->data
[1].point
.x
;
491 org_y
= segment
->data
[1].point
.y
;
493 switch (segment
->data
[2].header
.type
) {
494 case CAIRO_PATH_LINE_TO
:
495 vector
.x
= segment
->data
[3].point
.x
- org_x
;
496 vector
.y
= segment
->data
[3].point
.y
- org_y
;
497 cpml_vector_set_length(&vector
, 1.);
499 tail
.x
= vector
.x
* length
+ org_x
;
500 tail
.y
= vector
.y
* length
+ org_y
;
503 vector
.x
= -vector
.y
* height_2
;
504 vector
.y
= tmp
* height_2
;
506 tail1
.x
= tail
.x
+ vector
.x
;
507 tail1
.y
= tail
.y
+ vector
.y
;
509 tail2
.x
= tail
.x
- vector
.x
;
510 tail2
.y
= tail
.y
- vector
.y
;
512 cairo_move_to(cr
, org_x
, org_y
);
513 cairo_line_to(cr
, tail1
.x
, tail1
.y
);
514 cairo_line_to(cr
, tail2
.x
, tail2
.y
);
515 cairo_close_path(cr
);
518 case CAIRO_PATH_CURVE_TO
:
522 g_assert_not_reached();
527 draw_circle(cairo_t
*cr
, AdgArrowStyle
*arrow_style
, CpmlSegment
*segment
)
529 double radius
= arrow_style
->priv
->size
/ 2.;
532 cairo_device_to_user_distance(cr
, &radius
, &dummy
);
535 segment
->data
[1].point
.x
, segment
->data
[1].point
.y
,