doc: updated copyright
[adg.git] / src / adg / adg-rdim.c
blob4f831a8b5a3d32ea69d2c2102cc3415c6546e41c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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-rdim
23 * @short_description: Radial dimensions
25 * The #AdgRDim entity represents a radial dimension.
27 * Since: 1.0
28 **/
30 /**
31 * AdgRDim:
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
36 * Since: 1.0
37 **/
40 #include "adg-internal.h"
41 #include "adg-container.h"
42 #include "adg-alignment.h"
43 #include "adg-model.h"
44 #include "adg-point.h"
45 #include "adg-trail.h"
46 #include "adg-marker.h"
47 #include "adg-style.h"
48 #include "adg-dim-style.h"
49 #include "adg-textual.h"
50 #include "adg-toy-text.h"
51 #include "adg-dim.h"
52 #include "adg-dim-private.h"
53 #include <math.h>
55 #include "adg-rdim.h"
56 #include "adg-rdim-private.h"
59 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_rdim_parent_class)
60 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_rdim_parent_class)
63 G_DEFINE_TYPE(AdgRDim, adg_rdim, ADG_TYPE_DIM)
65 enum {
66 PROP_0,
67 PROP_VALUE
71 static void _adg_dispose (GObject *object);
72 static void _adg_get_property (GObject *object,
73 guint param_id,
74 GValue *value,
75 GParamSpec *pspec);
76 static void _adg_set_property (GObject *object,
77 guint param_id,
78 const GValue *value,
79 GParamSpec *pspec);
80 static void _adg_global_changed (AdgEntity *entity);
81 static void _adg_local_changed (AdgEntity *entity);
82 static void _adg_invalidate (AdgEntity *entity);
83 static void _adg_arrange (AdgEntity *entity);
84 static void _adg_render (AdgEntity *entity,
85 cairo_t *cr);
86 static gchar * _adg_default_value (AdgDim *dim);
87 static gboolean _adg_update_geometry (AdgRDim *rdim);
88 static void _adg_update_entities (AdgRDim *rdim);
89 static void _adg_clear_trail (AdgRDim *rdim);
90 static void _adg_dispose_trail (AdgRDim *rdim);
91 static void _adg_dispose_marker (AdgRDim *rdim);
92 static CpmlPath * _adg_trail_callback (AdgTrail *trail,
93 gpointer user_data);
96 static void
97 adg_rdim_class_init(AdgRDimClass *klass)
99 GObjectClass *gobject_class;
100 AdgEntityClass *entity_class;
101 AdgDimClass *dim_class;
102 GParamSpec *param, *old_param;
104 gobject_class = (GObjectClass *) klass;
105 entity_class = (AdgEntityClass *) klass;
106 dim_class = (AdgDimClass *) klass;
108 g_type_class_add_private(klass, sizeof(AdgRDimPrivate));
110 gobject_class->dispose = _adg_dispose;
111 gobject_class->get_property = _adg_get_property;
112 gobject_class->set_property = _adg_set_property;
114 entity_class->global_changed = _adg_global_changed;
115 entity_class->local_changed = _adg_local_changed;
116 entity_class->invalidate = _adg_invalidate;
117 entity_class->arrange = _adg_arrange;
118 entity_class->render = _adg_render;
120 dim_class->default_value = _adg_default_value;
122 /* Override #AdgDim:value to prepend "R "
123 * to the default set value template string */
124 old_param = g_object_class_find_property(gobject_class, "value");
125 param = g_param_spec_string(old_param->name,
126 g_param_spec_get_nick(old_param),
127 g_param_spec_get_blurb(old_param),
128 "R <>",
129 old_param->flags);
130 g_object_class_install_property(gobject_class, PROP_VALUE, param);
133 static void
134 adg_rdim_init(AdgRDim *rdim)
136 AdgRDimPrivate *data;
137 cairo_path_data_t move_to, line_to;
139 data = G_TYPE_INSTANCE_GET_PRIVATE(rdim, ADG_TYPE_RDIM, AdgRDimPrivate);
140 move_to.header.type = CPML_MOVE;
141 move_to.header.length = 2;
142 line_to.header.type = CPML_LINE;
143 line_to.header.length = 2;
145 data->trail = NULL;
146 data->marker = NULL;
147 data->geometry_arranged = FALSE;
148 data->radius = -1.;
149 data->shift.base.x = data->shift.base.y = 0;
150 cairo_matrix_init_identity(&data->quote.global_map);
152 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
153 data->cpml.path.data = data->cpml.data;
154 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
155 data->cpml.path.data[0] = move_to;
156 data->cpml.path.data[2] = line_to;
157 data->cpml.path.data[4] = move_to;
158 data->cpml.path.data[6] = line_to;
160 rdim->data = data;
163 static void
164 _adg_dispose(GObject *object)
166 AdgRDim *rdim = (AdgRDim *) object;
168 _adg_dispose_trail(rdim);
169 _adg_dispose_marker(rdim);
171 if (_ADG_OLD_OBJECT_CLASS->dispose)
172 _ADG_OLD_OBJECT_CLASS->dispose(object);
175 static void
176 _adg_get_property(GObject *object, guint prop_id,
177 GValue *value, GParamSpec *pspec)
179 switch (prop_id) {
180 case PROP_VALUE:
181 g_value_set_string(value, adg_dim_get_value((AdgDim *) object));
182 break;
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
185 break;
189 static void
190 _adg_set_property(GObject *object, guint prop_id,
191 const GValue *value, GParamSpec *pspec)
193 switch (prop_id) {
194 case PROP_VALUE:
195 adg_dim_set_value((AdgDim *) object, g_value_get_string(value));
196 break;
197 default:
198 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
199 break;
205 * adg_rdim_new:
207 * Creates a new uninitialized radial dimension. To be useful, you
208 * need at least define the center of the arc to quote in #AdgDim:ref1,
209 * a point on the arc in #AdgDim:ref2 and the position of the quote
210 * in #AdgDim:pos using any valid #AdgDim method.
212 * Returns: a newly created quote
214 * Since: 1.0
216 AdgRDim *
217 adg_rdim_new(void)
219 return g_object_new(ADG_TYPE_RDIM, NULL);
223 * adg_rdim_new_full:
224 * @center: allow-none: center of the arc to quote
225 * @radius: allow-none: where the quote must be applied on the arc
226 * @pos: allow-none: position of the quote text
228 * Creates a new quote by specifying explicitely all the needed
229 * data to get a valid quote.
231 * Returns: a newly created quote
233 * Since: 1.0
235 AdgRDim *
236 adg_rdim_new_full(const CpmlPair *center, const CpmlPair *radius,
237 const CpmlPair *pos)
239 AdgRDim *rdim;
240 AdgDim *dim;
242 rdim = adg_rdim_new();
243 dim = (AdgDim *) rdim;
245 if (center != NULL)
246 adg_dim_set_ref1_from_pair(dim, center);
248 if (radius != NULL)
249 adg_dim_set_ref2_from_pair(dim, radius);
251 if (pos != NULL)
252 adg_dim_set_pos_from_pair(dim, pos);
254 return rdim;
258 * adg_rdim_new_full_explicit:
259 * @center_x: x coordinate of the center of the arc to quote
260 * @center_y: y coordinate of the center of the arc to quote
261 * @radius_x: x coordiante where the quote must be applied on the arc
262 * @radius_y: y coordiante where the quote must be applied on the arc
263 * @pos_x: x coordinate of the quote text
264 * @pos_y: y coordinate of the quote text
266 * Does the same job of adg_rdim_full() but using specific coordinates
267 * instead of #CpmlPair structures.
268 * data to get a valid quote.
270 * Returns: a newly created quote
272 * Since: 1.0
274 AdgRDim *
275 adg_rdim_new_full_explicit(gdouble center_x, gdouble center_y,
276 gdouble radius_x, gdouble radius_y,
277 gdouble pos_x, gdouble pos_y)
279 CpmlPair center, radius, pos;
281 center.x = center_x;
282 center.y = center_y;
283 radius.x = radius_x;
284 radius.y = radius_y;
285 pos.x = pos_x;
286 pos.y = pos_y;
288 return adg_rdim_new_full(&center, &radius, &pos);
292 * adg_rdim_new_full_from_model:
293 * @model: transfer-none: the model from which the named pairs are taken
294 * @center: allow-none: the center point of the arc to quote
295 * @radius: allow-none: an arbitrary point on the arc
296 * @pos: allow-none: the position reference
298 * Creates a new radial dimension, specifing all the needed properties in
299 * one shot and using named pairs from @model.
301 * Returns: the newly created radial dimension entity
303 * Since: 1.0
305 AdgRDim *
306 adg_rdim_new_full_from_model(AdgModel *model, const gchar *center,
307 const gchar *radius, const gchar *pos)
309 AdgRDim *rdim;
310 AdgDim *dim;
312 g_return_val_if_fail(model != NULL, NULL);
314 rdim = adg_rdim_new();
315 dim = (AdgDim *) rdim;
317 if (center != NULL)
318 adg_dim_set_ref1_from_model(dim, model, center);
320 if (radius != NULL)
321 adg_dim_set_ref2_from_model(dim, model, radius);
323 if (pos != NULL)
324 adg_dim_set_pos_from_model(dim, model, pos);
326 return rdim;
330 static void
331 _adg_global_changed(AdgEntity *entity)
333 AdgRDimPrivate *data = ((AdgRDim *) entity)->data;
335 _adg_clear_trail((AdgRDim *) entity);
337 if (_ADG_OLD_ENTITY_CLASS->global_changed)
338 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
340 if (data->marker != NULL)
341 adg_entity_global_changed((AdgEntity *) data->marker);
344 static void
345 _adg_local_changed(AdgEntity *entity)
347 _adg_clear_trail((AdgRDim *) entity);
349 if (_ADG_OLD_ENTITY_CLASS->local_changed)
350 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
353 static void
354 _adg_invalidate(AdgEntity *entity)
356 AdgRDim *rdim;
357 AdgRDimPrivate *data;
359 rdim = (AdgRDim *) entity;
360 data = rdim->data;
362 _adg_dispose_trail(rdim);
363 _adg_dispose_marker(rdim);
364 data->geometry_arranged = FALSE;
365 _adg_clear_trail(rdim);
367 if (_ADG_OLD_ENTITY_CLASS->invalidate)
368 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
371 static void
372 _adg_arrange(AdgEntity *entity)
374 AdgRDim *rdim;
375 AdgDim *dim;
376 AdgRDimPrivate *data;
377 AdgAlignment *quote;
378 AdgEntity *quote_entity;
379 gboolean outside;
380 const cairo_matrix_t *global, *local;
381 CpmlPair ref2, base;
382 CpmlPair pair;
383 CpmlExtents extents;
385 if (_ADG_OLD_ENTITY_CLASS->arrange != NULL)
386 _ADG_OLD_ENTITY_CLASS->arrange(entity);
388 rdim = (AdgRDim *) entity;
389 dim = (AdgDim *) rdim;
390 data = rdim->data;
391 quote = adg_dim_get_quote(dim);
392 quote_entity = (AdgEntity *) quote;
394 if (!_adg_update_geometry(rdim))
395 return;
397 _adg_update_entities(rdim);
399 /* Check for cached result */
400 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
401 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
402 return;
405 outside = adg_dim_get_outside(dim);
406 if (outside == ADG_THREE_STATE_UNKNOWN)
407 outside = ADG_THREE_STATE_OFF;
409 global = adg_entity_get_global_matrix(entity);
410 local = adg_entity_get_local_matrix(entity);
411 extents.is_defined = FALSE;
413 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
414 cpml_pair_copy(&base, &data->point.base);
416 cpml_pair_transform(&ref2, local);
417 cpml_pair_transform(&base, local);
418 base.x += data->shift.base.x;
419 base.y += data->shift.base.y;
421 /* baseline start */
422 cpml_pair_to_cairo(&base, &data->cpml.data[1]);
424 /* baseline end */
425 cpml_pair_to_cairo(&ref2, &data->cpml.data[3]);
427 if (outside) {
428 AdgDimStyle *dim_style;
429 gdouble beyond;
430 CpmlVector vector;
432 dim_style = _ADG_GET_DIM_STYLE(dim);
433 beyond = adg_dim_style_get_beyond(dim_style);
434 vector.x = ref2.x - base.x;
435 vector.y = ref2.y - base.y;
436 cpml_vector_set_length(&vector, beyond);
437 pair.x = ref2.x + vector.x;
438 pair.y = ref2.y + vector.y;
440 data->cpml.data[2].header.length = 2;
442 /* Outside segment start */
443 cpml_pair_to_cairo(&pair, &data->cpml.data[5]);
445 /* Outside segment end */
446 cpml_pair_to_cairo(&ref2, &data->cpml.data[7]);
447 } else {
448 data->cpml.data[2].header.length = 6;
451 /* Arrange the quote */
452 if (quote != NULL) {
453 cairo_matrix_t map;
455 adg_alignment_set_factor_explicit(quote, 1, 0);
457 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
458 cairo_matrix_init_translate(&map, pair.x, pair.y);
459 cairo_matrix_rotate(&map, data->angle);
460 adg_entity_set_global_map(quote_entity, &map);
461 adg_entity_arrange(quote_entity);
462 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
464 adg_matrix_copy(&data->quote.global_map, &map);
467 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
469 /* Arrange the trail */
470 if (data->trail != NULL) {
471 CpmlExtents trail_extents;
472 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
473 cpml_extents_transform(&trail_extents, global);
474 cpml_extents_add(&extents, &trail_extents);
475 } else {
476 _adg_dispose_marker(rdim);
479 /* Arrange the marker */
480 if (data->marker != NULL) {
481 AdgEntity *marker_entity = (AdgEntity *) data->marker;
482 adg_marker_set_segment(data->marker, data->trail, outside ? 2 : 1);
483 adg_entity_local_changed(marker_entity);
484 adg_entity_arrange(marker_entity);
485 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
488 adg_entity_set_extents(entity, &extents);
491 static void
492 _adg_render(AdgEntity *entity, cairo_t *cr)
494 AdgRDim *rdim;
495 AdgDim *dim;
496 AdgRDimPrivate *data;
497 AdgDimStyle *dim_style;
498 AdgDress dress;
499 const cairo_path_t *cairo_path;
501 rdim = (AdgRDim *) entity;
502 data = rdim->data;
504 if (!data->geometry_arranged) {
505 /* Entity not arranged, probably due to undefined pair found */
506 return;
509 dim = (AdgDim *) entity;
510 dim_style = _ADG_GET_DIM_STYLE(dim);
512 adg_style_apply((AdgStyle *) dim_style, entity, cr);
513 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
515 if (data->marker != NULL)
516 adg_entity_render((AdgEntity *) data->marker, cr);
518 cairo_transform(cr, adg_entity_get_global_matrix(entity));
519 dress = adg_dim_style_get_line_dress(dim_style);
520 adg_entity_apply_dress(entity, dress, cr);
522 cairo_path = adg_trail_get_cairo_path(data->trail);
523 cairo_append_path(cr, cairo_path);
524 cairo_stroke(cr);
527 static gchar *
528 _adg_default_value(AdgDim *dim)
530 AdgRDim *rdim;
531 AdgRDimPrivate *data;
532 AdgDimStyle *dim_style;
533 const gchar *format;
535 rdim = (AdgRDim *) dim;
536 data = rdim->data;
537 dim_style = _ADG_GET_DIM_STYLE(dim);
538 format = adg_dim_style_get_number_format(dim_style);
540 if (!_adg_update_geometry(rdim))
541 return g_strdup("undef");
543 return g_strdup_printf(format, data->radius);
546 static gboolean
547 _adg_update_geometry(AdgRDim *rdim)
549 AdgRDimPrivate *data;
550 AdgDim *dim;
551 AdgDimStyle *dim_style;
552 const CpmlPair *ref1, *ref2;
553 const CpmlPair *pos;
554 gdouble spacing, level, pos_distance;
555 CpmlVector vector;
557 data = rdim->data;
559 if (data->geometry_arranged)
560 return TRUE;
562 dim = (AdgDim *) rdim;
563 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
564 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
565 pos = adg_point_get_pair(adg_dim_get_pos(dim));
567 if (ref1 == NULL || ref2 == NULL || pos == NULL)
568 return FALSE;
570 dim_style = _ADG_GET_DIM_STYLE(rdim);
571 spacing = adg_dim_style_get_baseline_spacing(dim_style);
572 level = adg_dim_get_level(dim);
573 pos_distance = cpml_pair_distance(pos, ref1);
574 vector.x = ref2->x - ref1->x;
575 vector.y = ref2->y - ref1->y;
577 if (cpml_pair_squared_distance(pos, ref1) <
578 cpml_pair_squared_distance(pos, ref2)) {
579 vector.x = -vector.x;
580 vector.y = -vector.y;
583 /* radius */
584 data->radius = cpml_pair_distance(&vector, NULL);
586 /* angle */
587 data->angle = adg_dim_quote_angle(dim, cpml_vector_angle(&vector));
589 /* point.base */
590 cpml_pair_copy(&data->point.base, &vector);
591 cpml_vector_set_length(&data->point.base, pos_distance);
592 data->point.base.x += ref1->x;
593 data->point.base.y += ref1->y;
595 /* shift.base */
596 cpml_pair_copy(&data->shift.base, &vector);
597 cpml_vector_set_length(&data->shift.base, spacing * level);
599 data->geometry_arranged = TRUE;
601 return TRUE;
604 static void
605 _adg_update_entities(AdgRDim *rdim)
607 AdgEntity *entity;
608 AdgRDimPrivate *data;
609 AdgDimStyle *dim_style;
611 entity = (AdgEntity *) rdim;
612 data = rdim->data;
613 dim_style = _ADG_GET_DIM_STYLE(rdim);
615 if (data->trail == NULL)
616 data->trail = adg_trail_new(_adg_trail_callback, rdim);
618 if (data->marker == NULL) {
619 data->marker = adg_dim_style_marker2_new(dim_style);
620 adg_entity_set_parent((AdgEntity *) data->marker, entity);
624 static void
625 _adg_clear_trail(AdgRDim *rdim)
627 AdgRDimPrivate *data = rdim->data;
629 if (data->trail != NULL)
630 adg_model_clear((AdgModel *) data->trail);
632 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
635 static void
636 _adg_dispose_trail(AdgRDim *rdim)
638 AdgRDimPrivate *data = rdim->data;
640 if (data->trail != NULL) {
641 g_object_unref(data->trail);
642 data->trail = NULL;
646 static void
647 _adg_dispose_marker(AdgRDim *rdim)
649 AdgRDimPrivate *data = rdim->data;
651 if (data->marker != NULL) {
652 g_object_unref(data->marker);
653 data->marker = NULL;
657 static CpmlPath *
658 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
660 AdgRDim *rdim;
661 AdgRDimPrivate *data;
663 rdim = (AdgRDim *) user_data;
664 data = rdim->data;
666 return &data->cpml.path;