Updated "GNU Library General License" to "GNU Lesser General License
[adg.git] / adg / adg-arrow-style.c
blob66285a3468bae9210dad5472c583f3f01914e393
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.
20 /**
21 * SECTION:arrow-style
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-intl.h"
32 #include "adg-util.h"
33 #include <math.h>
35 #define PARENT_CLASS ((AdgStyleClass *) adg_arrow_style_parent_class)
38 enum {
39 PROP_0,
40 PROP_SIZE,
41 PROP_ANGLE,
42 PROP_MARGIN,
43 PROP_RENDERER
47 static void get_property (GObject *object,
48 guint prop_id,
49 GValue *value,
50 GParamSpec *pspec);
51 static void set_property (GObject *object,
52 guint prop_id,
53 const GValue *value,
54 GParamSpec *pspec);
55 static GPtrArray * get_pool (void);
56 static void arrow_renderer (AdgArrowStyle *arrow_style,
57 cairo_t *cr,
58 CpmlPath *segment);
59 static void triangle_renderer (AdgArrowStyle *arrow_style,
60 cairo_t *cr,
61 CpmlPath *segment);
62 static void dot_renderer (AdgArrowStyle *arrow_style,
63 cairo_t *cr,
64 CpmlPath *segment);
65 static void circle_renderer (AdgArrowStyle *arrow_style,
66 cairo_t *cr,
67 CpmlPath *segment);
68 static void block_renderer (AdgArrowStyle *arrow_style,
69 cairo_t *cr,
70 CpmlPath *segment);
71 static void square_renderer (AdgArrowStyle *arrow_style,
72 cairo_t *cr,
73 CpmlPath *segment);
74 static void tick_renderer (AdgArrowStyle *arrow_style,
75 cairo_t *cr,
76 CpmlPath *segment);
77 static void draw_triangle (cairo_t *cr,
78 AdgArrowStyle *arrow_style,
79 CpmlPath *segment);
80 static void draw_circle (cairo_t *cr,
81 AdgArrowStyle *arrow_style,
82 CpmlPath *segment);
85 G_DEFINE_TYPE(AdgArrowStyle, adg_arrow_style, ADG_TYPE_STYLE)
88 static void
89 adg_arrow_style_class_init(AdgArrowStyleClass *klass)
91 GObjectClass *gobject_class;
92 AdgStyleClass *style_class;
93 GParamSpec *param;
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",
106 P_("Arrow Size"),
108 ("The size of the arrow, a renderer dependent parameter"),
109 -G_MAXDOUBLE, G_MAXDOUBLE, 14.,
110 G_PARAM_READWRITE);
111 g_object_class_install_property(gobject_class, PROP_SIZE, param);
113 param = g_param_spec_double("angle",
114 P_("Arrow Angle"),
116 ("The angle of the arrow, a renderer dependent parameter"),
117 -G_MAXDOUBLE, G_MAXDOUBLE, G_PI / 6.,
118 G_PARAM_READWRITE);
119 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
121 param = g_param_spec_double("margin",
122 P_("Arrow Margin"),
124 ("The margin of the arrow, a renderer dependent parameter"),
125 -G_MAXDOUBLE, G_MAXDOUBLE, 14.,
126 G_PARAM_READWRITE);
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"),
133 G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_RENDERER, param);
137 static void
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);
144 priv->size = 14.;
145 priv->angle = G_PI / 6.;
146 priv->margin = 14.;
147 priv->renderer = NULL;
149 arrow_style->priv = priv;
152 static void
153 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
155 AdgArrowStyle *arrow_style = (AdgArrowStyle *) object;
157 switch (prop_id) {
158 case PROP_SIZE:
159 g_value_set_double(value, arrow_style->priv->size);
160 break;
161 case PROP_ANGLE:
162 g_value_set_double(value, arrow_style->priv->angle);
163 break;
164 case PROP_MARGIN:
165 g_value_set_double(value, arrow_style->priv->margin);
166 break;
167 case PROP_RENDERER:
168 g_value_set_pointer(value, arrow_style->priv->renderer);
169 break;
170 default:
171 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
172 break;
176 static void
177 set_property(GObject *object,
178 guint prop_id, const GValue *value, GParamSpec *pspec)
180 AdgArrowStyle *arrow_style = (AdgArrowStyle *) object;
182 switch (prop_id) {
183 case PROP_SIZE:
184 arrow_style->priv->size = g_value_get_double(value);
185 break;
186 case PROP_ANGLE:
187 arrow_style->priv->angle = g_value_get_double(value);
188 break;
189 case PROP_MARGIN:
190 arrow_style->priv->margin = g_value_get_double(value);
191 break;
192 case PROP_RENDERER:
193 arrow_style->priv->renderer = g_value_get_pointer(value);
194 break;
195 default:
196 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
197 break;
203 * adg_arrow_style_get_slot:
205 * Gets the slot id for this style class.
207 * Return value: the slot
209 AdgStyleSlot
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);
217 return slot;
221 * adg_arrow_style_new:
223 * Constructs a new arrow style initialized with default params.
225 * Return value: a new arrow style
227 AdgStyle *
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.
242 void
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);
250 /* NULL renderer */
251 if (arrow_style->priv->renderer == NULL)
252 return;
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
266 gdouble
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
279 * Sets a new size.
281 void
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
298 gdouble
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
311 * Sets a new angle.
313 void
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
331 gdouble
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
344 * Sets a new margin.
346 void
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.
378 void
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");
389 static GPtrArray *
390 get_pool(void)
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,
399 NULL);
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,
415 NULL);
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;
424 return pool;
427 static void
428 arrow_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
430 draw_triangle(cr, arrow_style, segment);
431 cairo_fill(cr);
434 static void
435 triangle_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
437 draw_triangle(cr, arrow_style, segment);
438 cairo_stroke(cr);
441 static void
442 dot_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
444 draw_circle(cr, arrow_style, segment);
445 cairo_fill(cr);
448 static void
449 circle_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
451 draw_circle(cr, arrow_style, segment);
452 cairo_stroke(cr);
455 static void
456 block_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
458 ADG_STUB();
461 static void
462 square_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
464 ADG_STUB();
467 static void
468 tick_renderer(AdgArrowStyle *arrow_style, cairo_t *cr, CpmlPath *segment)
470 ADG_STUB();
473 static void
474 draw_triangle(cairo_t *cr, AdgArrowStyle *arrow_style, CpmlPath *segment)
476 double length, height_2;
477 double tmp;
478 CpmlPair tail, tail1, tail2;
479 CpmlPair vector;
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;
495 tmp = vector.x;
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);
510 break;
511 case CAIRO_PATH_CURVE_TO:
512 ADG_STUB();
513 break;
514 default:
515 g_assert_not_reached();
519 static void
520 draw_circle(cairo_t *cr, AdgArrowStyle *arrow_style, CpmlPath *segment)
522 double radius = arrow_style->priv->size / 2.;
523 double dummy = 0.;
525 cairo_device_to_user_distance(cr, &radius, &dummy);
526 cairo_new_path(cr);
527 cairo_arc(cr, segment->org.x, segment->org.y, radius, 0., M_PI);