build: depends on cairo-gobject if introspection is enabled
[adg.git] / src / adg / adg-ldim.c
blobd619ff5162a5eaac45df06a567e6dafcdda2843b
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-ldim
23 * @short_description: Linear dimensions
25 * The #AdgLDim entity represents a linear dimension.
27 * Since: 1.0
28 **/
30 /**
31 * AdgLDim:
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"
54 #include "adg-ldim.h"
55 #include "adg-ldim-private.h"
58 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_ldim_parent_class)
59 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
62 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM)
64 enum {
65 PROP_0,
66 PROP_DIRECTION,
67 PROP_HAS_EXTENSION1,
68 PROP_HAS_EXTENSION2
72 static void _adg_dispose (GObject *object);
73 static void _adg_get_property (GObject *object,
74 guint param_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void _adg_set_property (GObject *object,
78 guint param_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void _adg_global_changed (AdgEntity *entity);
82 static void _adg_local_changed (AdgEntity *entity);
83 static void _adg_invalidate (AdgEntity *entity);
84 static void _adg_arrange (AdgEntity *entity);
85 static void _adg_render (AdgEntity *entity,
86 cairo_t *cr);
87 static gchar * _adg_default_value (AdgDim *dim);
88 static gboolean _adg_update_geometry (AdgLDim *ldim);
89 static void _adg_update_shift (AdgLDim *ldim);
90 static void _adg_update_entities (AdgLDim *ldim);
91 static void _adg_choose_flags (AdgLDim *ldim,
92 gboolean *to_outside,
93 gboolean *to_detach);
94 static void _adg_update_quote (AdgLDim *ldim,
95 CpmlPair *base1,
96 CpmlPair *base2,
97 CpmlPair *pos,
98 gboolean detach,
99 gboolean outside,
100 gdouble gap);
101 static void _adg_update_extents (AdgLDim *ldim);
102 static void _adg_unset_trail (AdgLDim *ldim);
103 static void _adg_dispose_trail (AdgLDim *ldim);
104 static void _adg_dispose_markers (AdgLDim *ldim);
105 static cairo_path_t * _adg_trail_callback (AdgTrail *trail,
106 gpointer user_data);
109 static void
110 adg_ldim_class_init(AdgLDimClass *klass)
112 GObjectClass *gobject_class;
113 AdgEntityClass *entity_class;
114 AdgDimClass *dim_class;
115 GParamSpec *param;
117 gobject_class = (GObjectClass *) klass;
118 entity_class = (AdgEntityClass *) klass;
119 dim_class = (AdgDimClass *) klass;
121 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
123 gobject_class->dispose = _adg_dispose;
124 gobject_class->get_property = _adg_get_property;
125 gobject_class->set_property = _adg_set_property;
127 entity_class->global_changed = _adg_global_changed;
128 entity_class->local_changed = _adg_local_changed;
129 entity_class->invalidate = _adg_invalidate;
130 entity_class->arrange = _adg_arrange;
131 entity_class->render = _adg_render;
133 dim_class->default_value = _adg_default_value;
135 param = g_param_spec_double("direction",
136 P_("Direction"),
137 P_("The inclination angle of the extension lines"),
138 -G_PI, G_PI, ADG_DIR_RIGHT,
139 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
140 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
142 param = g_param_spec_boolean("has-extension1",
143 P_("Has First Extension Line flag"),
144 P_("Show (TRUE) or hide (FALSE) the first extension line"),
145 TRUE, G_PARAM_READWRITE);
146 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
148 param = g_param_spec_boolean("has-extension2",
149 P_("Has Second Extension Line flag"),
150 P_("Show (TRUE) or hide (FALSE) the second extension line"),
151 TRUE, G_PARAM_READWRITE);
152 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
155 static void
156 adg_ldim_init(AdgLDim *ldim)
158 AdgLDimPrivate *data;
159 cairo_path_data_t move_to, line_to;
161 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
162 move_to.header.type = CPML_MOVE;
163 move_to.header.length = 2;
164 line_to.header.type = CPML_LINE;
165 line_to.header.length = 2;
167 data->direction = ADG_DIR_RIGHT;
168 data->has_extension1 = TRUE;
169 data->has_extension2 = TRUE;
171 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
172 data->cairo.path.data = data->cairo.data;
173 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
174 data->cairo.path.data[0] = move_to;
175 data->cairo.path.data[2] = line_to;
176 data->cairo.path.data[4] = move_to;
177 data->cairo.path.data[6] = line_to;
178 data->cairo.path.data[8] = move_to;
179 data->cairo.path.data[10] = line_to;
180 data->cairo.path.data[12] = move_to;
181 data->cairo.path.data[14] = line_to;
182 data->cairo.path.data[16] = move_to;
183 data->cairo.path.data[18] = line_to;
185 data->trail = NULL;
186 data->marker1 = NULL;
187 data->marker2 = NULL;
189 data->geometry.is_arranged = FALSE;
190 data->shift.is_arranged = FALSE;
192 ldim->data = data;
195 static void
196 _adg_dispose(GObject *object)
198 AdgLDim *ldim = (AdgLDim *) object;
200 _adg_dispose_trail(ldim);
201 _adg_dispose_markers(ldim);
203 if (_ADG_OLD_OBJECT_CLASS->dispose)
204 _ADG_OLD_OBJECT_CLASS->dispose(object);
207 static void
208 _adg_get_property(GObject *object, guint prop_id,
209 GValue *value, GParamSpec *pspec)
211 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
213 switch (prop_id) {
214 case PROP_DIRECTION:
215 g_value_set_double(value, data->direction);
216 break;
217 case PROP_HAS_EXTENSION1:
218 g_value_set_boolean(value, data->has_extension1);
219 break;
220 case PROP_HAS_EXTENSION2:
221 g_value_set_boolean(value, data->has_extension2);
222 break;
223 default:
224 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
225 break;
229 static void
230 _adg_set_property(GObject *object, guint prop_id,
231 const GValue *value, GParamSpec *pspec)
233 AdgLDim *ldim;
234 AdgLDimPrivate *data;
236 ldim = (AdgLDim *) object;
237 data = ldim->data;
239 switch (prop_id) {
240 case PROP_DIRECTION:
241 data->direction = cpml_angle(g_value_get_double(value));
242 break;
243 case PROP_HAS_EXTENSION1:
244 data->has_extension1 = g_value_get_boolean(value);
245 break;
246 case PROP_HAS_EXTENSION2:
247 data->has_extension2 = g_value_get_boolean(value);
248 break;
249 default:
250 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
251 break;
257 * adg_ldim_new:
259 * Creates a new - undefined - linear dimension. You must, at least,
260 * define the start of the dimension in #AdgDim:ref1, the end in
261 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
262 * any valid #AdgDim method. The director of the dimension (that is,
263 * if it is horizontal, vertical or oblique at a specific angle)
264 * should be specified with adg_ldim_set_direction().
266 * Returns: the newly created linear dimension entity
268 * Since: 1.0
270 AdgLDim *
271 adg_ldim_new(void)
273 return g_object_new(ADG_TYPE_LDIM, NULL);
277 * adg_ldim_new_full:
278 * @ref1: (allow-none): the first reference point
279 * @ref2: (allow-none): the second reference point
280 * @pos: (allow-none): the position reference
281 * @direction: angle where to extend the dimension
283 * Creates a new linear dimension, specifing all the needed properties in
284 * one shot.
286 * Returns: the newly created linear dimension entity
288 * Since: 1.0
290 AdgLDim *
291 adg_ldim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
292 const CpmlPair *pos, gdouble direction)
294 AdgLDim *ldim;
295 AdgDim *dim;
297 ldim = adg_ldim_new();
298 dim = (AdgDim *) ldim;
300 if (ref1 != NULL)
301 adg_dim_set_ref1_from_pair(dim, ref1);
303 if (ref2 != NULL)
304 adg_dim_set_ref2_from_pair(dim, ref2);
306 if (pos != NULL)
307 adg_dim_set_pos_from_pair(dim, pos);
309 adg_ldim_set_direction(ldim, direction);
310 return ldim;
314 * adg_ldim_new_full_explicit:
315 * @ref1_x: the x coordinate of the first reference point
316 * @ref1_y: the y coordinate of the first reference point
317 * @ref2_x: the x coordinate of the second reference point
318 * @ref2_y: the y coordinate of the second reference point
319 * @pos_x: the x coordinate of the position reference
320 * @pos_y: the y coordinate of the position reference
321 * @direction: angle where to extend the dimension
323 * Wrappes adg_ldim_new_full() with explicit values.
325 * Returns: the newly created linear dimension entity
327 * Since: 1.0
329 AdgLDim *
330 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
331 gdouble ref2_x, gdouble ref2_y,
332 gdouble pos_x, gdouble pos_y, gdouble direction)
334 CpmlPair ref1;
335 CpmlPair ref2;
336 CpmlPair pos;
338 ref1.x = ref1_x;
339 ref1.y = ref1_y;
340 ref2.x = ref2_x;
341 ref2.y = ref2_y;
342 pos.x = pos_x;
343 pos.y = pos_y;
345 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
349 * adg_ldim_new_full_from_model:
350 * @model: (transfer none): the model from which the named pairs are taken
351 * @ref1: (allow-none): the first reference point
352 * @ref2: (allow-none): the second reference point
353 * @pos: (allow-none): the position reference
354 * @direction: angle where to extend the dimension
356 * Creates a new linear dimension, specifing all the needed properties in
357 * one shot and using named pairs from @model.
359 * Returns: the newly created linear dimension entity
361 * Since: 1.0
363 AdgLDim *
364 adg_ldim_new_full_from_model(AdgModel *model,
365 const gchar *ref1, const gchar *ref2,
366 const gchar *pos, gdouble direction)
368 AdgLDim *ldim;
369 AdgDim *dim;
371 g_return_val_if_fail(model != NULL, NULL);
373 ldim = adg_ldim_new();
374 dim = (AdgDim *) ldim;
376 if (ref1 != NULL)
377 adg_dim_set_ref1_from_model(dim, model, ref1);
379 if (ref2 != NULL)
380 adg_dim_set_ref2_from_model(dim, model, ref2);
382 if (pos != NULL)
383 adg_dim_set_pos_from_model(dim, model, pos);
385 adg_ldim_set_direction(ldim, direction);
386 return (AdgLDim *) dim;
390 * adg_ldim_set_direction:
391 * @ldim: an #AdgLDim entity
392 * @direction: an angle value, in radians
394 * Sets the direction angle where to extend @ldim.
395 * @direction is normalized by cpml_angle() before being used.
397 * Since: 1.0
399 void
400 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
402 g_return_if_fail(ADG_IS_LDIM(ldim));
403 g_object_set(ldim, "direction", direction, NULL);
407 * adg_ldim_get_direction:
408 * @ldim: an #AdgLDim entity
410 * Gets the direction where @ldim will extend.
412 * Returns: the direction angle in radians
414 * Since: 1.0
416 gdouble
417 adg_ldim_get_direction(AdgLDim *ldim)
419 AdgLDimPrivate *data;
421 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
423 data = ldim->data;
425 return data->direction;
429 * adg_ldim_switch_extension1:
430 * @ldim: an #AdgLDim entity
431 * @new_state: the new state
433 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
434 * the first extension line of @ldim.
436 * Since: 1.0
438 void
439 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
441 g_return_if_fail(ADG_IS_LDIM(ldim));
442 g_return_if_fail(adg_is_boolean_value(new_state));
443 g_object_set(ldim, "has-extension1", new_state, NULL);
447 * adg_ldim_has_extension1:
448 * @ldim: an #AdgLDim entity
450 * Checks if @ldim should render also the first extension line.
452 * Returns: %TRUE on first extension line presents, %FALSE otherwise
454 * Since: 1.0
456 gboolean
457 adg_ldim_has_extension1(AdgLDim *ldim)
459 AdgLDimPrivate *data;
461 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
463 data = ldim->data;
465 return data->has_extension1;
469 * adg_ldim_switch_extension2:
470 * @ldim: an #AdgLDim entity
471 * @new_state: the new new_state
473 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
474 * the second extension line of @ldim.
476 * Since: 1.0
478 void
479 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
481 g_return_if_fail(ADG_IS_LDIM(ldim));
482 g_return_if_fail(adg_is_boolean_value(new_state));
483 g_object_set(ldim, "has-extension2", new_state, NULL);
487 * adg_ldim_has_extension2:
488 * @ldim: an #AdgLDim entity
490 * Checks if @ldim should render also the second extension line.
492 * Returns: %TRUE on first extension line presents, %FALSE otherwise
494 * Since: 1.0
496 gboolean
497 adg_ldim_has_extension2(AdgLDim *ldim)
499 AdgLDimPrivate *data;
501 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
503 data = ldim->data;
505 return data->has_extension2;
509 static void
510 _adg_global_changed(AdgEntity *entity)
512 AdgLDimPrivate *data = ((AdgLDim *) entity)->data;
514 _adg_unset_trail((AdgLDim *) entity);
516 if (_ADG_OLD_ENTITY_CLASS->global_changed)
517 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
519 if (data->marker1)
520 adg_entity_global_changed((AdgEntity *) data->marker1);
522 if (data->marker2)
523 adg_entity_global_changed((AdgEntity *) data->marker2);
526 static void
527 _adg_local_changed(AdgEntity *entity)
529 _adg_unset_trail((AdgLDim *) entity);
531 if (_ADG_OLD_ENTITY_CLASS->local_changed)
532 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
535 static void
536 _adg_invalidate(AdgEntity *entity)
538 AdgLDim *ldim;
539 AdgLDimPrivate *data;
541 ldim = (AdgLDim *) entity;
542 data = ldim->data;
544 _adg_dispose_trail(ldim);
545 _adg_dispose_markers(ldim);
546 data->geometry.is_arranged = FALSE;
547 data->shift.is_arranged = FALSE;
548 _adg_unset_trail(ldim);
550 if (_ADG_OLD_ENTITY_CLASS->invalidate)
551 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
554 static void
555 _adg_arrange(AdgEntity *entity)
557 AdgLDim *ldim;
558 AdgDim *dim;
559 AdgLDimPrivate *data;
560 AdgAlignment *quote;
561 AdgDimStyle *dim_style;
562 gboolean outside, detach;
563 const cairo_matrix_t *local;
564 CpmlPair ref1, ref2, pos, base1, base2;
565 CpmlPair pair;
567 if (_ADG_OLD_ENTITY_CLASS->arrange)
568 _ADG_OLD_ENTITY_CLASS->arrange(entity);
570 ldim = (AdgLDim *) entity;
571 if (! _adg_update_geometry(ldim))
572 return;
574 dim = (AdgDim *) ldim;
575 data = ldim->data;
576 quote = adg_dim_get_quote(dim);
578 _adg_update_shift(ldim);
579 _adg_update_entities(ldim);
581 /* Check for cached result */
582 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
583 adg_entity_set_global_map((AdgEntity *) quote, &data->quote.global_map);
584 return;
587 _adg_choose_flags(ldim, &outside, &detach);
589 dim_style = _ADG_GET_DIM_STYLE(dim);
590 local = adg_entity_get_local_matrix(entity);
592 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
593 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
594 cpml_pair_copy(&pos, (CpmlPair *) adg_dim_get_pos(dim));
595 cpml_pair_copy(&base1, &data->geometry.base1);
596 cpml_pair_copy(&base2, &data->geometry.base2);
598 cpml_pair_transform(&ref1, local);
599 cpml_pair_transform(&ref2, local);
600 cpml_pair_transform(&pos, local);
601 cpml_pair_transform(&base1, local);
602 base1.x += data->shift.base.x;
603 base1.y += data->shift.base.y;
604 cpml_pair_transform(&base2, local);
605 base2.x += data->shift.base.x;
606 base2.y += data->shift.base.y;
608 pair.x = ref1.x + data->shift.from.x;
609 pair.y = ref1.y + data->shift.from.y;
610 cpml_pair_to_cairo(&pair, &data->cairo.data[13]);
612 cpml_pair_to_cairo(&base1, &data->cairo.data[1]);
614 pair.x = base1.x + data->shift.to.x;
615 pair.y = base1.y + data->shift.to.y;
616 cpml_pair_to_cairo(&pair, &data->cairo.data[15]);
618 pair.x = ref2.x + data->shift.from.x;
619 pair.y = ref2.y + data->shift.from.y;
620 cpml_pair_to_cairo(&pair, &data->cairo.data[17]);
622 cpml_pair_to_cairo(&base2, &data->cairo.data[3]);
624 pair.x = base2.x + data->shift.to.x;
625 pair.y = base2.y + data->shift.to.y;
626 cpml_pair_to_cairo(&pair, &data->cairo.data[19]);
628 /* Calculate the outside segments */
629 if (outside) {
630 gdouble beyond;
631 CpmlVector vector;
633 beyond = adg_dim_style_get_beyond(dim_style);
634 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
636 cpml_pair_from_cairo(&vector, &data->cairo.data[3]);
637 vector.x -= pair.x;
638 vector.y -= pair.y;
639 cpml_vector_set_length(&vector, beyond);
641 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
642 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
644 pair.x -= vector.x;
645 pair.y -= vector.y;
646 cpml_pair_to_cairo(&pair, &data->cairo.data[7]);
648 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
649 cpml_pair_to_cairo(&pair, &data->cairo.data[11]);
651 pair.x += vector.x;
652 pair.y += vector.y;
653 cpml_pair_to_cairo(&pair, &data->cairo.data[9]);
655 data->cairo.data[2].header.length = 2;
656 } else {
657 data->cairo.data[2].header.length = 10;
659 data->cairo.data[10].header.length = 2;
661 _adg_update_quote(ldim, &base1, &base2, &pos, detach, outside,
662 adg_dim_style_get_quote_shift(dim_style)->x);
664 /* Play with header lengths to show or hide the extension lines */
665 if (data->has_extension1) {
666 data->cairo.data[14].header.length = data->has_extension2 ? 2 : 6;
667 } else {
668 gint n = ! outside && ! detach ? 2 : 10;
669 data->cairo.data[14].header.length = 2;
670 data->cairo.data[n].header.length += 4;
671 if (!data->has_extension2)
672 data->cairo.data[n].header.length += 4;
675 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
677 /* Update the markers, if present */
678 if (data->trail != NULL) {
679 if (data->marker1 != NULL)
680 adg_marker_set_segment(data->marker1, data->trail, outside ? 2 : 1);
681 if (data->marker2 != NULL)
682 adg_marker_set_segment(data->marker2, data->trail, outside ? 3 : 1);
683 } else {
684 _adg_dispose_markers(ldim);
687 _adg_update_extents(ldim);
690 static void
691 _adg_render(AdgEntity *entity, cairo_t *cr)
693 AdgLDim *ldim;
694 AdgDim *dim;
695 AdgLDimPrivate *data;
696 AdgDimStyle *dim_style;
697 AdgDress dress;
698 const cairo_path_t *cairo_path;
700 ldim = (AdgLDim *) entity;
701 data = ldim->data;
703 if (!data->geometry.is_arranged) {
704 /* Entity not arranged, probably due to undefined pair found */
705 return;
708 dim = (AdgDim *) entity;
709 dim_style = _ADG_GET_DIM_STYLE(dim);
711 adg_style_apply((AdgStyle *) dim_style, entity, cr);
712 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
714 if (data->marker1)
715 adg_entity_render((AdgEntity *) data->marker1, cr);
716 if (data->marker2)
717 adg_entity_render((AdgEntity *) data->marker2, cr);
719 cairo_transform(cr, adg_entity_get_global_matrix(entity));
720 dress = adg_dim_style_get_line_dress(dim_style);
721 adg_entity_apply_dress(entity, dress, cr);
723 cairo_path = adg_trail_get_cairo_path(data->trail);
724 cairo_append_path(cr, cairo_path);
725 cairo_stroke(cr);
728 static gchar *
729 _adg_default_value(AdgDim *dim)
731 AdgLDim *ldim;
732 AdgLDimPrivate *data;
733 AdgDimStyle *dim_style;
734 const gchar *format;
736 ldim = (AdgLDim *) dim;
737 data = ldim->data;
738 dim_style = _ADG_GET_DIM_STYLE(dim);
739 format = adg_dim_style_get_number_format(dim_style);
741 if (! _adg_update_geometry(ldim))
742 return g_strdup("undef");
744 return g_strdup_printf(format, data->geometry.distance);
747 static gboolean
748 _adg_update_geometry(AdgLDim *ldim)
750 AdgLDimPrivate *data;
751 AdgDim *dim;
752 AdgPoint *ref1_point, *ref2_point, *pos_point;
753 const CpmlPair *ref1, *ref2, *pos;
754 CpmlVector baseline, extension;
755 gdouble d, k;
757 data = ldim->data;
759 /* Check for cached results */
760 if (data->geometry.is_arranged)
761 return TRUE;
763 dim = (AdgDim *) ldim;
764 ref1_point = adg_dim_get_ref1(dim);
765 ref2_point = adg_dim_get_ref2(dim);
766 pos_point = adg_dim_get_pos(dim);
768 /* Check if the needed points are all defined */
769 if (! adg_point_update(ref1_point) ||
770 ! adg_point_update(ref2_point) ||
771 ! adg_point_update(pos_point))
772 return FALSE;
774 ref1 = (CpmlPair *) ref1_point;
775 ref2 = (CpmlPair *) ref2_point;
776 pos = (CpmlPair *) pos_point;
778 /* Check if the given points have valid coordinates */
779 if (cpml_pair_equal(ref1, ref2)) {
780 g_warning(_("%s: ref1 and ref2 cannot be coincidents (%lf, %lf)"),
781 G_STRLOC, ref1->x, ref1->y);
782 return FALSE;
785 cpml_vector_from_angle(&extension, data->direction);
786 cpml_pair_copy(&baseline, &extension);
787 cpml_vector_normal(&baseline);
789 d = extension.y * baseline.x -
790 extension.x * baseline.y;
791 g_return_val_if_fail(d != 0, FALSE);
793 k = ((pos->y - ref1->y) * baseline.x -
794 (pos->x - ref1->x) * baseline.y) / d;
795 data->geometry.base1.x = ref1->x + k * extension.x;
796 data->geometry.base1.y = ref1->y + k * extension.y;
798 k = ((pos->y - ref2->y) * baseline.x -
799 (pos->x - ref2->x) * baseline.y) / d;
800 data->geometry.base2.x = ref2->x + k * extension.x;
801 data->geometry.base2.y = ref2->y + k * extension.y;
803 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
804 &data->geometry.base2);
806 data->geometry.is_arranged = TRUE;
808 return TRUE;
811 static void
812 _adg_update_shift(AdgLDim *ldim)
814 AdgLDimPrivate *data;
815 AdgDimStyle *dim_style;
816 gdouble from_offset, to_offset;
817 gdouble baseline_spacing, level;
818 CpmlVector vector;
820 data = ldim->data;
822 if (data->shift.is_arranged)
823 return;
825 dim_style = _ADG_GET_DIM_STYLE(ldim);
826 from_offset = adg_dim_style_get_from_offset(dim_style);
827 to_offset = adg_dim_style_get_to_offset(dim_style);
828 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
829 level = adg_dim_get_level((AdgDim *) ldim);
831 cpml_vector_from_angle(&vector, data->direction);
833 cpml_vector_set_length(&vector, from_offset);
834 cpml_pair_copy(&data->shift.from, &vector);
836 cpml_vector_set_length(&vector, to_offset);
837 cpml_pair_copy(&data->shift.to, &vector);
839 cpml_vector_set_length(&vector, level * baseline_spacing);
840 cpml_pair_copy(&data->shift.base, &vector);
842 data->shift.is_arranged = TRUE;
845 static void
846 _adg_update_entities(AdgLDim *ldim)
848 AdgEntity *entity;
849 AdgLDimPrivate *data;
850 AdgDimStyle *dim_style;
852 entity = (AdgEntity *) ldim;
853 data = ldim->data;
854 dim_style = _ADG_GET_DIM_STYLE(ldim);
856 if (data->trail == NULL)
857 data->trail = adg_trail_new(_adg_trail_callback, ldim);
859 if (data->marker1 == NULL) {
860 data->marker1 = adg_dim_style_marker1_new(dim_style);
861 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
864 if (data->marker2 == NULL) {
865 data->marker2 = adg_dim_style_marker2_new(dim_style);
866 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
870 static void
871 _adg_choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
873 AdgDim *dim;
874 AdgThreeState outside, detached;
875 AdgLDimPrivate *data;
876 const cairo_matrix_t *local, *global;
877 gdouble local_factor, global_factor;
878 gdouble available_space, markers_space, quote_space;
880 dim = (AdgDim *) ldim;
881 outside = adg_dim_get_outside(dim);
882 detached = adg_dim_get_detached(dim);
884 *to_outside = outside == ADG_THREE_STATE_ON;
885 *to_detach = detached == ADG_THREE_STATE_ON;
887 /* On explicit flags, no further investigation is required */
888 if (outside != ADG_THREE_STATE_UNKNOWN &&
889 detached != ADG_THREE_STATE_UNKNOWN)
890 return;
892 data = ldim->data;
893 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
894 global = adg_entity_get_global_matrix((AdgEntity *) ldim);
895 local_factor = (local->xx + local->yy) / 2;
896 global_factor = (global->xx + global->yy) / 2;
897 available_space = data->geometry.distance * local_factor * global_factor;
899 markers_space = 0;
900 if (outside != ADG_THREE_STATE_ON) {
901 if (data->marker1 != NULL)
902 markers_space += adg_marker_get_size(data->marker1);
903 if (data->marker2 != NULL)
904 markers_space += adg_marker_get_size(data->marker2);
906 markers_space *= global_factor;
909 if (detached == ADG_THREE_STATE_ON) {
910 /* Leave at least 0.25 markers_space between the markers */
911 quote_space = markers_space * 0.25;
912 } else {
913 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote(dim);
914 adg_entity_arrange(quote);
915 quote_space = adg_entity_get_extents(quote)->size.x;
918 if (outside == ADG_THREE_STATE_UNKNOWN &&
919 detached == ADG_THREE_STATE_UNKNOWN) {
920 /* Both flags need to be choosed */
921 if (quote_space + markers_space < available_space) {
922 *to_detach = FALSE;
923 *to_outside = FALSE;
924 } else if (quote_space < available_space) {
925 *to_detach = FALSE;
926 *to_outside = TRUE;
927 } else {
928 *to_detach = TRUE;
929 *to_outside = markers_space * 1.25 > available_space;
931 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
932 /* Only the outside flag may be guessed */
933 *to_outside = quote_space + markers_space > available_space;
934 } else {
935 /* Only the detached flag may be guessed */
936 *to_detach = quote_space + markers_space > available_space;
940 static void
941 _adg_update_quote(AdgLDim *ldim, CpmlPair *base1, CpmlPair *base2, CpmlPair *pos,
942 gboolean detach, gboolean outside, gdouble gap)
944 AdgLDimPrivate *data;
945 AdgEntity *quote_entity;
946 CpmlPair middle, factor, org;
947 CpmlVector dir;
948 cairo_matrix_t map;
950 quote_entity = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
951 if (quote_entity == NULL)
952 return;
954 data = (AdgLDimPrivate *) ldim->data;
955 middle.x = (base1->x + base2->x) / 2;
956 middle.y = (base1->y + base2->y) / 2;
957 dir.x = base2->x - base1->x;
958 dir.y = base2->y - base1->y;
959 cpml_vector_set_length(&dir, 1);
961 if (detach) {
962 /* Detached quote: position the quote at "pos" */
963 CpmlPair tmp_pair, quote_end;
964 CpmlVector vector;
965 gdouble distance1, distance2, quote_size;
966 gboolean on_side1;
967 cairo_path_data_t *to_extend;
969 /* Set "org" to the properly converted "pos" coordinates */
970 org.x = pos->x + data->shift.base.x;
971 org.y = pos->y + data->shift.base.y;
973 /* Check in which side of the base line the quote must be rendered.
974 * This is done by checking if the (pos..middle) vector is closer
975 * to the base line vector or to its inverse, that is if they are
976 * concordants or discordants. For this purpose, an algorithm
977 * based on squared distances is used. */
978 tmp_pair.x = org.x - middle.x;
979 tmp_pair.y = org.y - middle.y;
980 vector.x = -dir.x;
981 vector.y = -dir.y;
982 distance2 = cpml_pair_squared_distance(&dir, &tmp_pair);
983 distance1 = cpml_pair_squared_distance(&vector, &tmp_pair);
984 on_side1 = distance2 > distance1;
986 /* Properly align the quote, depending on its side */
987 factor.x = on_side1 ? 1 : 0;
988 if (! on_side1)
989 vector = dir;
991 /* Add a gap between the quote and the extension line */
992 org.x += vector.x * gap;
993 org.y += vector.y * gap;
995 /* Calculate the end point (on the base line) of the quote */
996 quote_size = adg_entity_get_extents(quote_entity)->size.x;
997 quote_end.x = org.x + vector.x * quote_size;
998 quote_end.y = org.y + vector.y * quote_size;
1000 /* Extends the base line to include the "quote_end" pair,
1001 * so a detached quote is properly underlined */
1002 if (outside) {
1003 to_extend = &data->cairo.data[on_side1 ? 7 : 9];
1004 } else {
1005 to_extend = &data->cairo.data[9];
1006 cpml_pair_to_cairo(&middle, &data->cairo.data[9]);
1007 cpml_pair_to_cairo(on_side1 ? base1 : base2, &data->cairo.data[11]);
1008 data->cairo.data[2].header.length = 6;
1011 /* Extend the base line only if needed */
1012 cpml_pair_from_cairo(&tmp_pair, to_extend);
1013 distance1 = cpml_pair_squared_distance(&quote_end, &middle);
1014 distance2 = cpml_pair_squared_distance(&tmp_pair, &middle);
1015 if (distance1 > distance2)
1016 cpml_pair_to_cairo(&quote_end, to_extend);
1017 } else {
1018 /* Center the quote in the middle of the base line */
1019 factor.x = 0.5;
1020 cpml_pair_copy(&org, &middle);
1023 factor.y = 0;
1024 adg_alignment_set_factor((AdgAlignment *) quote_entity, &factor);
1025 cairo_matrix_init(&map, dir.x, dir.y, -dir.y, dir.x, org.x, org.y);
1026 adg_entity_set_global_map(quote_entity, &map);
1027 adg_entity_arrange(quote_entity);
1029 adg_matrix_copy(&data->quote.global_map, &map);
1032 static void
1033 _adg_update_extents(AdgLDim *ldim)
1035 AdgLDimPrivate *data;
1036 CpmlExtents new_extents;
1037 const CpmlExtents *extents;
1038 AdgEntity *quote;
1039 AdgEntity *marker_entity;
1041 data = ldim->data;
1042 quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
1043 new_extents.is_defined = FALSE;
1045 /* The quote is always present (otherwise something bad happened) */
1046 extents = adg_entity_get_extents(quote);
1047 if (extents != NULL)
1048 cpml_extents_add(&new_extents, extents);
1050 if (data->trail != NULL) {
1051 extents = adg_trail_get_extents(data->trail);
1052 if (extents != NULL)
1053 cpml_extents_add(&new_extents, extents);
1056 if (data->marker1 != NULL) {
1057 marker_entity = (AdgEntity *) data->marker1;
1058 adg_entity_local_changed(marker_entity);
1059 adg_entity_arrange(marker_entity);
1060 extents = adg_entity_get_extents(marker_entity);
1061 if (extents != NULL)
1062 cpml_extents_add(&new_extents, extents);
1065 if (data->marker2 != NULL) {
1066 marker_entity = (AdgEntity *) data->marker2;
1067 adg_entity_local_changed(marker_entity);
1068 adg_entity_arrange(marker_entity);
1069 extents = adg_entity_get_extents(marker_entity);
1070 if (extents != NULL)
1071 cpml_extents_add(&new_extents, extents);
1074 adg_entity_set_extents((AdgEntity *) ldim, &new_extents);
1077 static void
1078 _adg_unset_trail(AdgLDim *ldim)
1080 AdgLDimPrivate *data = ldim->data;
1082 if (data->trail != NULL)
1083 adg_model_clear((AdgModel *) data->trail);
1085 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1088 static void
1089 _adg_dispose_trail(AdgLDim *ldim)
1091 AdgLDimPrivate *data = ldim->data;
1093 if (data->trail != NULL) {
1094 g_object_unref(data->trail);
1095 data->trail = NULL;
1099 static void
1100 _adg_dispose_markers(AdgLDim *ldim)
1102 AdgLDimPrivate *data = ldim->data;
1104 if (data->marker1 != NULL) {
1105 g_object_unref(data->marker1);
1106 data->marker1 = NULL;
1109 if (data->marker2 != NULL) {
1110 g_object_unref(data->marker2);
1111 data->marker2 = NULL;
1115 static cairo_path_t *
1116 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1118 AdgLDim *ldim;
1119 AdgLDimPrivate *data;
1121 ldim = (AdgLDim *) user_data;
1122 data = ldim->data;
1124 return &data->cairo.path;