doc: update copyright line for 2021
[adg.git] / src / adg / adg-ldim.c
blob5d064065c42051702455b51a48cc53a8e91f75b5
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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"
53 #include <math.h>
55 #include "adg-ldim.h"
56 #include "adg-ldim-private.h"
59 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_ldim_parent_class)
60 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
63 G_DEFINE_TYPE_WITH_PRIVATE(AdgLDim, adg_ldim, ADG_TYPE_DIM)
65 enum {
66 PROP_0,
67 PROP_DIRECTION,
68 PROP_HAS_EXTENSION1,
69 PROP_HAS_EXTENSION2
73 static void _adg_dispose (GObject *object);
74 static void _adg_get_property (GObject *object,
75 guint param_id,
76 GValue *value,
77 GParamSpec *pspec);
78 static void _adg_set_property (GObject *object,
79 guint param_id,
80 const GValue *value,
81 GParamSpec *pspec);
82 static void _adg_global_changed (AdgEntity *entity);
83 static void _adg_local_changed (AdgEntity *entity);
84 static void _adg_invalidate (AdgEntity *entity);
85 static void _adg_arrange (AdgEntity *entity);
86 static void _adg_render (AdgEntity *entity,
87 cairo_t *cr);
88 static gchar * _adg_default_value (AdgDim *dim);
89 static gboolean _adg_compute_geometry (AdgDim *dim);
90 static void _adg_update_shift (AdgLDim *ldim);
91 static void _adg_update_entities (AdgLDim *ldim);
92 static void _adg_choose_flags (AdgLDim *ldim,
93 gboolean *to_outside,
94 gboolean *to_detach);
95 static void _adg_update_quote (AdgLDim *ldim,
96 CpmlPair *base1,
97 CpmlPair *base2,
98 CpmlPair *pos,
99 gboolean detach,
100 gboolean outside,
101 gdouble gap);
102 static void _adg_update_extents (AdgLDim *ldim);
103 static void _adg_unset_trail (AdgLDim *ldim);
104 static void _adg_dispose_trail (AdgLDim *ldim);
105 static void _adg_dispose_markers (AdgLDim *ldim);
106 static cairo_path_t * _adg_trail_callback (AdgTrail *trail,
107 gpointer user_data);
110 static void
111 adg_ldim_class_init(AdgLDimClass *klass)
113 GObjectClass *gobject_class;
114 AdgEntityClass *entity_class;
115 AdgDimClass *dim_class;
116 GParamSpec *param;
118 gobject_class = (GObjectClass *) klass;
119 entity_class = (AdgEntityClass *) klass;
120 dim_class = (AdgDimClass *) klass;
122 gobject_class->dispose = _adg_dispose;
123 gobject_class->get_property = _adg_get_property;
124 gobject_class->set_property = _adg_set_property;
126 entity_class->global_changed = _adg_global_changed;
127 entity_class->local_changed = _adg_local_changed;
128 entity_class->invalidate = _adg_invalidate;
129 entity_class->arrange = _adg_arrange;
130 entity_class->render = _adg_render;
132 dim_class->default_value = _adg_default_value;
133 dim_class->compute_geometry = _adg_compute_geometry;
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 = adg_ldim_get_instance_private(ldim);
159 cairo_path_data_t move_to, line_to;
161 move_to.header.type = CPML_MOVE;
162 move_to.header.length = 2;
163 line_to.header.type = CPML_LINE;
164 line_to.header.length = 2;
166 data->direction = ADG_DIR_RIGHT;
167 data->has_extension1 = TRUE;
168 data->has_extension2 = TRUE;
170 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
171 data->cairo.path.data = data->cairo.data;
172 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
173 data->cairo.path.data[0] = move_to;
174 data->cairo.path.data[2] = line_to;
175 data->cairo.path.data[4] = move_to;
176 data->cairo.path.data[6] = line_to;
177 data->cairo.path.data[8] = move_to;
178 data->cairo.path.data[10] = line_to;
179 data->cairo.path.data[12] = move_to;
180 data->cairo.path.data[14] = line_to;
181 data->cairo.path.data[16] = move_to;
182 data->cairo.path.data[18] = line_to;
184 data->trail = NULL;
185 data->marker1 = NULL;
186 data->marker2 = NULL;
189 static void
190 _adg_dispose(GObject *object)
192 AdgLDim *ldim = (AdgLDim *) object;
194 _adg_dispose_trail(ldim);
195 _adg_dispose_markers(ldim);
197 if (_ADG_OLD_OBJECT_CLASS->dispose)
198 _ADG_OLD_OBJECT_CLASS->dispose(object);
201 static void
202 _adg_get_property(GObject *object, guint prop_id,
203 GValue *value, GParamSpec *pspec)
205 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) object);
207 switch (prop_id) {
208 case PROP_DIRECTION:
209 g_value_set_double(value, data->direction);
210 break;
211 case PROP_HAS_EXTENSION1:
212 g_value_set_boolean(value, data->has_extension1);
213 break;
214 case PROP_HAS_EXTENSION2:
215 g_value_set_boolean(value, data->has_extension2);
216 break;
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
219 break;
223 static void
224 _adg_set_property(GObject *object, guint prop_id,
225 const GValue *value, GParamSpec *pspec)
227 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) object);
229 switch (prop_id) {
230 case PROP_DIRECTION:
231 data->direction = cpml_angle(g_value_get_double(value));
232 break;
233 case PROP_HAS_EXTENSION1:
234 data->has_extension1 = g_value_get_boolean(value);
235 break;
236 case PROP_HAS_EXTENSION2:
237 data->has_extension2 = g_value_get_boolean(value);
238 break;
239 default:
240 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
241 break;
247 * adg_ldim_new:
249 * Creates a new - undefined - linear dimension. You must, at least,
250 * define the start of the dimension in #AdgDim:ref1, the end in
251 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
252 * any valid #AdgDim method. The director of the dimension (that is,
253 * if it is horizontal, vertical or oblique at a specific angle)
254 * should be specified with adg_ldim_set_direction().
256 * Returns: the newly created linear dimension entity
258 * Since: 1.0
260 AdgLDim *
261 adg_ldim_new(void)
263 return g_object_new(ADG_TYPE_LDIM, NULL);
267 * adg_ldim_new_full:
268 * @ref1: (allow-none): the first reference point
269 * @ref2: (allow-none): the second reference point
270 * @pos: (allow-none): the position reference
271 * @direction: angle where to extend the dimension
273 * Creates a new linear dimension, specifing all the needed properties in
274 * one shot.
276 * Returns: the newly created linear dimension entity
278 * Since: 1.0
280 AdgLDim *
281 adg_ldim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
282 const CpmlPair *pos, gdouble direction)
284 AdgLDim *ldim;
285 AdgDim *dim;
287 ldim = adg_ldim_new();
288 dim = (AdgDim *) ldim;
290 if (ref1 != NULL)
291 adg_dim_set_ref1_from_pair(dim, ref1);
293 if (ref2 != NULL)
294 adg_dim_set_ref2_from_pair(dim, ref2);
296 if (pos != NULL)
297 adg_dim_set_pos_from_pair(dim, pos);
299 adg_ldim_set_direction(ldim, direction);
300 return ldim;
304 * adg_ldim_new_full_explicit:
305 * @ref1_x: the x coordinate of the first reference point
306 * @ref1_y: the y coordinate of the first reference point
307 * @ref2_x: the x coordinate of the second reference point
308 * @ref2_y: the y coordinate of the second reference point
309 * @pos_x: the x coordinate of the position reference
310 * @pos_y: the y coordinate of the position reference
311 * @direction: angle where to extend the dimension
313 * Wrappes adg_ldim_new_full() with explicit values.
315 * Returns: the newly created linear dimension entity
317 * Since: 1.0
319 AdgLDim *
320 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
321 gdouble ref2_x, gdouble ref2_y,
322 gdouble pos_x, gdouble pos_y, gdouble direction)
324 CpmlPair ref1;
325 CpmlPair ref2;
326 CpmlPair pos;
328 ref1.x = ref1_x;
329 ref1.y = ref1_y;
330 ref2.x = ref2_x;
331 ref2.y = ref2_y;
332 pos.x = pos_x;
333 pos.y = pos_y;
335 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
339 * adg_ldim_new_full_from_model:
340 * @model: (transfer none): the model from which the named pairs are taken
341 * @ref1: (allow-none): the first reference point
342 * @ref2: (allow-none): the second reference point
343 * @pos: (allow-none): the position reference
344 * @direction: angle where to extend the dimension
346 * Creates a new linear dimension, specifing all the needed properties in
347 * one shot and using named pairs from @model.
349 * Returns: the newly created linear dimension entity
351 * Since: 1.0
353 AdgLDim *
354 adg_ldim_new_full_from_model(AdgModel *model,
355 const gchar *ref1, const gchar *ref2,
356 const gchar *pos, gdouble direction)
358 AdgLDim *ldim;
359 AdgDim *dim;
361 g_return_val_if_fail(model != NULL, NULL);
363 ldim = adg_ldim_new();
364 dim = (AdgDim *) ldim;
366 if (ref1 != NULL)
367 adg_dim_set_ref1_from_model(dim, model, ref1);
369 if (ref2 != NULL)
370 adg_dim_set_ref2_from_model(dim, model, ref2);
372 if (pos != NULL)
373 adg_dim_set_pos_from_model(dim, model, pos);
375 adg_ldim_set_direction(ldim, direction);
376 return (AdgLDim *) dim;
380 * adg_ldim_set_direction:
381 * @ldim: an #AdgLDim entity
382 * @direction: an angle value, in radians
384 * Sets the direction angle where to extend @ldim.
385 * @direction is normalized by cpml_angle() before being used.
387 * Since: 1.0
389 void
390 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
392 g_return_if_fail(ADG_IS_LDIM(ldim));
393 g_object_set(ldim, "direction", direction, NULL);
397 * adg_ldim_get_direction:
398 * @ldim: an #AdgLDim entity
400 * Gets the direction where @ldim will extend.
402 * Returns: the direction angle in radians
404 * Since: 1.0
406 gdouble
407 adg_ldim_get_direction(AdgLDim *ldim)
409 AdgLDimPrivate *data;
411 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
413 data = adg_ldim_get_instance_private(ldim);
414 return data->direction;
418 * adg_ldim_switch_extension1:
419 * @ldim: an #AdgLDim entity
420 * @new_state: the new state
422 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
423 * @new_state is <constant>FALSE</constant>) the first extension
424 * line of @ldim.
426 * Since: 1.0
428 void
429 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
431 g_return_if_fail(ADG_IS_LDIM(ldim));
432 g_return_if_fail(adg_is_boolean_value(new_state));
433 g_object_set(ldim, "has-extension1", new_state, NULL);
437 * adg_ldim_has_extension1:
438 * @ldim: an #AdgLDim entity
440 * Checks if @ldim should render also the first extension line.
442 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
444 * Since: 1.0
446 gboolean
447 adg_ldim_has_extension1(AdgLDim *ldim)
449 AdgLDimPrivate *data;
451 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
453 data = adg_ldim_get_instance_private(ldim);
454 return data->has_extension1;
458 * adg_ldim_switch_extension2:
459 * @ldim: an #AdgLDim entity
460 * @new_state: the new new_state
462 * Shows (if @new_state is <constant>TRUE</constant>) or hide (if
463 * @new_state is <constant>FALSE</constant>) the second extension
464 * line of @ldim.
466 * Since: 1.0
468 void
469 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
471 g_return_if_fail(ADG_IS_LDIM(ldim));
472 g_return_if_fail(adg_is_boolean_value(new_state));
473 g_object_set(ldim, "has-extension2", new_state, NULL);
477 * adg_ldim_has_extension2:
478 * @ldim: an #AdgLDim entity
480 * Checks if @ldim should render also the second extension line.
482 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
484 * Since: 1.0
486 gboolean
487 adg_ldim_has_extension2(AdgLDim *ldim)
489 AdgLDimPrivate *data;
491 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
493 data = adg_ldim_get_instance_private(ldim);
494 return data->has_extension2;
498 static void
499 _adg_global_changed(AdgEntity *entity)
501 AdgLDimPrivate *data = adg_ldim_get_instance_private((AdgLDim *) entity);
503 _adg_unset_trail((AdgLDim *) entity);
505 if (_ADG_OLD_ENTITY_CLASS->global_changed)
506 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
508 if (data->marker1)
509 adg_entity_global_changed((AdgEntity *) data->marker1);
511 if (data->marker2)
512 adg_entity_global_changed((AdgEntity *) data->marker2);
515 static void
516 _adg_local_changed(AdgEntity *entity)
518 _adg_unset_trail((AdgLDim *) entity);
520 if (_ADG_OLD_ENTITY_CLASS->local_changed)
521 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
524 static void
525 _adg_invalidate(AdgEntity *entity)
527 AdgLDim *ldim = (AdgLDim *) entity;
529 _adg_dispose_trail(ldim);
530 _adg_dispose_markers(ldim);
531 _adg_unset_trail(ldim);
533 if (_ADG_OLD_ENTITY_CLASS->invalidate)
534 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
537 static void
538 _adg_arrange(AdgEntity *entity)
540 AdgLDim *ldim;
541 AdgDim *dim;
542 AdgLDimPrivate *data;
543 AdgAlignment *quote;
544 AdgDimStyle *dim_style;
545 gboolean outside, detach;
546 const cairo_matrix_t *local;
547 CpmlPair ref1, ref2, pos, base1, base2;
548 CpmlPair pair;
550 if (_ADG_OLD_ENTITY_CLASS->arrange)
551 _ADG_OLD_ENTITY_CLASS->arrange(entity);
553 dim = (AdgDim *) entity;
554 if (! adg_dim_compute_geometry(dim))
555 return;
557 ldim = (AdgLDim *) entity;
558 data = adg_ldim_get_instance_private(ldim);
559 quote = adg_dim_get_quote(dim);
561 _adg_update_entities(ldim);
563 /* Check for cached result */
564 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
565 adg_entity_set_global_map((AdgEntity *) quote, &data->quote.global_map);
566 return;
569 _adg_choose_flags(ldim, &outside, &detach);
571 dim_style = adg_dim_get_dim_style(dim);
572 local = adg_entity_get_local_matrix(entity);
574 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
575 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
576 cpml_pair_copy(&pos, (CpmlPair *) adg_dim_get_pos(dim));
577 cpml_pair_copy(&base1, &data->geometry.base1);
578 cpml_pair_copy(&base2, &data->geometry.base2);
580 cpml_pair_transform(&ref1, local);
581 cpml_pair_transform(&ref2, local);
582 cpml_pair_transform(&pos, local);
583 cpml_pair_transform(&base1, local);
584 base1.x += data->shift.base.x;
585 base1.y += data->shift.base.y;
586 cpml_pair_transform(&base2, local);
587 base2.x += data->shift.base.x;
588 base2.y += data->shift.base.y;
590 pair.x = ref1.x + data->shift.from.x;
591 pair.y = ref1.y + data->shift.from.y;
592 cpml_pair_to_cairo(&pair, &data->cairo.data[13]);
594 cpml_pair_to_cairo(&base1, &data->cairo.data[1]);
596 pair.x = base1.x + data->shift.to.x;
597 pair.y = base1.y + data->shift.to.y;
598 cpml_pair_to_cairo(&pair, &data->cairo.data[15]);
600 pair.x = ref2.x + data->shift.from.x;
601 pair.y = ref2.y + data->shift.from.y;
602 cpml_pair_to_cairo(&pair, &data->cairo.data[17]);
604 cpml_pair_to_cairo(&base2, &data->cairo.data[3]);
606 pair.x = base2.x + data->shift.to.x;
607 pair.y = base2.y + data->shift.to.y;
608 cpml_pair_to_cairo(&pair, &data->cairo.data[19]);
610 /* Calculate the outside segments */
611 if (outside) {
612 gdouble beyond;
613 CpmlVector vector;
615 beyond = adg_dim_style_get_beyond(dim_style);
616 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
618 cpml_pair_from_cairo(&vector, &data->cairo.data[3]);
619 vector.x -= pair.x;
620 vector.y -= pair.y;
621 cpml_vector_set_length(&vector, beyond);
623 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
624 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
626 pair.x -= vector.x;
627 pair.y -= vector.y;
628 cpml_pair_to_cairo(&pair, &data->cairo.data[7]);
630 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
631 cpml_pair_to_cairo(&pair, &data->cairo.data[11]);
633 pair.x += vector.x;
634 pair.y += vector.y;
635 cpml_pair_to_cairo(&pair, &data->cairo.data[9]);
637 data->cairo.data[2].header.length = 2;
638 } else {
639 data->cairo.data[2].header.length = 10;
641 data->cairo.data[10].header.length = 2;
643 _adg_update_quote(ldim, &base1, &base2, &pos, detach, outside,
644 adg_dim_style_get_quote_shift(dim_style)->x);
646 /* Play with header lengths to show or hide the extension lines */
647 if (data->has_extension1) {
648 data->cairo.data[14].header.length = data->has_extension2 ? 2 : 6;
649 } else {
650 gint n = ! outside && ! detach ? 2 : 10;
651 data->cairo.data[14].header.length = 2;
652 data->cairo.data[n].header.length += 4;
653 if (!data->has_extension2)
654 data->cairo.data[n].header.length += 4;
657 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
659 /* Update the markers, if present */
660 if (data->trail != NULL) {
661 if (data->marker1 != NULL)
662 adg_marker_set_segment(data->marker1, data->trail, outside ? 2 : 1);
663 if (data->marker2 != NULL)
664 adg_marker_set_segment(data->marker2, data->trail, outside ? 3 : 1);
665 } else {
666 _adg_dispose_markers(ldim);
669 _adg_update_extents(ldim);
672 static void
673 _adg_render(AdgEntity *entity, cairo_t *cr)
675 AdgLDim *ldim;
676 AdgDim *dim;
677 AdgLDimPrivate *data;
678 AdgDimStyle *dim_style;
679 AdgDress dress;
680 const cairo_path_t *cairo_path;
682 dim = (AdgDim *) entity;
683 if (! adg_dim_compute_geometry(dim))
684 return;
686 ldim = (AdgLDim *) entity;
687 data = adg_ldim_get_instance_private(ldim);
688 dim_style = adg_dim_get_dim_style(dim);
690 adg_style_apply((AdgStyle *) dim_style, entity, cr);
691 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
693 if (data->marker1)
694 adg_entity_render((AdgEntity *) data->marker1, cr);
695 if (data->marker2)
696 adg_entity_render((AdgEntity *) data->marker2, cr);
698 cairo_transform(cr, adg_entity_get_global_matrix(entity));
699 dress = adg_dim_style_get_line_dress(dim_style);
700 adg_entity_apply_dress(entity, dress, cr);
702 cairo_path = adg_trail_get_cairo_path(data->trail);
703 cairo_append_path(cr, cairo_path);
704 cairo_stroke(cr);
707 static gchar *
708 _adg_default_value(AdgDim *dim)
710 AdgLDim *ldim;
711 AdgLDimPrivate *data;
712 gdouble value;
714 if (! adg_dim_compute_geometry(dim))
715 return g_strdup("undef");
717 ldim = (AdgLDim *) dim;
718 data = adg_ldim_get_instance_private(ldim);
719 value = data->geometry.distance;
721 return adg_dim_get_text(dim, value);
724 static gboolean
725 _adg_compute_geometry(AdgDim *dim)
727 AdgLDim *ldim;
728 AdgLDimPrivate *data;
729 AdgPoint *ref1_point, *ref2_point, *pos_point;
730 const CpmlPair *ref1, *ref2, *pos;
731 CpmlVector baseline, extension;
732 gdouble d, k;
734 ref1_point = adg_dim_get_ref1(dim);
735 if (! adg_point_update(ref1_point)) {
736 adg_dim_geometry_missing(dim, "ref1");
737 return FALSE;
740 ref2_point = adg_dim_get_ref2(dim);
741 if (! adg_point_update(ref2_point)) {
742 adg_dim_geometry_missing(dim, "ref2");
743 return FALSE;
746 pos_point = adg_dim_get_pos(dim);
747 if (! adg_point_update(pos_point)) {
748 adg_dim_geometry_missing(dim, "pos");
749 return FALSE;
752 ref1 = (CpmlPair *) ref1_point;
753 ref2 = (CpmlPair *) ref2_point;
754 if (cpml_pair_equal(ref1, ref2)) {
755 adg_dim_geometry_coincident(dim, "ref1", "ref2", ref1);
756 return FALSE;
759 pos = (CpmlPair *) pos_point;
760 ldim = (AdgLDim *) dim;
761 data = adg_ldim_get_instance_private(ldim);
763 cpml_vector_from_angle(&extension, data->direction);
764 cpml_pair_copy(&baseline, &extension);
765 cpml_vector_normal(&baseline);
767 d = extension.y * baseline.x -
768 extension.x * baseline.y;
769 g_return_val_if_fail(d != 0, FALSE);
771 k = ((pos->y - ref1->y) * baseline.x -
772 (pos->x - ref1->x) * baseline.y) / d;
773 data->geometry.base1.x = ref1->x + k * extension.x;
774 data->geometry.base1.y = ref1->y + k * extension.y;
776 k = ((pos->y - ref2->y) * baseline.x -
777 (pos->x - ref2->x) * baseline.y) / d;
778 data->geometry.base2.x = ref2->x + k * extension.x;
779 data->geometry.base2.y = ref2->y + k * extension.y;
781 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
782 &data->geometry.base2);
784 _adg_update_shift(ldim);
785 return TRUE;
788 static void
789 _adg_update_shift(AdgLDim *ldim)
791 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
792 AdgDimStyle *dim_style;
793 gdouble from_offset, to_offset;
794 gdouble baseline_spacing, level;
795 CpmlVector vector;
797 if (data->shift.is_arranged)
798 return;
800 dim_style = adg_dim_get_dim_style((AdgDim *) ldim);
801 from_offset = adg_dim_style_get_from_offset(dim_style);
802 to_offset = adg_dim_style_get_to_offset(dim_style);
803 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
804 level = adg_dim_get_level((AdgDim *) ldim);
806 cpml_vector_from_angle(&vector, data->direction);
808 cpml_vector_set_length(&vector, from_offset);
809 cpml_pair_copy(&data->shift.from, &vector);
811 cpml_vector_set_length(&vector, to_offset);
812 cpml_pair_copy(&data->shift.to, &vector);
814 cpml_vector_set_length(&vector, level * baseline_spacing);
815 cpml_pair_copy(&data->shift.base, &vector);
818 static void
819 _adg_update_entities(AdgLDim *ldim)
821 AdgEntity *entity = (AdgEntity *) ldim;
822 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
823 AdgDimStyle *dim_style = adg_dim_get_dim_style((AdgDim *) ldim);
825 if (data->trail == NULL)
826 data->trail = adg_trail_new(_adg_trail_callback, ldim);
828 if (data->marker1 == NULL) {
829 data->marker1 = adg_dim_style_marker1_new(dim_style);
830 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
833 if (data->marker2 == NULL) {
834 data->marker2 = adg_dim_style_marker2_new(dim_style);
835 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
839 static void
840 _adg_choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
842 AdgDim *dim;
843 AdgThreeState outside, detached;
844 AdgLDimPrivate *data;
845 const cairo_matrix_t *local, *global;
846 gdouble local_factor, global_factor;
847 gdouble available_space, markers_space, quote_space;
849 dim = (AdgDim *) ldim;
850 outside = adg_dim_get_outside(dim);
851 detached = adg_dim_get_detached(dim);
853 *to_outside = outside == ADG_THREE_STATE_ON;
854 *to_detach = detached == ADG_THREE_STATE_ON;
856 /* On explicit flags, no further investigation is required */
857 if (outside != ADG_THREE_STATE_UNKNOWN &&
858 detached != ADG_THREE_STATE_UNKNOWN)
859 return;
861 data = adg_ldim_get_instance_private(ldim);
862 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
863 global = adg_entity_get_global_matrix((AdgEntity *) ldim);
864 local_factor = fabs(local->xx + local->yy) / 2;
865 global_factor = fabs(global->xx + global->yy) / 2;
866 available_space = data->geometry.distance * local_factor * global_factor;
868 markers_space = 0;
869 if (outside != ADG_THREE_STATE_ON) {
870 if (data->marker1 != NULL)
871 markers_space += adg_marker_get_size(data->marker1);
872 if (data->marker2 != NULL)
873 markers_space += adg_marker_get_size(data->marker2);
875 markers_space *= global_factor;
878 if (detached == ADG_THREE_STATE_ON) {
879 /* Leave at least 0.25 markers_space between the markers */
880 quote_space = markers_space * 0.25;
881 } else {
882 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote(dim);
883 adg_entity_arrange(quote);
884 quote_space = adg_entity_get_extents(quote)->size.x;
887 if (outside == ADG_THREE_STATE_UNKNOWN &&
888 detached == ADG_THREE_STATE_UNKNOWN) {
889 /* Both flags need to be choosed */
890 if (quote_space + markers_space < available_space) {
891 *to_detach = FALSE;
892 *to_outside = FALSE;
893 } else if (quote_space < available_space) {
894 *to_detach = FALSE;
895 *to_outside = TRUE;
896 } else {
897 *to_detach = TRUE;
898 *to_outside = markers_space * 1.25 > available_space;
900 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
901 /* Only the outside flag may be guessed */
902 *to_outside = quote_space + markers_space > available_space;
903 } else {
904 /* Only the detached flag may be guessed */
905 *to_detach = quote_space + markers_space > available_space;
909 static void
910 _adg_update_quote(AdgLDim *ldim, CpmlPair *base1, CpmlPair *base2, CpmlPair *pos,
911 gboolean detach, gboolean outside, gdouble gap)
913 AdgLDimPrivate *data;
914 AdgEntity *quote_entity;
915 CpmlPair middle, factor, org;
916 CpmlVector dir;
917 cairo_matrix_t map;
919 quote_entity = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
920 if (quote_entity == NULL)
921 return;
923 data = adg_ldim_get_instance_private(ldim);
924 middle.x = (base1->x + base2->x) / 2;
925 middle.y = (base1->y + base2->y) / 2;
926 dir.x = base2->x - base1->x;
927 dir.y = base2->y - base1->y;
928 cpml_vector_set_length(&dir, 1);
930 if (detach) {
931 /* Detached quote: position the quote at "pos" */
932 CpmlPair tmp_pair, quote_end;
933 CpmlVector vector;
934 gdouble distance1, distance2, quote_size;
935 gboolean on_side1;
936 cairo_path_data_t *to_extend;
938 /* Set "org" to the properly converted "pos" coordinates */
939 org.x = pos->x + data->shift.base.x;
940 org.y = pos->y + data->shift.base.y;
942 /* Check in which side of the base line the quote must be rendered.
943 * This is done by checking if the (pos..middle) vector is closer
944 * to the base line vector or to its inverse, that is if they are
945 * concordants or discordants. For this purpose, an algorithm
946 * based on squared distances is used. */
947 tmp_pair.x = org.x - middle.x;
948 tmp_pair.y = org.y - middle.y;
949 vector.x = -dir.x;
950 vector.y = -dir.y;
951 distance2 = cpml_pair_squared_distance(&dir, &tmp_pair);
952 distance1 = cpml_pair_squared_distance(&vector, &tmp_pair);
953 on_side1 = distance2 > distance1;
955 /* Properly align the quote, depending on its side */
956 factor.x = on_side1 ? 1 : 0;
957 if (! on_side1)
958 vector = dir;
960 /* Add a gap between the quote and the extension line */
961 org.x += vector.x * gap;
962 org.y += vector.y * gap;
964 /* Calculate the end point (on the base line) of the quote */
965 quote_size = adg_entity_get_extents(quote_entity)->size.x;
966 quote_end.x = org.x + vector.x * quote_size;
967 quote_end.y = org.y + vector.y * quote_size;
969 /* Extends the base line to include the "quote_end" pair,
970 * so a detached quote is properly underlined */
971 if (outside) {
972 to_extend = &data->cairo.data[on_side1 ? 7 : 9];
973 } else {
974 to_extend = &data->cairo.data[9];
975 cpml_pair_to_cairo(&middle, &data->cairo.data[9]);
976 cpml_pair_to_cairo(on_side1 ? base1 : base2, &data->cairo.data[11]);
977 data->cairo.data[2].header.length = 6;
980 /* Extend the base line only if needed */
981 cpml_pair_from_cairo(&tmp_pair, to_extend);
982 distance1 = cpml_pair_squared_distance(&quote_end, &middle);
983 distance2 = cpml_pair_squared_distance(&tmp_pair, &middle);
984 if (distance1 > distance2)
985 cpml_pair_to_cairo(&quote_end, to_extend);
986 } else {
987 /* Center the quote in the middle of the base line */
988 factor.x = 0.5;
989 cpml_pair_copy(&org, &middle);
992 factor.y = 0;
993 adg_alignment_set_factor((AdgAlignment *) quote_entity, &factor);
994 cairo_matrix_init(&map, dir.x, dir.y, -dir.y, dir.x, org.x, org.y);
995 adg_entity_set_global_map(quote_entity, &map);
996 adg_entity_arrange(quote_entity);
998 adg_matrix_copy(&data->quote.global_map, &map);
1001 static void
1002 _adg_update_extents(AdgLDim *ldim)
1004 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1005 AdgEntity *quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
1006 CpmlExtents new_extents;
1007 const CpmlExtents *extents;
1008 AdgEntity *marker_entity;
1010 new_extents.is_defined = FALSE;
1012 /* The quote is always present (otherwise something bad happened) */
1013 extents = adg_entity_get_extents(quote);
1014 if (extents != NULL)
1015 cpml_extents_add(&new_extents, extents);
1017 if (data->trail != NULL) {
1018 extents = adg_trail_get_extents(data->trail);
1019 if (extents != NULL)
1020 cpml_extents_add(&new_extents, extents);
1023 if (data->marker1 != NULL) {
1024 marker_entity = (AdgEntity *) data->marker1;
1025 adg_entity_local_changed(marker_entity);
1026 adg_entity_arrange(marker_entity);
1027 extents = adg_entity_get_extents(marker_entity);
1028 if (extents != NULL)
1029 cpml_extents_add(&new_extents, extents);
1032 if (data->marker2 != NULL) {
1033 marker_entity = (AdgEntity *) data->marker2;
1034 adg_entity_local_changed(marker_entity);
1035 adg_entity_arrange(marker_entity);
1036 extents = adg_entity_get_extents(marker_entity);
1037 if (extents != NULL)
1038 cpml_extents_add(&new_extents, extents);
1041 adg_entity_set_extents((AdgEntity *) ldim, &new_extents);
1044 static void
1045 _adg_unset_trail(AdgLDim *ldim)
1047 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1049 if (data->trail != NULL)
1050 adg_model_clear((AdgModel *) data->trail);
1052 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1055 static void
1056 _adg_dispose_trail(AdgLDim *ldim)
1058 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1060 if (data->trail != NULL) {
1061 g_object_unref(data->trail);
1062 data->trail = NULL;
1066 static void
1067 _adg_dispose_markers(AdgLDim *ldim)
1069 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1071 if (data->marker1 != NULL) {
1072 g_object_unref(data->marker1);
1073 data->marker1 = NULL;
1076 if (data->marker2 != NULL) {
1077 g_object_unref(data->marker2);
1078 data->marker2 = NULL;
1082 static cairo_path_t *
1083 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1085 AdgLDim *ldim = (AdgLDim *) user_data;
1086 AdgLDimPrivate *data = adg_ldim_get_instance_private(ldim);
1087 return &data->cairo.path;