cpml: removed CpmlPath
[adg.git] / src / adg / adg-rdim.c
blobe9ff63bff00d41db2342cb9169053e5c067458f0
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 cairo_path_t * _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->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
153 data->cairo.path.data = data->cairo.data;
154 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
155 data->cairo.path.data[0] = move_to;
156 data->cairo.path.data[2] = line_to;
157 data->cairo.path.data[4] = move_to;
158 data->cairo.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->cairo.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, (CpmlPair *) 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->cairo.data[1]);
424 /* baseline end */
425 cpml_pair_to_cairo(&ref2, &data->cairo.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->cairo.data[2].header.length = 2;
442 /* Outside segment start */
443 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
445 /* Outside segment end */
446 cpml_pair_to_cairo(&ref2, &data->cairo.data[7]);
447 } else {
448 data->cairo.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->cairo.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->cairo.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 AdgPoint *ref1_point, *ref2_point, *pos_point;
553 const CpmlPair *ref1, *ref2, *pos;
554 gdouble spacing, level, pos_distance;
555 CpmlVector vector;
557 data = rdim->data;
559 /* Check for cached results */
560 if (data->geometry_arranged)
561 return TRUE;
563 dim = (AdgDim *) rdim;
564 ref1_point = adg_dim_get_ref1(dim);
565 ref2_point = adg_dim_get_ref2(dim);
566 pos_point = adg_dim_get_pos(dim);
568 /* Check if the needed points are all properly defined */
569 if (! adg_point_update(ref1_point) ||
570 ! adg_point_update(ref2_point) ||
571 ! adg_point_update(pos_point))
572 return FALSE;
574 ref1 = (CpmlPair *) ref1_point;
575 ref2 = (CpmlPair *) ref2_point;
576 pos = (CpmlPair *) pos_point;
578 /* Check if the given points have valid coordinates */
579 if (cpml_pair_equal(ref1, ref2)) {
580 g_warning(_("%s: ref1 and ref2 cannot be coincidents (%lf, %lf)"),
581 G_STRLOC, ref1->x, ref1->y);
582 return FALSE;
585 dim_style = _ADG_GET_DIM_STYLE(rdim);
586 spacing = adg_dim_style_get_baseline_spacing(dim_style);
587 level = adg_dim_get_level(dim);
588 pos_distance = cpml_pair_distance(pos, ref1);
589 vector.x = ref2->x - ref1->x;
590 vector.y = ref2->y - ref1->y;
592 if (cpml_pair_squared_distance(pos, ref1) <
593 cpml_pair_squared_distance(pos, ref2)) {
594 vector.x = -vector.x;
595 vector.y = -vector.y;
598 /* radius */
599 data->radius = cpml_pair_distance(&vector, NULL);
601 /* angle */
602 data->angle = adg_dim_quote_angle(dim, cpml_vector_angle(&vector));
604 /* point.base */
605 cpml_pair_copy(&data->point.base, &vector);
606 cpml_vector_set_length(&data->point.base, pos_distance);
607 data->point.base.x += ref1->x;
608 data->point.base.y += ref1->y;
610 /* shift.base */
611 cpml_pair_copy(&data->shift.base, &vector);
612 cpml_vector_set_length(&data->shift.base, spacing * level);
614 data->geometry_arranged = TRUE;
616 return TRUE;
619 static void
620 _adg_update_entities(AdgRDim *rdim)
622 AdgEntity *entity;
623 AdgRDimPrivate *data;
624 AdgDimStyle *dim_style;
626 entity = (AdgEntity *) rdim;
627 data = rdim->data;
628 dim_style = _ADG_GET_DIM_STYLE(rdim);
630 if (data->trail == NULL)
631 data->trail = adg_trail_new(_adg_trail_callback, rdim);
633 if (data->marker == NULL) {
634 data->marker = adg_dim_style_marker2_new(dim_style);
635 adg_entity_set_parent((AdgEntity *) data->marker, entity);
639 static void
640 _adg_clear_trail(AdgRDim *rdim)
642 AdgRDimPrivate *data = rdim->data;
644 if (data->trail != NULL)
645 adg_model_clear((AdgModel *) data->trail);
647 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
650 static void
651 _adg_dispose_trail(AdgRDim *rdim)
653 AdgRDimPrivate *data = rdim->data;
655 if (data->trail != NULL) {
656 g_object_unref(data->trail);
657 data->trail = NULL;
661 static void
662 _adg_dispose_marker(AdgRDim *rdim)
664 AdgRDimPrivate *data = rdim->data;
666 if (data->marker != NULL) {
667 g_object_unref(data->marker);
668 data->marker = NULL;
672 static cairo_path_t *
673 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
675 AdgRDim *rdim;
676 AdgRDimPrivate *data;
678 rdim = (AdgRDim *) user_data;
679 data = rdim->data;
681 return &data->cairo.path;