adg: use G_PRIVATE_ADD and friends
[adg.git] / src / adg / adg-ldim.c
blobcd1b30e837dfb72597ea787f2623ba2250269786
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2019 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_WITH_PRIVATE(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_compute_geometry (AdgDim *dim);
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 gobject_class->dispose = _adg_dispose;
122 gobject_class->get_property = _adg_get_property;
123 gobject_class->set_property = _adg_set_property;
125 entity_class->global_changed = _adg_global_changed;
126 entity_class->local_changed = _adg_local_changed;
127 entity_class->invalidate = _adg_invalidate;
128 entity_class->arrange = _adg_arrange;
129 entity_class->render = _adg_render;
131 dim_class->default_value = _adg_default_value;
132 dim_class->compute_geometry = _adg_compute_geometry;
134 param = g_param_spec_double("direction",
135 P_("Direction"),
136 P_("The inclination angle of the extension lines"),
137 -G_PI, G_PI, ADG_DIR_RIGHT,
138 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
139 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
141 param = g_param_spec_boolean("has-extension1",
142 P_("Has First Extension Line flag"),
143 P_("Show (TRUE) or hide (FALSE) the first extension line"),
144 TRUE, G_PARAM_READWRITE);
145 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
147 param = g_param_spec_boolean("has-extension2",
148 P_("Has Second Extension Line flag"),
149 P_("Show (TRUE) or hide (FALSE) the second extension line"),
150 TRUE, G_PARAM_READWRITE);
151 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
154 static void
155 adg_ldim_init(AdgLDim *ldim)
157 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
158 cairo_path_data_t move_to, line_to;
160 move_to.header.type = CPML_MOVE;
161 move_to.header.length = 2;
162 line_to.header.type = CPML_LINE;
163 line_to.header.length = 2;
165 data->direction = ADG_DIR_RIGHT;
166 data->has_extension1 = TRUE;
167 data->has_extension2 = TRUE;
169 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
170 data->cairo.path.data = data->cairo.data;
171 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
172 data->cairo.path.data[0] = move_to;
173 data->cairo.path.data[2] = line_to;
174 data->cairo.path.data[4] = move_to;
175 data->cairo.path.data[6] = line_to;
176 data->cairo.path.data[8] = move_to;
177 data->cairo.path.data[10] = line_to;
178 data->cairo.path.data[12] = move_to;
179 data->cairo.path.data[14] = line_to;
180 data->cairo.path.data[16] = move_to;
181 data->cairo.path.data[18] = line_to;
183 data->trail = NULL;
184 data->marker1 = NULL;
185 data->marker2 = NULL;
188 static void
189 _adg_dispose(GObject *object)
191 AdgLDim *ldim = (AdgLDim *) object;
193 _adg_dispose_trail(ldim);
194 _adg_dispose_markers(ldim);
196 if (_ADG_OLD_OBJECT_CLASS->dispose)
197 _ADG_OLD_OBJECT_CLASS->dispose(object);
200 static void
201 _adg_get_property(GObject *object, guint prop_id,
202 GValue *value, GParamSpec *pspec)
204 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) object);
206 switch (prop_id) {
207 case PROP_DIRECTION:
208 g_value_set_double(value, data->direction);
209 break;
210 case PROP_HAS_EXTENSION1:
211 g_value_set_boolean(value, data->has_extension1);
212 break;
213 case PROP_HAS_EXTENSION2:
214 g_value_set_boolean(value, data->has_extension2);
215 break;
216 default:
217 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
218 break;
222 static void
223 _adg_set_property(GObject *object, guint prop_id,
224 const GValue *value, GParamSpec *pspec)
226 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) object);
228 switch (prop_id) {
229 case PROP_DIRECTION:
230 data->direction = cpml_angle(g_value_get_double(value));
231 break;
232 case PROP_HAS_EXTENSION1:
233 data->has_extension1 = g_value_get_boolean(value);
234 break;
235 case PROP_HAS_EXTENSION2:
236 data->has_extension2 = g_value_get_boolean(value);
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
240 break;
246 * adg_ldim_new:
248 * Creates a new - undefined - linear dimension. You must, at least,
249 * define the start of the dimension in #AdgDim:ref1, the end in
250 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
251 * any valid #AdgDim method. The director of the dimension (that is,
252 * if it is horizontal, vertical or oblique at a specific angle)
253 * should be specified with adg_ldim_set_direction().
255 * Returns: the newly created linear dimension entity
257 * Since: 1.0
259 AdgLDim *
260 adg_ldim_new(void)
262 return g_object_new(ADG_TYPE_LDIM, NULL);
266 * adg_ldim_new_full:
267 * @ref1: (allow-none): the first reference point
268 * @ref2: (allow-none): the second reference point
269 * @pos: (allow-none): the position reference
270 * @direction: angle where to extend the dimension
272 * Creates a new linear dimension, specifing all the needed properties in
273 * one shot.
275 * Returns: the newly created linear dimension entity
277 * Since: 1.0
279 AdgLDim *
280 adg_ldim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
281 const CpmlPair *pos, gdouble direction)
283 AdgLDim *ldim;
284 AdgDim *dim;
286 ldim = adg_ldim_new();
287 dim = (AdgDim *) ldim;
289 if (ref1 != NULL)
290 adg_dim_set_ref1_from_pair(dim, ref1);
292 if (ref2 != NULL)
293 adg_dim_set_ref2_from_pair(dim, ref2);
295 if (pos != NULL)
296 adg_dim_set_pos_from_pair(dim, pos);
298 adg_ldim_set_direction(ldim, direction);
299 return ldim;
303 * adg_ldim_new_full_explicit:
304 * @ref1_x: the x coordinate of the first reference point
305 * @ref1_y: the y coordinate of the first reference point
306 * @ref2_x: the x coordinate of the second reference point
307 * @ref2_y: the y coordinate of the second reference point
308 * @pos_x: the x coordinate of the position reference
309 * @pos_y: the y coordinate of the position reference
310 * @direction: angle where to extend the dimension
312 * Wrappes adg_ldim_new_full() with explicit values.
314 * Returns: the newly created linear dimension entity
316 * Since: 1.0
318 AdgLDim *
319 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
320 gdouble ref2_x, gdouble ref2_y,
321 gdouble pos_x, gdouble pos_y, gdouble direction)
323 CpmlPair ref1;
324 CpmlPair ref2;
325 CpmlPair pos;
327 ref1.x = ref1_x;
328 ref1.y = ref1_y;
329 ref2.x = ref2_x;
330 ref2.y = ref2_y;
331 pos.x = pos_x;
332 pos.y = pos_y;
334 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
338 * adg_ldim_new_full_from_model:
339 * @model: (transfer none): the model from which the named pairs are taken
340 * @ref1: (allow-none): the first reference point
341 * @ref2: (allow-none): the second reference point
342 * @pos: (allow-none): the position reference
343 * @direction: angle where to extend the dimension
345 * Creates a new linear dimension, specifing all the needed properties in
346 * one shot and using named pairs from @model.
348 * Returns: the newly created linear dimension entity
350 * Since: 1.0
352 AdgLDim *
353 adg_ldim_new_full_from_model(AdgModel *model,
354 const gchar *ref1, const gchar *ref2,
355 const gchar *pos, gdouble direction)
357 AdgLDim *ldim;
358 AdgDim *dim;
360 g_return_val_if_fail(model != NULL, NULL);
362 ldim = adg_ldim_new();
363 dim = (AdgDim *) ldim;
365 if (ref1 != NULL)
366 adg_dim_set_ref1_from_model(dim, model, ref1);
368 if (ref2 != NULL)
369 adg_dim_set_ref2_from_model(dim, model, ref2);
371 if (pos != NULL)
372 adg_dim_set_pos_from_model(dim, model, pos);
374 adg_ldim_set_direction(ldim, direction);
375 return (AdgLDim *) dim;
379 * adg_ldim_set_direction:
380 * @ldim: an #AdgLDim entity
381 * @direction: an angle value, in radians
383 * Sets the direction angle where to extend @ldim.
384 * @direction is normalized by cpml_angle() before being used.
386 * Since: 1.0
388 void
389 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
391 g_return_if_fail(ADG_IS_LDIM(ldim));
392 g_object_set(ldim, "direction", direction, NULL);
396 * adg_ldim_get_direction:
397 * @ldim: an #AdgLDim entity
399 * Gets the direction where @ldim will extend.
401 * Returns: the direction angle in radians
403 * Since: 1.0
405 gdouble
406 adg_ldim_get_direction(AdgLDim *ldim)
408 AdgLDimPrivate *data;
410 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
412 data = adg_ldim_get_instance_private(ldim);
413 return data->direction;
417 * adg_ldim_switch_extension1:
418 * @ldim: an #AdgLDim entity
419 * @new_state: the new state
421 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
422 * @new_state is <constant>FALSE</constant>) the first extension
423 * line of @ldim.
425 * Since: 1.0
427 void
428 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
430 g_return_if_fail(ADG_IS_LDIM(ldim));
431 g_return_if_fail(adg_is_boolean_value(new_state));
432 g_object_set(ldim, "has-extension1", new_state, NULL);
436 * adg_ldim_has_extension1:
437 * @ldim: an #AdgLDim entity
439 * Checks if @ldim should render also the first extension line.
441 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
443 * Since: 1.0
445 gboolean
446 adg_ldim_has_extension1(AdgLDim *ldim)
448 AdgLDimPrivate *data;
450 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
452 data = adg_ldim_get_instance_private(ldim);
453 return data->has_extension1;
457 * adg_ldim_switch_extension2:
458 * @ldim: an #AdgLDim entity
459 * @new_state: the new new_state
461 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
462 * @new_state is <constant>FALSE</constant>) the second extension
463 * line of @ldim.
465 * Since: 1.0
467 void
468 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
470 g_return_if_fail(ADG_IS_LDIM(ldim));
471 g_return_if_fail(adg_is_boolean_value(new_state));
472 g_object_set(ldim, "has-extension2", new_state, NULL);
476 * adg_ldim_has_extension2:
477 * @ldim: an #AdgLDim entity
479 * Checks if @ldim should render also the second extension line.
481 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
483 * Since: 1.0
485 gboolean
486 adg_ldim_has_extension2(AdgLDim *ldim)
488 AdgLDimPrivate *data;
490 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
492 data = adg_ldim_get_instance_private(ldim);
493 return data->has_extension2;
497 static void
498 _adg_global_changed(AdgEntity *entity)
500 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) entity);
502 _adg_unset_trail((AdgLDim *) entity);
504 if (_ADG_OLD_ENTITY_CLASS->global_changed)
505 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
507 if (data->marker1)
508 adg_entity_global_changed((AdgEntity *) data->marker1);
510 if (data->marker2)
511 adg_entity_global_changed((AdgEntity *) data->marker2);
514 static void
515 _adg_local_changed(AdgEntity *entity)
517 _adg_unset_trail((AdgLDim *) entity);
519 if (_ADG_OLD_ENTITY_CLASS->local_changed)
520 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
523 static void
524 _adg_invalidate(AdgEntity *entity)
526 AdgLDim *ldim = (AdgLDim *) entity;
528 _adg_dispose_trail(ldim);
529 _adg_dispose_markers(ldim);
530 _adg_unset_trail(ldim);
532 if (_ADG_OLD_ENTITY_CLASS->invalidate)
533 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
536 static void
537 _adg_arrange(AdgEntity *entity)
539 AdgLDim *ldim;
540 AdgDim *dim;
541 AdgLDimPrivate *data;
542 AdgAlignment *quote;
543 AdgDimStyle *dim_style;
544 gboolean outside, detach;
545 const cairo_matrix_t *local;
546 CpmlPair ref1, ref2, pos, base1, base2;
547 CpmlPair pair;
549 if (_ADG_OLD_ENTITY_CLASS->arrange)
550 _ADG_OLD_ENTITY_CLASS->arrange(entity);
552 dim = (AdgDim *) entity;
553 if (! adg_dim_compute_geometry(dim))
554 return;
556 ldim = (AdgLDim *) entity;
557 data = adg_ldim_get_instance_private(ldim);
558 quote = adg_dim_get_quote(dim);
560 _adg_update_entities(ldim);
562 /* Check for cached result */
563 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
564 adg_entity_set_global_map((AdgEntity *) quote, &data->quote.global_map);
565 return;
568 _adg_choose_flags(ldim, &outside, &detach);
570 dim_style = adg_dim_get_dim_style(dim);
571 local = adg_entity_get_local_matrix(entity);
573 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
574 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
575 cpml_pair_copy(&pos, (CpmlPair *) adg_dim_get_pos(dim));
576 cpml_pair_copy(&base1, &data->geometry.base1);
577 cpml_pair_copy(&base2, &data->geometry.base2);
579 cpml_pair_transform(&ref1, local);
580 cpml_pair_transform(&ref2, local);
581 cpml_pair_transform(&pos, local);
582 cpml_pair_transform(&base1, local);
583 base1.x += data->shift.base.x;
584 base1.y += data->shift.base.y;
585 cpml_pair_transform(&base2, local);
586 base2.x += data->shift.base.x;
587 base2.y += data->shift.base.y;
589 pair.x = ref1.x + data->shift.from.x;
590 pair.y = ref1.y + data->shift.from.y;
591 cpml_pair_to_cairo(&pair, &data->cairo.data[13]);
593 cpml_pair_to_cairo(&base1, &data->cairo.data[1]);
595 pair.x = base1.x + data->shift.to.x;
596 pair.y = base1.y + data->shift.to.y;
597 cpml_pair_to_cairo(&pair, &data->cairo.data[15]);
599 pair.x = ref2.x + data->shift.from.x;
600 pair.y = ref2.y + data->shift.from.y;
601 cpml_pair_to_cairo(&pair, &data->cairo.data[17]);
603 cpml_pair_to_cairo(&base2, &data->cairo.data[3]);
605 pair.x = base2.x + data->shift.to.x;
606 pair.y = base2.y + data->shift.to.y;
607 cpml_pair_to_cairo(&pair, &data->cairo.data[19]);
609 /* Calculate the outside segments */
610 if (outside) {
611 gdouble beyond;
612 CpmlVector vector;
614 beyond = adg_dim_style_get_beyond(dim_style);
615 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
617 cpml_pair_from_cairo(&vector, &data->cairo.data[3]);
618 vector.x -= pair.x;
619 vector.y -= pair.y;
620 cpml_vector_set_length(&vector, beyond);
622 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
623 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
625 pair.x -= vector.x;
626 pair.y -= vector.y;
627 cpml_pair_to_cairo(&pair, &data->cairo.data[7]);
629 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
630 cpml_pair_to_cairo(&pair, &data->cairo.data[11]);
632 pair.x += vector.x;
633 pair.y += vector.y;
634 cpml_pair_to_cairo(&pair, &data->cairo.data[9]);
636 data->cairo.data[2].header.length = 2;
637 } else {
638 data->cairo.data[2].header.length = 10;
640 data->cairo.data[10].header.length = 2;
642 _adg_update_quote(ldim, &base1, &base2, &pos, detach, outside,
643 adg_dim_style_get_quote_shift(dim_style)->x);
645 /* Play with header lengths to show or hide the extension lines */
646 if (data->has_extension1) {
647 data->cairo.data[14].header.length = data->has_extension2 ? 2 : 6;
648 } else {
649 gint n = ! outside && ! detach ? 2 : 10;
650 data->cairo.data[14].header.length = 2;
651 data->cairo.data[n].header.length += 4;
652 if (!data->has_extension2)
653 data->cairo.data[n].header.length += 4;
656 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
658 /* Update the markers, if present */
659 if (data->trail != NULL) {
660 if (data->marker1 != NULL)
661 adg_marker_set_segment(data->marker1, data->trail, outside ? 2 : 1);
662 if (data->marker2 != NULL)
663 adg_marker_set_segment(data->marker2, data->trail, outside ? 3 : 1);
664 } else {
665 _adg_dispose_markers(ldim);
668 _adg_update_extents(ldim);
671 static void
672 _adg_render(AdgEntity *entity, cairo_t *cr)
674 AdgLDim *ldim;
675 AdgDim *dim;
676 AdgLDimPrivate *data;
677 AdgDimStyle *dim_style;
678 AdgDress dress;
679 const cairo_path_t *cairo_path;
681 dim = (AdgDim *) entity;
682 if (! adg_dim_compute_geometry(dim))
683 return;
685 ldim = (AdgLDim *) entity;
686 data = adg_ldim_get_instance_private(ldim);
687 dim_style = adg_dim_get_dim_style(dim);
689 adg_style_apply((AdgStyle *) dim_style, entity, cr);
690 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
692 if (data->marker1)
693 adg_entity_render((AdgEntity *) data->marker1, cr);
694 if (data->marker2)
695 adg_entity_render((AdgEntity *) data->marker2, cr);
697 cairo_transform(cr, adg_entity_get_global_matrix(entity));
698 dress = adg_dim_style_get_line_dress(dim_style);
699 adg_entity_apply_dress(entity, dress, cr);
701 cairo_path = adg_trail_get_cairo_path(data->trail);
702 cairo_append_path(cr, cairo_path);
703 cairo_stroke(cr);
706 static gchar *
707 _adg_default_value(AdgDim *dim)
709 AdgLDim *ldim;
710 AdgLDimPrivate *data;
711 gdouble value;
713 if (! adg_dim_compute_geometry(dim))
714 return g_strdup("undef");
716 ldim = (AdgLDim *) dim;
717 data = adg_ldim_get_instance_private(ldim);
718 value = data->geometry.distance;
720 return adg_dim_get_text(dim, value);
723 static gboolean
724 _adg_compute_geometry(AdgDim *dim)
726 AdgLDim *ldim;
727 AdgLDimPrivate *data;
728 AdgPoint *ref1_point, *ref2_point, *pos_point;
729 const CpmlPair *ref1, *ref2, *pos;
730 CpmlVector baseline, extension;
731 gdouble d, k;
733 ref1_point = adg_dim_get_ref1(dim);
734 if (! adg_point_update(ref1_point)) {
735 adg_dim_geometry_missing(dim, "ref1");
736 return FALSE;
739 ref2_point = adg_dim_get_ref2(dim);
740 if (! adg_point_update(ref2_point)) {
741 adg_dim_geometry_missing(dim, "ref2");
742 return FALSE;
745 pos_point = adg_dim_get_pos(dim);
746 if (! adg_point_update(pos_point)) {
747 adg_dim_geometry_missing(dim, "pos");
748 return FALSE;
751 ref1 = (CpmlPair *) ref1_point;
752 ref2 = (CpmlPair *) ref2_point;
753 if (cpml_pair_equal(ref1, ref2)) {
754 adg_dim_geometry_coincident(dim, "ref1", "ref2", ref1);
755 return FALSE;
758 pos = (CpmlPair *) pos_point;
759 ldim = (AdgLDim *) dim;
760 data = adg_ldim_get_instance_private(ldim);
762 cpml_vector_from_angle(&extension, data->direction);
763 cpml_pair_copy(&baseline, &extension);
764 cpml_vector_normal(&baseline);
766 d = extension.y * baseline.x -
767 extension.x * baseline.y;
768 g_return_val_if_fail(d != 0, FALSE);
770 k = ((pos->y - ref1->y) * baseline.x -
771 (pos->x - ref1->x) * baseline.y) / d;
772 data->geometry.base1.x = ref1->x + k * extension.x;
773 data->geometry.base1.y = ref1->y + k * extension.y;
775 k = ((pos->y - ref2->y) * baseline.x -
776 (pos->x - ref2->x) * baseline.y) / d;
777 data->geometry.base2.x = ref2->x + k * extension.x;
778 data->geometry.base2.y = ref2->y + k * extension.y;
780 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
781 &data->geometry.base2);
783 _adg_update_shift(ldim);
784 return TRUE;
787 static void
788 _adg_update_shift(AdgLDim *ldim)
790 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
791 AdgDimStyle *dim_style;
792 gdouble from_offset, to_offset;
793 gdouble baseline_spacing, level;
794 CpmlVector vector;
796 if (data->shift.is_arranged)
797 return;
799 dim_style = adg_dim_get_dim_style((AdgDim *) ldim);
800 from_offset = adg_dim_style_get_from_offset(dim_style);
801 to_offset = adg_dim_style_get_to_offset(dim_style);
802 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
803 level = adg_dim_get_level((AdgDim *) ldim);
805 cpml_vector_from_angle(&vector, data->direction);
807 cpml_vector_set_length(&vector, from_offset);
808 cpml_pair_copy(&data->shift.from, &vector);
810 cpml_vector_set_length(&vector, to_offset);
811 cpml_pair_copy(&data->shift.to, &vector);
813 cpml_vector_set_length(&vector, level * baseline_spacing);
814 cpml_pair_copy(&data->shift.base, &vector);
817 static void
818 _adg_update_entities(AdgLDim *ldim)
820 AdgEntity *entity = (AdgEntity *) ldim;
821 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
822 AdgDimStyle *dim_style = adg_dim_get_dim_style((AdgDim *) ldim);
824 if (data->trail == NULL)
825 data->trail = adg_trail_new(_adg_trail_callback, ldim);
827 if (data->marker1 == NULL) {
828 data->marker1 = adg_dim_style_marker1_new(dim_style);
829 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
832 if (data->marker2 == NULL) {
833 data->marker2 = adg_dim_style_marker2_new(dim_style);
834 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
838 static void
839 _adg_choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
841 AdgDim *dim;
842 AdgThreeState outside, detached;
843 AdgLDimPrivate *data;
844 const cairo_matrix_t *local, *global;
845 gdouble local_factor, global_factor;
846 gdouble available_space, markers_space, quote_space;
848 dim = (AdgDim *) ldim;
849 outside = adg_dim_get_outside(dim);
850 detached = adg_dim_get_detached(dim);
852 *to_outside = outside == ADG_THREE_STATE_ON;
853 *to_detach = detached == ADG_THREE_STATE_ON;
855 /* On explicit flags, no further investigation is required */
856 if (outside != ADG_THREE_STATE_UNKNOWN &&
857 detached != ADG_THREE_STATE_UNKNOWN)
858 return;
860 data = adg_ldim_get_instance_private(ldim);
861 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
862 global = adg_entity_get_global_matrix((AdgEntity *) ldim);
863 local_factor = abs(local->xx + local->yy) / 2;
864 global_factor = abs(global->xx + global->yy) / 2;
865 available_space = data->geometry.distance * local_factor * global_factor;
867 markers_space = 0;
868 if (outside != ADG_THREE_STATE_ON) {
869 if (data->marker1 != NULL)
870 markers_space += adg_marker_get_size(data->marker1);
871 if (data->marker2 != NULL)
872 markers_space += adg_marker_get_size(data->marker2);
874 markers_space *= global_factor;
877 if (detached == ADG_THREE_STATE_ON) {
878 /* Leave at least 0.25 markers_space between the markers */
879 quote_space = markers_space * 0.25;
880 } else {
881 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote(dim);
882 adg_entity_arrange(quote);
883 quote_space = adg_entity_get_extents(quote)->size.x;
886 if (outside == ADG_THREE_STATE_UNKNOWN &&
887 detached == ADG_THREE_STATE_UNKNOWN) {
888 /* Both flags need to be choosed */
889 if (quote_space + markers_space < available_space) {
890 *to_detach = FALSE;
891 *to_outside = FALSE;
892 } else if (quote_space < available_space) {
893 *to_detach = FALSE;
894 *to_outside = TRUE;
895 } else {
896 *to_detach = TRUE;
897 *to_outside = markers_space * 1.25 > available_space;
899 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
900 /* Only the outside flag may be guessed */
901 *to_outside = quote_space + markers_space > available_space;
902 } else {
903 /* Only the detached flag may be guessed */
904 *to_detach = quote_space + markers_space > available_space;
908 static void
909 _adg_update_quote(AdgLDim *ldim, CpmlPair *base1, CpmlPair *base2, CpmlPair *pos,
910 gboolean detach, gboolean outside, gdouble gap)
912 AdgLDimPrivate *data;
913 AdgEntity *quote_entity;
914 CpmlPair middle, factor, org;
915 CpmlVector dir;
916 cairo_matrix_t map;
918 quote_entity = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
919 if (quote_entity == NULL)
920 return;
922 data = adg_ldim_get_instance_private(ldim);
923 middle.x = (base1->x + base2->x) / 2;
924 middle.y = (base1->y + base2->y) / 2;
925 dir.x = base2->x - base1->x;
926 dir.y = base2->y - base1->y;
927 cpml_vector_set_length(&dir, 1);
929 if (detach) {
930 /* Detached quote: position the quote at "pos" */
931 CpmlPair tmp_pair, quote_end;
932 CpmlVector vector;
933 gdouble distance1, distance2, quote_size;
934 gboolean on_side1;
935 cairo_path_data_t *to_extend;
937 /* Set "org" to the properly converted "pos" coordinates */
938 org.x = pos->x + data->shift.base.x;
939 org.y = pos->y + data->shift.base.y;
941 /* Check in which side of the base line the quote must be rendered.
942 * This is done by checking if the (pos..middle) vector is closer
943 * to the base line vector or to its inverse, that is if they are
944 * concordants or discordants. For this purpose, an algorithm
945 * based on squared distances is used. */
946 tmp_pair.x = org.x - middle.x;
947 tmp_pair.y = org.y - middle.y;
948 vector.x = -dir.x;
949 vector.y = -dir.y;
950 distance2 = cpml_pair_squared_distance(&dir, &tmp_pair);
951 distance1 = cpml_pair_squared_distance(&vector, &tmp_pair);
952 on_side1 = distance2 > distance1;
954 /* Properly align the quote, depending on its side */
955 factor.x = on_side1 ? 1 : 0;
956 if (! on_side1)
957 vector = dir;
959 /* Add a gap between the quote and the extension line */
960 org.x += vector.x * gap;
961 org.y += vector.y * gap;
963 /* Calculate the end point (on the base line) of the quote */
964 quote_size = adg_entity_get_extents(quote_entity)->size.x;
965 quote_end.x = org.x + vector.x * quote_size;
966 quote_end.y = org.y + vector.y * quote_size;
968 /* Extends the base line to include the "quote_end" pair,
969 * so a detached quote is properly underlined */
970 if (outside) {
971 to_extend = &data->cairo.data[on_side1 ? 7 : 9];
972 } else {
973 to_extend = &data->cairo.data[9];
974 cpml_pair_to_cairo(&middle, &data->cairo.data[9]);
975 cpml_pair_to_cairo(on_side1 ? base1 : base2, &data->cairo.data[11]);
976 data->cairo.data[2].header.length = 6;
979 /* Extend the base line only if needed */
980 cpml_pair_from_cairo(&tmp_pair, to_extend);
981 distance1 = cpml_pair_squared_distance(&quote_end, &middle);
982 distance2 = cpml_pair_squared_distance(&tmp_pair, &middle);
983 if (distance1 > distance2)
984 cpml_pair_to_cairo(&quote_end, to_extend);
985 } else {
986 /* Center the quote in the middle of the base line */
987 factor.x = 0.5;
988 cpml_pair_copy(&org, &middle);
991 factor.y = 0;
992 adg_alignment_set_factor((AdgAlignment *) quote_entity, &factor);
993 cairo_matrix_init(&map, dir.x, dir.y, -dir.y, dir.x, org.x, org.y);
994 adg_entity_set_global_map(quote_entity, &map);
995 adg_entity_arrange(quote_entity);
997 adg_matrix_copy(&data->quote.global_map, &map);
1000 static void
1001 _adg_update_extents(AdgLDim *ldim)
1003 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1004 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
1005 CpmlExtents new_extents;
1006 const CpmlExtents *extents;
1007 AdgEntity *marker_entity;
1009 new_extents.is_defined = FALSE;
1011 /* The quote is always present (otherwise something bad happened) */
1012 extents = adg_entity_get_extents(quote);
1013 if (extents != NULL)
1014 cpml_extents_add(&new_extents, extents);
1016 if (data->trail != NULL) {
1017 extents = adg_trail_get_extents(data->trail);
1018 if (extents != NULL)
1019 cpml_extents_add(&new_extents, extents);
1022 if (data->marker1 != NULL) {
1023 marker_entity = (AdgEntity *) data->marker1;
1024 adg_entity_local_changed(marker_entity);
1025 adg_entity_arrange(marker_entity);
1026 extents = adg_entity_get_extents(marker_entity);
1027 if (extents != NULL)
1028 cpml_extents_add(&new_extents, extents);
1031 if (data->marker2 != NULL) {
1032 marker_entity = (AdgEntity *) data->marker2;
1033 adg_entity_local_changed(marker_entity);
1034 adg_entity_arrange(marker_entity);
1035 extents = adg_entity_get_extents(marker_entity);
1036 if (extents != NULL)
1037 cpml_extents_add(&new_extents, extents);
1040 adg_entity_set_extents((AdgEntity *) ldim, &new_extents);
1043 static void
1044 _adg_unset_trail(AdgLDim *ldim)
1046 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1048 if (data->trail != NULL)
1049 adg_model_clear((AdgModel *) data->trail);
1051 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1054 static void
1055 _adg_dispose_trail(AdgLDim *ldim)
1057 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1059 if (data->trail != NULL) {
1060 g_object_unref(data->trail);
1061 data->trail = NULL;
1065 static void
1066 _adg_dispose_markers(AdgLDim *ldim)
1068 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1070 if (data->marker1 != NULL) {
1071 g_object_unref(data->marker1);
1072 data->marker1 = NULL;
1075 if (data->marker2 != NULL) {
1076 g_object_unref(data->marker2);
1077 data->marker2 = NULL;
1081 static cairo_path_t *
1082 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1084 AdgLDim *ldim = (AdgLDim *) user_data;
1085 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1086 return &data->cairo.path;