[AdgArrow] First implementation
[adg.git] / adg / adg-arrow-style.c
blobb9f93d229969804be06586cef148dd9f86a3b42b
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.
21 /**
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.
27 **/
29 /**
30 * AdgArrowStyle:
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
34 **/
36 /**
37 * ADG_SLOT_ARROW_STYLE:
39 * Gets the slot id for this style class.
41 * Returns: the requested slot id
42 **/
45 #include "adg-arrow-style.h"
46 #include "adg-arrow-style-private.h"
47 #include "adg-context.h"
48 #include "adg-intl.h"
49 #include "adg-util.h"
50 #include <math.h>
53 enum {
54 PROP_0,
55 PROP_SIZE,
56 PROP_ANGLE,
57 PROP_MARGIN,
58 PROP_RENDERER
62 static void get_property (GObject *object,
63 guint prop_id,
64 GValue *value,
65 GParamSpec *pspec);
66 static void set_property (GObject *object,
67 guint prop_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static GPtrArray * get_pool (void);
71 static void arrow_renderer (AdgArrowStyle *arrow_style,
72 cairo_t *cr,
73 CpmlSegment *segment);
74 static void triangle_renderer (AdgArrowStyle *arrow_style,
75 cairo_t *cr,
76 CpmlSegment *segment);
77 static void dot_renderer (AdgArrowStyle *arrow_style,
78 cairo_t *cr,
79 CpmlSegment *segment);
80 static void circle_renderer (AdgArrowStyle *arrow_style,
81 cairo_t *cr,
82 CpmlSegment *segment);
83 static void block_renderer (AdgArrowStyle *arrow_style,
84 cairo_t *cr,
85 CpmlSegment *segment);
86 static void square_renderer (AdgArrowStyle *arrow_style,
87 cairo_t *cr,
88 CpmlSegment *segment);
89 static void tick_renderer (AdgArrowStyle *arrow_style,
90 cairo_t *cr,
91 CpmlSegment *segment);
92 static void draw_triangle (cairo_t *cr,
93 AdgArrowStyle *arrow_style,
94 CpmlSegment *segment);
95 static void draw_circle (cairo_t *cr,
96 AdgArrowStyle *arrow_style,
97 CpmlSegment *segment);
100 G_DEFINE_TYPE(AdgArrowStyle, adg_arrow_style, ADG_TYPE_STYLE)
103 static void
104 adg_arrow_style_class_init(AdgArrowStyleClass *klass)
106 GObjectClass *gobject_class;
107 AdgStyleClass *style_class;
108 GParamSpec *param;
110 gobject_class = (GObjectClass *) klass;
111 style_class = (AdgStyleClass *) klass;
113 g_type_class_add_private(klass, sizeof(AdgArrowStylePrivate));
115 gobject_class->get_property = get_property;
116 gobject_class->set_property = set_property;
118 style_class->get_pool = get_pool;
120 param = g_param_spec_double("size",
121 P_("Arrow Size"),
123 ("The size of the arrow, a renderer dependent parameter"),
124 -G_MAXDOUBLE, G_MAXDOUBLE, 14.,
125 G_PARAM_READWRITE);
126 g_object_class_install_property(gobject_class, PROP_SIZE, param);
128 param = g_param_spec_double("angle",
129 P_("Arrow Angle"),
131 ("The angle of the arrow, a renderer dependent parameter"),
132 -G_MAXDOUBLE, G_MAXDOUBLE, G_PI / 6.,
133 G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
136 param = g_param_spec_double("margin",
137 P_("Arrow Margin"),
139 ("The margin of the arrow, a renderer dependent parameter"),
140 -G_MAXDOUBLE, G_MAXDOUBLE, 14.,
141 G_PARAM_READWRITE);
142 g_object_class_install_property(gobject_class, PROP_MARGIN, param);
144 param = g_param_spec_pointer("renderer",
145 P_("Renderer Callback"),
147 ("The callback to call to renderer this arrow type"),
148 G_PARAM_READWRITE);
149 g_object_class_install_property(gobject_class, PROP_RENDERER, param);
152 static void
153 adg_arrow_style_init(AdgArrowStyle *arrow_style)
155 AdgArrowStylePrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(arrow_style,
156 ADG_TYPE_ARROW_STYLE,
157 AdgArrowStylePrivate);
159 data->size = 14.;
160 data->angle = G_PI / 6.;
161 data->margin = 14.;
162 data->renderer = NULL;
164 arrow_style->data = data;
167 static void
168 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
170 AdgArrowStylePrivate *data = ((AdgArrowStyle *) object)->data;
172 switch (prop_id) {
173 case PROP_SIZE:
174 g_value_set_double(value, data->size);
175 break;
176 case PROP_ANGLE:
177 g_value_set_double(value, data->angle);
178 break;
179 case PROP_MARGIN:
180 g_value_set_double(value, data->margin);
181 break;
182 case PROP_RENDERER:
183 g_value_set_pointer(value, data->renderer);
184 break;
185 default:
186 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
187 break;
191 static void
192 set_property(GObject *object,
193 guint prop_id, const GValue *value, GParamSpec *pspec)
195 AdgArrowStylePrivate *data = ((AdgArrowStyle *) object)->data;
197 switch (prop_id) {
198 case PROP_SIZE:
199 data->size = g_value_get_double(value);
200 break;
201 case PROP_ANGLE:
202 data->angle = g_value_get_double(value);
203 break;
204 case PROP_MARGIN:
205 data->margin = g_value_get_double(value);
206 break;
207 case PROP_RENDERER:
208 data->renderer = g_value_get_pointer(value);
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
212 break;
217 AdgStyleSlot
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);
225 return slot;
229 * adg_arrow_style_new:
231 * Constructs a new arrow style initialized with default params.
233 * Returns: a new arrow style
235 AdgStyle *
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.
250 void
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;
263 /* NULL renderer */
264 if (data->renderer == NULL)
265 return;
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 global space) of the arrow (renderer dependent value).
281 * Returns: the size value
283 gdouble
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;
292 return data->size;
296 * adg_arrow_style_set_size:
297 * @arrow_style: an #AdgArrowStyle object
298 * @size: the new size
300 * Sets a new size.
302 void
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;
310 data->size = size;
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 * Returns: the angle value
323 gdouble
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;
332 return data->angle;
336 * adg_arrow_style_set_angle:
337 * @arrow_style: an #AdgArrowStyle object
338 * @angle: the new angle
340 * Sets a new angle.
342 void
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;
350 data->angle = angle;
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 global space) of this arrow (renderer dependent value).
360 * The margin is also used to trim the baseline of this amount.
362 * Returns: the margin value
364 gdouble
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;
373 return data->margin;
377 * adg_arrow_style_set_margin:
378 * @arrow_style: an #AdgArrowStyle object
379 * @margin: the new margin
381 * Sets a new margin.
383 void
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 * Returns: the renderer value
404 AdgArrowRenderer
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.
423 void
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");
438 static GPtrArray *
439 get_pool(void)
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,
448 NULL);
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,
464 NULL);
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;
473 return pool;
476 static void
477 arrow_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
479 draw_triangle(cr, arrow_style, segment);
480 cairo_fill(cr);
483 static void
484 triangle_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
486 draw_triangle(cr, arrow_style, segment);
487 cairo_stroke(cr);
490 static void
491 dot_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
493 draw_circle(cr, arrow_style, segment);
494 cairo_fill(cr);
497 static void
498 circle_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
500 draw_circle(cr, arrow_style, segment);
501 cairo_stroke(cr);
504 static void
505 block_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
507 ADG_STUB();
510 static void
511 square_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
513 ADG_STUB();
516 static void
517 tick_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlSegment *segment)
519 ADG_STUB();
522 static void
523 draw_triangle(cairo_t *cr, AdgArrowStyle *arrow_style, CpmlSegment *segment)
525 AdgArrowStylePrivate *data;
526 double length, height_2;
527 double org_x, org_y;
528 double tmp;
529 CpmlPair tail, tail1, tail2;
530 CpmlPair vector;
532 data = arrow_style->data;
533 length = data->size;
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;
548 tmp = vector.x;
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);
563 break;
564 case CAIRO_PATH_CURVE_TO:
565 ADG_STUB();
566 break;
567 default:
568 g_assert_not_reached();
572 static void
573 draw_circle(cairo_t *cr, AdgArrowStyle *arrow_style, CpmlSegment *segment)
575 AdgArrowStylePrivate *data;
576 double radius;
577 double dummy;
579 data = arrow_style->data;
580 radius = data->size / 2.;
581 dummy = 0;
583 cairo_device_to_user_distance(cr, &radius, &dummy);
584 cairo_new_path(cr);
585 cairo_arc(cr,
586 segment->data[1].point.x, segment->data[1].point.y,
587 radius, 0., M_PI);