adg: take the absolute value when computing sizes
[adg.git] / src / adg / adg-ldim.c
blob913b683bdf34e8e4af2d6771ef2f15a21706d2dd
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2017 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_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 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;
134 dim_class->compute_geometry = _adg_compute_geometry;
136 param = g_param_spec_double("direction",
137 P_("Direction"),
138 P_("The inclination angle of the extension lines"),
139 -G_PI, G_PI, ADG_DIR_RIGHT,
140 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
141 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
143 param = g_param_spec_boolean("has-extension1",
144 P_("Has First Extension Line flag"),
145 P_("Show (TRUE) or hide (FALSE) the first extension line"),
146 TRUE, G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
149 param = g_param_spec_boolean("has-extension2",
150 P_("Has Second Extension Line flag"),
151 P_("Show (TRUE) or hide (FALSE) the second extension line"),
152 TRUE, G_PARAM_READWRITE);
153 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
156 static void
157 adg_ldim_init(AdgLDim *ldim)
159 AdgLDimPrivate *data;
160 cairo_path_data_t move_to, line_to;
162 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
163 move_to.header.type = CPML_MOVE;
164 move_to.header.length = 2;
165 line_to.header.type = CPML_LINE;
166 line_to.header.length = 2;
168 data->direction = ADG_DIR_RIGHT;
169 data->has_extension1 = TRUE;
170 data->has_extension2 = TRUE;
172 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
173 data->cairo.path.data = data->cairo.data;
174 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
175 data->cairo.path.data[0] = move_to;
176 data->cairo.path.data[2] = line_to;
177 data->cairo.path.data[4] = move_to;
178 data->cairo.path.data[6] = line_to;
179 data->cairo.path.data[8] = move_to;
180 data->cairo.path.data[10] = line_to;
181 data->cairo.path.data[12] = move_to;
182 data->cairo.path.data[14] = line_to;
183 data->cairo.path.data[16] = move_to;
184 data->cairo.path.data[18] = line_to;
186 data->trail = NULL;
187 data->marker1 = NULL;
188 data->marker2 = NULL;
190 ldim->data = data;
193 static void
194 _adg_dispose(GObject *object)
196 AdgLDim *ldim = (AdgLDim *) object;
198 _adg_dispose_trail(ldim);
199 _adg_dispose_markers(ldim);
201 if (_ADG_OLD_OBJECT_CLASS->dispose)
202 _ADG_OLD_OBJECT_CLASS->dispose(object);
205 static void
206 _adg_get_property(GObject *object, guint prop_id,
207 GValue *value, GParamSpec *pspec)
209 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
211 switch (prop_id) {
212 case PROP_DIRECTION:
213 g_value_set_double(value, data->direction);
214 break;
215 case PROP_HAS_EXTENSION1:
216 g_value_set_boolean(value, data->has_extension1);
217 break;
218 case PROP_HAS_EXTENSION2:
219 g_value_set_boolean(value, data->has_extension2);
220 break;
221 default:
222 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
223 break;
227 static void
228 _adg_set_property(GObject *object, guint prop_id,
229 const GValue *value, GParamSpec *pspec)
231 AdgLDim *ldim;
232 AdgLDimPrivate *data;
234 ldim = (AdgLDim *) object;
235 data = ldim->data;
237 switch (prop_id) {
238 case PROP_DIRECTION:
239 data->direction = cpml_angle(g_value_get_double(value));
240 break;
241 case PROP_HAS_EXTENSION1:
242 data->has_extension1 = g_value_get_boolean(value);
243 break;
244 case PROP_HAS_EXTENSION2:
245 data->has_extension2 = g_value_get_boolean(value);
246 break;
247 default:
248 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
249 break;
255 * adg_ldim_new:
257 * Creates a new - undefined - linear dimension. You must, at least,
258 * define the start of the dimension in #AdgDim:ref1, the end in
259 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
260 * any valid #AdgDim method. The director of the dimension (that is,
261 * if it is horizontal, vertical or oblique at a specific angle)
262 * should be specified with adg_ldim_set_direction().
264 * Returns: the newly created linear dimension entity
266 * Since: 1.0
268 AdgLDim *
269 adg_ldim_new(void)
271 return g_object_new(ADG_TYPE_LDIM, NULL);
275 * adg_ldim_new_full:
276 * @ref1: (allow-none): the first reference point
277 * @ref2: (allow-none): the second reference point
278 * @pos: (allow-none): the position reference
279 * @direction: angle where to extend the dimension
281 * Creates a new linear dimension, specifing all the needed properties in
282 * one shot.
284 * Returns: the newly created linear dimension entity
286 * Since: 1.0
288 AdgLDim *
289 adg_ldim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
290 const CpmlPair *pos, gdouble direction)
292 AdgLDim *ldim;
293 AdgDim *dim;
295 ldim = adg_ldim_new();
296 dim = (AdgDim *) ldim;
298 if (ref1 != NULL)
299 adg_dim_set_ref1_from_pair(dim, ref1);
301 if (ref2 != NULL)
302 adg_dim_set_ref2_from_pair(dim, ref2);
304 if (pos != NULL)
305 adg_dim_set_pos_from_pair(dim, pos);
307 adg_ldim_set_direction(ldim, direction);
308 return ldim;
312 * adg_ldim_new_full_explicit:
313 * @ref1_x: the x coordinate of the first reference point
314 * @ref1_y: the y coordinate of the first reference point
315 * @ref2_x: the x coordinate of the second reference point
316 * @ref2_y: the y coordinate of the second reference point
317 * @pos_x: the x coordinate of the position reference
318 * @pos_y: the y coordinate of the position reference
319 * @direction: angle where to extend the dimension
321 * Wrappes adg_ldim_new_full() with explicit values.
323 * Returns: the newly created linear dimension entity
325 * Since: 1.0
327 AdgLDim *
328 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
329 gdouble ref2_x, gdouble ref2_y,
330 gdouble pos_x, gdouble pos_y, gdouble direction)
332 CpmlPair ref1;
333 CpmlPair ref2;
334 CpmlPair pos;
336 ref1.x = ref1_x;
337 ref1.y = ref1_y;
338 ref2.x = ref2_x;
339 ref2.y = ref2_y;
340 pos.x = pos_x;
341 pos.y = pos_y;
343 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
347 * adg_ldim_new_full_from_model:
348 * @model: (transfer none): the model from which the named pairs are taken
349 * @ref1: (allow-none): the first reference point
350 * @ref2: (allow-none): the second reference point
351 * @pos: (allow-none): the position reference
352 * @direction: angle where to extend the dimension
354 * Creates a new linear dimension, specifing all the needed properties in
355 * one shot and using named pairs from @model.
357 * Returns: the newly created linear dimension entity
359 * Since: 1.0
361 AdgLDim *
362 adg_ldim_new_full_from_model(AdgModel *model,
363 const gchar *ref1, const gchar *ref2,
364 const gchar *pos, gdouble direction)
366 AdgLDim *ldim;
367 AdgDim *dim;
369 g_return_val_if_fail(model != NULL, NULL);
371 ldim = adg_ldim_new();
372 dim = (AdgDim *) ldim;
374 if (ref1 != NULL)
375 adg_dim_set_ref1_from_model(dim, model, ref1);
377 if (ref2 != NULL)
378 adg_dim_set_ref2_from_model(dim, model, ref2);
380 if (pos != NULL)
381 adg_dim_set_pos_from_model(dim, model, pos);
383 adg_ldim_set_direction(ldim, direction);
384 return (AdgLDim *) dim;
388 * adg_ldim_set_direction:
389 * @ldim: an #AdgLDim entity
390 * @direction: an angle value, in radians
392 * Sets the direction angle where to extend @ldim.
393 * @direction is normalized by cpml_angle() before being used.
395 * Since: 1.0
397 void
398 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
400 g_return_if_fail(ADG_IS_LDIM(ldim));
401 g_object_set(ldim, "direction", direction, NULL);
405 * adg_ldim_get_direction:
406 * @ldim: an #AdgLDim entity
408 * Gets the direction where @ldim will extend.
410 * Returns: the direction angle in radians
412 * Since: 1.0
414 gdouble
415 adg_ldim_get_direction(AdgLDim *ldim)
417 AdgLDimPrivate *data;
419 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
421 data = ldim->data;
423 return data->direction;
427 * adg_ldim_switch_extension1:
428 * @ldim: an #AdgLDim entity
429 * @new_state: the new state
431 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
432 * @new_state is <constant>FALSE</constant>) the first extension
433 * line of @ldim.
435 * Since: 1.0
437 void
438 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
440 g_return_if_fail(ADG_IS_LDIM(ldim));
441 g_return_if_fail(adg_is_boolean_value(new_state));
442 g_object_set(ldim, "has-extension1", new_state, NULL);
446 * adg_ldim_has_extension1:
447 * @ldim: an #AdgLDim entity
449 * Checks if @ldim should render also the first extension line.
451 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
453 * Since: 1.0
455 gboolean
456 adg_ldim_has_extension1(AdgLDim *ldim)
458 AdgLDimPrivate *data;
460 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
462 data = ldim->data;
464 return data->has_extension1;
468 * adg_ldim_switch_extension2:
469 * @ldim: an #AdgLDim entity
470 * @new_state: the new new_state
472 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
473 * @new_state is <constant>FALSE</constant>) the second extension
474 * 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: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> 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 = (AdgLDim *) entity;
540 _adg_dispose_trail(ldim);
541 _adg_dispose_markers(ldim);
542 _adg_unset_trail(ldim);
544 if (_ADG_OLD_ENTITY_CLASS->invalidate)
545 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
548 static void
549 _adg_arrange(AdgEntity *entity)
551 AdgLDim *ldim;
552 AdgDim *dim;
553 AdgLDimPrivate *data;
554 AdgAlignment *quote;
555 AdgDimStyle *dim_style;
556 gboolean outside, detach;
557 const cairo_matrix_t *local;
558 CpmlPair ref1, ref2, pos, base1, base2;
559 CpmlPair pair;
561 if (_ADG_OLD_ENTITY_CLASS->arrange)
562 _ADG_OLD_ENTITY_CLASS->arrange(entity);
564 dim = (AdgDim *) entity;
565 if (! adg_dim_compute_geometry(dim))
566 return;
568 ldim = (AdgLDim *) entity;
569 data = ldim->data;
570 quote = adg_dim_get_quote(dim);
572 _adg_update_entities(ldim);
574 /* Check for cached result */
575 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
576 adg_entity_set_global_map((AdgEntity *) quote, &data->quote.global_map);
577 return;
580 _adg_choose_flags(ldim, &outside, &detach);
582 dim_style = _ADG_GET_DIM_STYLE(dim);
583 local = adg_entity_get_local_matrix(entity);
585 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
586 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
587 cpml_pair_copy(&pos, (CpmlPair *) adg_dim_get_pos(dim));
588 cpml_pair_copy(&base1, &data->geometry.base1);
589 cpml_pair_copy(&base2, &data->geometry.base2);
591 cpml_pair_transform(&ref1, local);
592 cpml_pair_transform(&ref2, local);
593 cpml_pair_transform(&pos, local);
594 cpml_pair_transform(&base1, local);
595 base1.x += data->shift.base.x;
596 base1.y += data->shift.base.y;
597 cpml_pair_transform(&base2, local);
598 base2.x += data->shift.base.x;
599 base2.y += data->shift.base.y;
601 pair.x = ref1.x + data->shift.from.x;
602 pair.y = ref1.y + data->shift.from.y;
603 cpml_pair_to_cairo(&pair, &data->cairo.data[13]);
605 cpml_pair_to_cairo(&base1, &data->cairo.data[1]);
607 pair.x = base1.x + data->shift.to.x;
608 pair.y = base1.y + data->shift.to.y;
609 cpml_pair_to_cairo(&pair, &data->cairo.data[15]);
611 pair.x = ref2.x + data->shift.from.x;
612 pair.y = ref2.y + data->shift.from.y;
613 cpml_pair_to_cairo(&pair, &data->cairo.data[17]);
615 cpml_pair_to_cairo(&base2, &data->cairo.data[3]);
617 pair.x = base2.x + data->shift.to.x;
618 pair.y = base2.y + data->shift.to.y;
619 cpml_pair_to_cairo(&pair, &data->cairo.data[19]);
621 /* Calculate the outside segments */
622 if (outside) {
623 gdouble beyond;
624 CpmlVector vector;
626 beyond = adg_dim_style_get_beyond(dim_style);
627 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
629 cpml_pair_from_cairo(&vector, &data->cairo.data[3]);
630 vector.x -= pair.x;
631 vector.y -= pair.y;
632 cpml_vector_set_length(&vector, beyond);
634 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
635 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
637 pair.x -= vector.x;
638 pair.y -= vector.y;
639 cpml_pair_to_cairo(&pair, &data->cairo.data[7]);
641 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
642 cpml_pair_to_cairo(&pair, &data->cairo.data[11]);
644 pair.x += vector.x;
645 pair.y += vector.y;
646 cpml_pair_to_cairo(&pair, &data->cairo.data[9]);
648 data->cairo.data[2].header.length = 2;
649 } else {
650 data->cairo.data[2].header.length = 10;
652 data->cairo.data[10].header.length = 2;
654 _adg_update_quote(ldim, &base1, &base2, &pos, detach, outside,
655 adg_dim_style_get_quote_shift(dim_style)->x);
657 /* Play with header lengths to show or hide the extension lines */
658 if (data->has_extension1) {
659 data->cairo.data[14].header.length = data->has_extension2 ? 2 : 6;
660 } else {
661 gint n = ! outside && ! detach ? 2 : 10;
662 data->cairo.data[14].header.length = 2;
663 data->cairo.data[n].header.length += 4;
664 if (!data->has_extension2)
665 data->cairo.data[n].header.length += 4;
668 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
670 /* Update the markers, if present */
671 if (data->trail != NULL) {
672 if (data->marker1 != NULL)
673 adg_marker_set_segment(data->marker1, data->trail, outside ? 2 : 1);
674 if (data->marker2 != NULL)
675 adg_marker_set_segment(data->marker2, data->trail, outside ? 3 : 1);
676 } else {
677 _adg_dispose_markers(ldim);
680 _adg_update_extents(ldim);
683 static void
684 _adg_render(AdgEntity *entity, cairo_t *cr)
686 AdgLDim *ldim;
687 AdgDim *dim;
688 AdgLDimPrivate *data;
689 AdgDimStyle *dim_style;
690 AdgDress dress;
691 const cairo_path_t *cairo_path;
693 dim = (AdgDim *) entity;
694 if (! adg_dim_compute_geometry(dim))
695 return;
697 ldim = (AdgLDim *) entity;
698 data = ldim->data;
699 dim_style = _ADG_GET_DIM_STYLE(dim);
701 adg_style_apply((AdgStyle *) dim_style, entity, cr);
702 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
704 if (data->marker1)
705 adg_entity_render((AdgEntity *) data->marker1, cr);
706 if (data->marker2)
707 adg_entity_render((AdgEntity *) data->marker2, cr);
709 cairo_transform(cr, adg_entity_get_global_matrix(entity));
710 dress = adg_dim_style_get_line_dress(dim_style);
711 adg_entity_apply_dress(entity, dress, cr);
713 cairo_path = adg_trail_get_cairo_path(data->trail);
714 cairo_append_path(cr, cairo_path);
715 cairo_stroke(cr);
718 static gchar *
719 _adg_default_value(AdgDim *dim)
721 AdgLDim *ldim;
722 AdgLDimPrivate *data;
723 gdouble value;
725 if (! adg_dim_compute_geometry(dim))
726 return g_strdup("undef");
728 ldim = (AdgLDim *) dim;
729 data = ldim->data;
730 value = data->geometry.distance;
732 return adg_dim_get_text(dim, value);
735 static gboolean
736 _adg_compute_geometry(AdgDim *dim)
738 AdgLDim *ldim;
739 AdgLDimPrivate *data;
740 AdgPoint *ref1_point, *ref2_point, *pos_point;
741 const CpmlPair *ref1, *ref2, *pos;
742 CpmlVector baseline, extension;
743 gdouble d, k;
745 ref1_point = adg_dim_get_ref1(dim);
746 if (! adg_point_update(ref1_point)) {
747 adg_dim_geometry_missing(dim, "ref1");
748 return FALSE;
751 ref2_point = adg_dim_get_ref2(dim);
752 if (! adg_point_update(ref2_point)) {
753 adg_dim_geometry_missing(dim, "ref2");
754 return FALSE;
757 pos_point = adg_dim_get_pos(dim);
758 if (! adg_point_update(pos_point)) {
759 adg_dim_geometry_missing(dim, "pos");
760 return FALSE;
763 ref1 = (CpmlPair *) ref1_point;
764 ref2 = (CpmlPair *) ref2_point;
765 if (cpml_pair_equal(ref1, ref2)) {
766 adg_dim_geometry_coincident(dim, "ref1", "ref2", ref1);
767 return FALSE;
770 pos = (CpmlPair *) pos_point;
771 ldim = (AdgLDim *) dim;
772 data = ldim->data;
774 cpml_vector_from_angle(&extension, data->direction);
775 cpml_pair_copy(&baseline, &extension);
776 cpml_vector_normal(&baseline);
778 d = extension.y * baseline.x -
779 extension.x * baseline.y;
780 g_return_val_if_fail(d != 0, FALSE);
782 k = ((pos->y - ref1->y) * baseline.x -
783 (pos->x - ref1->x) * baseline.y) / d;
784 data->geometry.base1.x = ref1->x + k * extension.x;
785 data->geometry.base1.y = ref1->y + k * extension.y;
787 k = ((pos->y - ref2->y) * baseline.x -
788 (pos->x - ref2->x) * baseline.y) / d;
789 data->geometry.base2.x = ref2->x + k * extension.x;
790 data->geometry.base2.y = ref2->y + k * extension.y;
792 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
793 &data->geometry.base2);
795 _adg_update_shift(ldim);
796 return TRUE;
799 static void
800 _adg_update_shift(AdgLDim *ldim)
802 AdgLDimPrivate *data;
803 AdgDimStyle *dim_style;
804 gdouble from_offset, to_offset;
805 gdouble baseline_spacing, level;
806 CpmlVector vector;
808 data = ldim->data;
810 if (data->shift.is_arranged)
811 return;
813 dim_style = _ADG_GET_DIM_STYLE(ldim);
814 from_offset = adg_dim_style_get_from_offset(dim_style);
815 to_offset = adg_dim_style_get_to_offset(dim_style);
816 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
817 level = adg_dim_get_level((AdgDim *) ldim);
819 cpml_vector_from_angle(&vector, data->direction);
821 cpml_vector_set_length(&vector, from_offset);
822 cpml_pair_copy(&data->shift.from, &vector);
824 cpml_vector_set_length(&vector, to_offset);
825 cpml_pair_copy(&data->shift.to, &vector);
827 cpml_vector_set_length(&vector, level * baseline_spacing);
828 cpml_pair_copy(&data->shift.base, &vector);
831 static void
832 _adg_update_entities(AdgLDim *ldim)
834 AdgEntity *entity;
835 AdgLDimPrivate *data;
836 AdgDimStyle *dim_style;
838 entity = (AdgEntity *) ldim;
839 data = ldim->data;
840 dim_style = _ADG_GET_DIM_STYLE(ldim);
842 if (data->trail == NULL)
843 data->trail = adg_trail_new(_adg_trail_callback, ldim);
845 if (data->marker1 == NULL) {
846 data->marker1 = adg_dim_style_marker1_new(dim_style);
847 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
850 if (data->marker2 == NULL) {
851 data->marker2 = adg_dim_style_marker2_new(dim_style);
852 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
856 static void
857 _adg_choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
859 AdgDim *dim;
860 AdgThreeState outside, detached;
861 AdgLDimPrivate *data;
862 const cairo_matrix_t *local, *global;
863 gdouble local_factor, global_factor;
864 gdouble available_space, markers_space, quote_space;
866 dim = (AdgDim *) ldim;
867 outside = adg_dim_get_outside(dim);
868 detached = adg_dim_get_detached(dim);
870 *to_outside = outside == ADG_THREE_STATE_ON;
871 *to_detach = detached == ADG_THREE_STATE_ON;
873 /* On explicit flags, no further investigation is required */
874 if (outside != ADG_THREE_STATE_UNKNOWN &&
875 detached != ADG_THREE_STATE_UNKNOWN)
876 return;
878 data = ldim->data;
879 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
880 global = adg_entity_get_global_matrix((AdgEntity *) ldim);
881 local_factor = abs(local->xx + local->yy) / 2;
882 global_factor = abs(global->xx + global->yy) / 2;
883 available_space = data->geometry.distance * local_factor * global_factor;
885 markers_space = 0;
886 if (outside != ADG_THREE_STATE_ON) {
887 if (data->marker1 != NULL)
888 markers_space += adg_marker_get_size(data->marker1);
889 if (data->marker2 != NULL)
890 markers_space += adg_marker_get_size(data->marker2);
892 markers_space *= global_factor;
895 if (detached == ADG_THREE_STATE_ON) {
896 /* Leave at least 0.25 markers_space between the markers */
897 quote_space = markers_space * 0.25;
898 } else {
899 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote(dim);
900 adg_entity_arrange(quote);
901 quote_space = adg_entity_get_extents(quote)->size.x;
904 if (outside == ADG_THREE_STATE_UNKNOWN &&
905 detached == ADG_THREE_STATE_UNKNOWN) {
906 /* Both flags need to be choosed */
907 if (quote_space + markers_space < available_space) {
908 *to_detach = FALSE;
909 *to_outside = FALSE;
910 } else if (quote_space < available_space) {
911 *to_detach = FALSE;
912 *to_outside = TRUE;
913 } else {
914 *to_detach = TRUE;
915 *to_outside = markers_space * 1.25 > available_space;
917 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
918 /* Only the outside flag may be guessed */
919 *to_outside = quote_space + markers_space > available_space;
920 } else {
921 /* Only the detached flag may be guessed */
922 *to_detach = quote_space + markers_space > available_space;
926 static void
927 _adg_update_quote(AdgLDim *ldim, CpmlPair *base1, CpmlPair *base2, CpmlPair *pos,
928 gboolean detach, gboolean outside, gdouble gap)
930 AdgLDimPrivate *data;
931 AdgEntity *quote_entity;
932 CpmlPair middle, factor, org;
933 CpmlVector dir;
934 cairo_matrix_t map;
936 quote_entity = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
937 if (quote_entity == NULL)
938 return;
940 data = (AdgLDimPrivate *) ldim->data;
941 middle.x = (base1->x + base2->x) / 2;
942 middle.y = (base1->y + base2->y) / 2;
943 dir.x = base2->x - base1->x;
944 dir.y = base2->y - base1->y;
945 cpml_vector_set_length(&dir, 1);
947 if (detach) {
948 /* Detached quote: position the quote at "pos" */
949 CpmlPair tmp_pair, quote_end;
950 CpmlVector vector;
951 gdouble distance1, distance2, quote_size;
952 gboolean on_side1;
953 cairo_path_data_t *to_extend;
955 /* Set "org" to the properly converted "pos" coordinates */
956 org.x = pos->x + data->shift.base.x;
957 org.y = pos->y + data->shift.base.y;
959 /* Check in which side of the base line the quote must be rendered.
960 * This is done by checking if the (pos..middle) vector is closer
961 * to the base line vector or to its inverse, that is if they are
962 * concordants or discordants. For this purpose, an algorithm
963 * based on squared distances is used. */
964 tmp_pair.x = org.x - middle.x;
965 tmp_pair.y = org.y - middle.y;
966 vector.x = -dir.x;
967 vector.y = -dir.y;
968 distance2 = cpml_pair_squared_distance(&dir, &tmp_pair);
969 distance1 = cpml_pair_squared_distance(&vector, &tmp_pair);
970 on_side1 = distance2 > distance1;
972 /* Properly align the quote, depending on its side */
973 factor.x = on_side1 ? 1 : 0;
974 if (! on_side1)
975 vector = dir;
977 /* Add a gap between the quote and the extension line */
978 org.x += vector.x * gap;
979 org.y += vector.y * gap;
981 /* Calculate the end point (on the base line) of the quote */
982 quote_size = adg_entity_get_extents(quote_entity)->size.x;
983 quote_end.x = org.x + vector.x * quote_size;
984 quote_end.y = org.y + vector.y * quote_size;
986 /* Extends the base line to include the "quote_end" pair,
987 * so a detached quote is properly underlined */
988 if (outside) {
989 to_extend = &data->cairo.data[on_side1 ? 7 : 9];
990 } else {
991 to_extend = &data->cairo.data[9];
992 cpml_pair_to_cairo(&middle, &data->cairo.data[9]);
993 cpml_pair_to_cairo(on_side1 ? base1 : base2, &data->cairo.data[11]);
994 data->cairo.data[2].header.length = 6;
997 /* Extend the base line only if needed */
998 cpml_pair_from_cairo(&tmp_pair, to_extend);
999 distance1 = cpml_pair_squared_distance(&quote_end, &middle);
1000 distance2 = cpml_pair_squared_distance(&tmp_pair, &middle);
1001 if (distance1 > distance2)
1002 cpml_pair_to_cairo(&quote_end, to_extend);
1003 } else {
1004 /* Center the quote in the middle of the base line */
1005 factor.x = 0.5;
1006 cpml_pair_copy(&org, &middle);
1009 factor.y = 0;
1010 adg_alignment_set_factor((AdgAlignment *) quote_entity, &factor);
1011 cairo_matrix_init(&map, dir.x, dir.y, -dir.y, dir.x, org.x, org.y);
1012 adg_entity_set_global_map(quote_entity, &map);
1013 adg_entity_arrange(quote_entity);
1015 adg_matrix_copy(&data->quote.global_map, &map);
1018 static void
1019 _adg_update_extents(AdgLDim *ldim)
1021 AdgLDimPrivate *data;
1022 CpmlExtents new_extents;
1023 const CpmlExtents *extents;
1024 AdgEntity *quote;
1025 AdgEntity *marker_entity;
1027 data = ldim->data;
1028 quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
1029 new_extents.is_defined = FALSE;
1031 /* The quote is always present (otherwise something bad happened) */
1032 extents = adg_entity_get_extents(quote);
1033 if (extents != NULL)
1034 cpml_extents_add(&new_extents, extents);
1036 if (data->trail != NULL) {
1037 extents = adg_trail_get_extents(data->trail);
1038 if (extents != NULL)
1039 cpml_extents_add(&new_extents, extents);
1042 if (data->marker1 != NULL) {
1043 marker_entity = (AdgEntity *) data->marker1;
1044 adg_entity_local_changed(marker_entity);
1045 adg_entity_arrange(marker_entity);
1046 extents = adg_entity_get_extents(marker_entity);
1047 if (extents != NULL)
1048 cpml_extents_add(&new_extents, extents);
1051 if (data->marker2 != NULL) {
1052 marker_entity = (AdgEntity *) data->marker2;
1053 adg_entity_local_changed(marker_entity);
1054 adg_entity_arrange(marker_entity);
1055 extents = adg_entity_get_extents(marker_entity);
1056 if (extents != NULL)
1057 cpml_extents_add(&new_extents, extents);
1060 adg_entity_set_extents((AdgEntity *) ldim, &new_extents);
1063 static void
1064 _adg_unset_trail(AdgLDim *ldim)
1066 AdgLDimPrivate *data = ldim->data;
1068 if (data->trail != NULL)
1069 adg_model_clear((AdgModel *) data->trail);
1071 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1074 static void
1075 _adg_dispose_trail(AdgLDim *ldim)
1077 AdgLDimPrivate *data = ldim->data;
1079 if (data->trail != NULL) {
1080 g_object_unref(data->trail);
1081 data->trail = NULL;
1085 static void
1086 _adg_dispose_markers(AdgLDim *ldim)
1088 AdgLDimPrivate *data = ldim->data;
1090 if (data->marker1 != NULL) {
1091 g_object_unref(data->marker1);
1092 data->marker1 = NULL;
1095 if (data->marker2 != NULL) {
1096 g_object_unref(data->marker2);
1097 data->marker2 = NULL;
1101 static cairo_path_t *
1102 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1104 AdgLDim *ldim;
1105 AdgLDimPrivate *data;
1107 ldim = (AdgLDim *) user_data;
1108 data = ldim->data;
1110 return &data->cairo.path;