[AdgLDim] The trail extents are subject to the global matrix
[adg.git] / src / adg / adg-ldim.c
blob38805e6357822517cc233b466701b45938355948
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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.
26 **/
28 /**
29 * AdgLDim:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-internal.h"
37 #include "adg-ldim.h"
38 #include "adg-ldim-private.h"
39 #include "adg-dim-private.h"
40 #include "adg-dim-style.h"
42 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_ldim_parent_class)
43 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
46 enum {
47 PROP_0,
48 PROP_DIRECTION,
49 PROP_HAS_EXTENSION1,
50 PROP_HAS_EXTENSION2
54 static void dispose (GObject *object);
55 static void get_property (GObject *object,
56 guint param_id,
57 GValue *value,
58 GParamSpec *pspec);
59 static void set_property (GObject *object,
60 guint param_id,
61 const GValue *value,
62 GParamSpec *pspec);
63 static void _adg_global_changed (AdgEntity *entity);
64 static void local_changed (AdgEntity *entity);
65 static void invalidate (AdgEntity *entity);
66 static void arrange (AdgEntity *entity);
67 static void render (AdgEntity *entity,
68 cairo_t *cr);
69 static gchar * default_value (AdgDim *dim);
70 static gboolean _adg_set_direction (AdgLDim *ldim,
71 gdouble direction);
72 static void update_geometry (AdgLDim *ldim);
73 static void update_shift (AdgLDim *ldim);
74 static void update_entities (AdgLDim *ldim);
75 static void choose_flags (AdgLDim *ldim,
76 gboolean *to_outside,
77 gboolean *to_detach);
78 static void unset_trail (AdgLDim *ldim);
79 static void dispose_markers (AdgLDim *ldim);
80 static CpmlPath * trail_callback (AdgTrail *trail,
81 gpointer user_data);
84 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
87 static void
88 adg_ldim_class_init(AdgLDimClass *klass)
90 GObjectClass *gobject_class;
91 AdgEntityClass *entity_class;
92 AdgDimClass *dim_class;
93 GParamSpec *param;
95 gobject_class = (GObjectClass *) klass;
96 entity_class = (AdgEntityClass *) klass;
97 dim_class = (AdgDimClass *) klass;
99 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
101 gobject_class->dispose = dispose;
102 gobject_class->get_property = get_property;
103 gobject_class->set_property = set_property;
105 entity_class->global_changed = _adg_global_changed;
106 entity_class->local_changed = local_changed;
107 entity_class->invalidate = invalidate;
108 entity_class->arrange = arrange;
109 entity_class->render = render;
111 dim_class->default_value = default_value;
113 param = g_param_spec_double("direction",
114 P_("Direction"),
115 P_("The inclination angle of the extension lines"),
116 -G_PI, G_PI, ADG_DIR_RIGHT,
117 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
118 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
120 param = g_param_spec_boolean("has-extension1",
121 P_("Has First Extension Line flag"),
122 P_("Show (TRUE) or hide (FALSE) the first extension line"),
123 TRUE, G_PARAM_READWRITE);
124 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
126 param = g_param_spec_boolean("has-extension2",
127 P_("Has Second Extension Line flag"),
128 P_("Show (TRUE) or hide (FALSE) the second extension line"),
129 TRUE, G_PARAM_READWRITE);
130 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
133 static void
134 adg_ldim_init(AdgLDim *ldim)
136 AdgLDimPrivate *data;
137 cairo_path_data_t move_to, line_to;
139 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
140 move_to.header.type = CPML_MOVE;
141 move_to.header.length = 2;
142 line_to.header.type = CPML_LINE;
143 line_to.header.length = 2;
145 data->direction = ADG_DIR_RIGHT;
146 data->has_extension1 = TRUE;
147 data->has_extension2 = TRUE;
149 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
150 data->cpml.path.data = data->cpml.data;
151 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
152 data->cpml.path.data[0] = move_to;
153 data->cpml.path.data[2] = line_to;
154 data->cpml.path.data[4] = move_to;
155 data->cpml.path.data[6] = line_to;
156 data->cpml.path.data[8] = move_to;
157 data->cpml.path.data[10] = line_to;
158 data->cpml.path.data[12] = move_to;
159 data->cpml.path.data[14] = line_to;
160 data->cpml.path.data[16] = move_to;
161 data->cpml.path.data[18] = line_to;
163 data->trail = NULL;
164 data->marker1 = NULL;
165 data->marker2 = NULL;
167 data->geometry.is_arranged = FALSE;
168 data->shift.is_arranged = FALSE;
170 ldim->data = data;
173 static void
174 dispose(GObject *object)
176 dispose_markers((AdgLDim *) object);
178 if (PARENT_OBJECT_CLASS->dispose)
179 PARENT_OBJECT_CLASS->dispose(object);
182 static void
183 get_property(GObject *object,
184 guint prop_id, GValue *value, GParamSpec *pspec)
186 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
188 switch (prop_id) {
189 case PROP_DIRECTION:
190 g_value_set_double(value, data->direction);
191 break;
192 case PROP_HAS_EXTENSION1:
193 g_value_set_boolean(value, data->has_extension1);
194 break;
195 case PROP_HAS_EXTENSION2:
196 g_value_set_boolean(value, data->has_extension2);
197 break;
198 default:
199 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
200 break;
204 static void
205 set_property(GObject *object,
206 guint prop_id, const GValue *value, GParamSpec *pspec)
208 AdgLDim *ldim;
209 AdgLDimPrivate *data;
211 ldim = (AdgLDim *) object;
212 data = ldim->data;
214 switch (prop_id) {
215 case PROP_DIRECTION:
216 _adg_set_direction(ldim, g_value_get_double(value));
217 break;
218 case PROP_HAS_EXTENSION1:
219 data->has_extension1 = g_value_get_boolean(value);
220 break;
221 case PROP_HAS_EXTENSION2:
222 data->has_extension2 = g_value_get_boolean(value);
223 break;
224 default:
225 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
226 break;
232 * adg_ldim_new:
234 * Creates a new - undefined - linear dimension. You must, at least,
235 * define the start of the dimension in #AdgDim:ref1, the end in
236 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
237 * any valid #AdgDim method. The director of the dimension (that is,
238 * if it is horizontal, vertical or oblique at a specific angle)
239 * should be specified with adg_ldim_set_direction().
241 * Returns: the newly created linear dimension entity
243 AdgLDim *
244 adg_ldim_new(void)
246 return g_object_new(ADG_TYPE_LDIM, NULL);
250 * adg_ldim_new_full:
251 * @ref1: the first reference point
252 * @ref2: the second reference point
253 * @pos: the position reference
254 * @direction: angle where to extend the dimension
256 * Creates a new linear dimension, specifing all the needed properties in
257 * one shot.
259 * Returns: the newly created linear dimension entity
261 AdgLDim *
262 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
263 const AdgPair *pos, gdouble direction)
265 AdgLDim *ldim;
266 AdgDim *dim;
268 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
269 dim = (AdgDim *) ldim;
271 adg_dim_set_ref1_from_pair(dim, ref1);
272 adg_dim_set_ref2_from_pair(dim, ref2);
273 adg_dim_set_pos_from_pair(dim, pos);
275 return ldim;
279 * adg_ldim_new_full_explicit:
280 * @ref1_x: the x coordinate of the first reference point
281 * @ref1_y: the y coordinate of the first reference point
282 * @ref2_x: the x coordinate of the second reference point
283 * @ref2_y: the y coordinate of the second reference point
284 * @pos_x: the x coordinate of the position reference
285 * @pos_y: the y coordinate of the position reference
286 * @direction: angle where to extend the dimension
288 * Wrappes adg_ldim_new_full() with explicit values.
290 * Returns: the newly created linear dimension entity
292 AdgLDim *
293 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
294 gdouble ref2_x, gdouble ref2_y,
295 gdouble pos_x, gdouble pos_y, gdouble direction)
297 AdgPair ref1;
298 AdgPair ref2;
299 AdgPair pos;
301 ref1.x = ref1_x;
302 ref1.y = ref1_y;
303 ref2.x = ref2_x;
304 ref2.y = ref2_y;
305 pos.x = pos_x;
306 pos.y = pos_y;
308 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
312 * adg_ldim_new_full_from_model:
313 * @model: the model from which the named pairs are taken
314 * @ref1: the first reference point
315 * @ref2: the second reference point
316 * @pos: the position reference
317 * @direction: angle where to extend the dimension
319 * Creates a new linear dimension, specifing all the needed properties in
320 * one shot and using named pairs from @model.
322 * Returns: the newly created linear dimension entity
324 AdgLDim *
325 adg_ldim_new_full_from_model(AdgModel *model,
326 const gchar *ref1, const gchar *ref2,
327 const gchar *pos, gdouble direction)
329 AdgLDim *ldim;
330 AdgDim *dim;
332 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
333 dim = (AdgDim *) ldim;
335 adg_dim_set_ref1_from_model(dim, model, ref1);
336 adg_dim_set_ref2_from_model(dim, model, ref2);
337 adg_dim_set_pos_from_model(dim, model, pos);
339 return ldim;
343 * adg_ldim_set_direction:
344 * @ldim: an #AdgLDim entity
345 * @direction: an angle value, in radians
347 * Sets the direction angle where to extend @ldim.
348 * @direction is normalized by cpml_angle() before being used.
350 void
351 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
353 g_return_if_fail(ADG_IS_LDIM(ldim));
355 if (_adg_set_direction(ldim, direction))
356 g_object_notify((GObject *) ldim, "direction");
360 * adg_ldim_get_direction:
361 * @ldim: an #AdgLDim entity
363 * Gets the direction where @ldim will extend.
365 * Returns: the direction angle in radians
367 gdouble
368 adg_ldim_get_direction(AdgLDim *ldim)
370 AdgLDimPrivate *data;
372 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
374 data = ldim->data;
376 return data->direction;
380 * adg_ldim_switch_extension1:
381 * @ldim: an #AdgLDim entity
382 * @new_state: the new state
384 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
385 * the first extension line of @ldim.
387 void
388 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
390 AdgLDimPrivate *data;
392 g_return_if_fail(ADG_IS_LDIM(ldim));
393 g_return_if_fail(adg_is_boolean_value(new_state));
395 data = ldim->data;
397 if (data->has_extension1 != new_state) {
398 data->has_extension1 = new_state;
399 g_object_notify((GObject *) ldim, "has-extension1");
404 * adg_ldim_has_extension1:
405 * @ldim: an #AdgLDim entity
407 * Checks if @ldim should render also the first extension line.
409 * Returns: %TRUE on first extension line presents, %FALSE otherwise
411 gboolean
412 adg_ldim_has_extension1(AdgLDim *ldim)
414 AdgLDimPrivate *data;
416 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
418 data = ldim->data;
420 return data->has_extension1;
424 * adg_ldim_switch_extension2:
425 * @ldim: an #AdgLDim entity
426 * @new_state: the new new_state
428 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
429 * the second extension line of @ldim.
431 void
432 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
434 AdgLDimPrivate *data;
436 g_return_if_fail(ADG_IS_LDIM(ldim));
437 g_return_if_fail(adg_is_boolean_value(new_state));
439 data = ldim->data;
441 if (data->has_extension2 != new_state) {
442 data->has_extension2 = new_state;
443 g_object_notify((GObject *) ldim, "has-extension2");
448 * adg_ldim_has_extension2:
449 * @ldim: an #AdgLDim entity
451 * Checks if @ldim should render also the second extension line.
453 * Returns: %TRUE on first extension line presents, %FALSE otherwise
455 gboolean
456 adg_ldim_has_extension2(AdgLDim *ldim)
458 AdgLDimPrivate *data;
460 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
462 data = ldim->data;
464 return data->has_extension2;
468 static void
469 _adg_global_changed(AdgEntity *entity)
471 AdgLDimPrivate *data = ((AdgLDim *) entity)->data;
473 unset_trail((AdgLDim *) entity);
475 if (PARENT_ENTITY_CLASS->global_changed)
476 PARENT_ENTITY_CLASS->global_changed(entity);
478 if (data->marker1 != NULL)
479 adg_entity_global_changed((AdgEntity *) data->marker1);
481 if (data->marker2 != NULL)
482 adg_entity_global_changed((AdgEntity *) data->marker2);
485 static void
486 local_changed(AdgEntity *entity)
488 unset_trail((AdgLDim *) entity);
490 if (PARENT_ENTITY_CLASS->local_changed)
491 PARENT_ENTITY_CLASS->local_changed(entity);
494 static void
495 invalidate(AdgEntity *entity)
497 AdgLDim *ldim;
498 AdgLDimPrivate *data;
500 ldim = (AdgLDim *) entity;
501 data = ldim->data;
503 dispose_markers(ldim);
504 data->geometry.is_arranged = FALSE;
505 data->shift.is_arranged = FALSE;
506 unset_trail(ldim);
508 if (PARENT_ENTITY_CLASS->invalidate)
509 PARENT_ENTITY_CLASS->invalidate(entity);
512 /* TODO: split the following crap in more manageable functions */
513 static void
514 arrange(AdgEntity *entity)
516 AdgLDim *ldim;
517 AdgDim *dim;
518 AdgLDimPrivate *data;
519 AdgAlignment *quote;
520 AdgDimStyle *dim_style;
521 gboolean to_outside, to_detach;
522 const AdgMatrix *local;
523 AdgPair ref1, ref2, base1, base2;
524 AdgPair pair;
525 gint n;
526 CpmlExtents extents;
528 if (PARENT_ENTITY_CLASS->arrange)
529 PARENT_ENTITY_CLASS->arrange(entity);
531 ldim = (AdgLDim *) entity;
532 dim = (AdgDim *) ldim;
533 data = ldim->data;
534 quote = adg_dim_get_quote(dim);
536 update_geometry(ldim);
537 update_shift(ldim);
538 update_entities(ldim);
540 /* Check for cached result */
541 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
542 AdgEntity *quote_entity = (AdgEntity *) quote;
543 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
544 return;
547 choose_flags(ldim, &to_outside, &to_detach);
549 dim_style = GET_DIM_STYLE(dim);
550 local = adg_entity_get_local_matrix(entity);
551 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
552 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
553 cpml_pair_copy(&base1, &data->geometry.base1);
554 cpml_pair_copy(&base2, &data->geometry.base2);
556 cpml_pair_transform(&ref1, local);
557 cpml_pair_transform(&ref2, local);
558 cpml_pair_transform(&base1, local);
559 base1.x += data->shift.base.x;
560 base1.y += data->shift.base.y;
561 cpml_pair_transform(&base2, local);
562 base2.x += data->shift.base.x;
563 base2.y += data->shift.base.y;
565 pair.x = ref1.x + data->shift.from.x;
566 pair.y = ref1.y + data->shift.from.y;
567 cpml_pair_to_cairo(&pair, &data->cpml.data[13]);
569 cpml_pair_to_cairo(&base1, &data->cpml.data[1]);
571 pair.x = base1.x + data->shift.to.x;
572 pair.y = base1.y + data->shift.to.y;
573 cpml_pair_to_cairo(&pair, &data->cpml.data[15]);
575 pair.x = ref2.x + data->shift.from.x;
576 pair.y = ref2.y + data->shift.from.y;
577 cpml_pair_to_cairo(&pair, &data->cpml.data[17]);
579 cpml_pair_to_cairo(&base2, &data->cpml.data[3]);
581 pair.x = base2.x + data->shift.to.x;
582 pair.y = base2.y + data->shift.to.y;
583 cpml_pair_to_cairo(&pair, &data->cpml.data[19]);
585 /* Calculate the outside segments */
586 if (to_outside) {
587 gdouble beyond;
588 CpmlVector vector;
590 beyond = adg_dim_style_get_beyond(dim_style);
591 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
593 cpml_pair_from_cairo(&vector, &data->cpml.data[3]);
594 vector.x -= pair.x;
595 vector.y -= pair.y;
596 cpml_vector_set_length(&vector, beyond);
598 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
599 cpml_pair_to_cairo(&pair, &data->cpml.data[5]);
601 pair.x -= vector.x;
602 pair.y -= vector.y;
603 cpml_pair_to_cairo(&pair, &data->cpml.data[7]);
605 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
606 cpml_pair_to_cairo(&pair, &data->cpml.data[11]);
608 pair.x += vector.x;
609 pair.y += vector.y;
610 cpml_pair_to_cairo(&pair, &data->cpml.data[9]);
612 data->cpml.data[2].header.length = 2;
613 n = 10;
614 } else {
615 data->cpml.data[2].header.length = 10;
616 n = 2;
619 data->cpml.data[10].header.length = 2;
620 extents.is_defined = FALSE;
622 /* Add the quote */
623 if (quote != NULL) {
624 AdgEntity *quote_entity;
625 gdouble angle;
626 AdgPair middle, factor;
627 AdgMatrix map;
629 quote_entity = (AdgEntity *) quote;
630 angle = adg_dim_quote_angle(dim, data->direction + G_PI_2);
631 middle.x = (base1.x + base2.x) / 2;
632 middle.y = (base1.y + base2.y) / 2;
633 factor.x = factor.y = 0;
635 if (to_detach) {
636 /* Detached quote: position the quote at "pos" */
637 AdgPair p_line, p_quote;
638 gdouble same_sd, opposite_sd, quote_size;
639 gint n_side;
640 cairo_path_data_t *to_extend;
642 /* Set "pair" to the properly converted "pos" coordinates */
643 cpml_pair_copy(&pair, adg_point_get_pair(adg_dim_get_pos(dim)));
644 cpml_pair_transform(&pair, local);
645 pair.x += data->shift.base.x;
646 pair.y += data->shift.base.y;
648 /* Taking "middle" as the reference, check if the quote
649 * is on the _left_ or on the _right_ side. This is got
650 * by checking if (pos-middle) is closer to the quote
651 * vector or to the negated quote vector by using an
652 * algorithm based on the squared distances. */
653 p_line.x = pair.x - middle.x;
654 p_line.y = pair.y - middle.y;
655 cpml_vector_from_angle(&p_quote, angle);
657 same_sd = cpml_pair_squared_distance(&p_quote, &p_line);
658 p_quote.x = -p_quote.x;
659 p_quote.y = -p_quote.y;
660 opposite_sd = cpml_pair_squared_distance(&p_quote, &p_line);
661 quote_size = adg_entity_get_extents(quote_entity)->size.x;
663 /* Properly align the quote, depending on its side */
664 if (same_sd > opposite_sd) {
665 factor.x = 1;
666 } else {
667 factor.x = 0;
668 p_quote.x = -p_quote.x;
669 p_quote.y = -p_quote.y;
672 cpml_vector_set_length(&p_quote, quote_size);
673 p_quote.x += pair.x;
674 p_quote.y += pair.y;
676 /* Extends the base line to include the "p_quote" pair,
677 * that is underline a detached quote */
678 if (cpml_pair_squared_distance(&p_quote, &base1) >
679 cpml_pair_squared_distance(&p_quote, &base2))
680 n_side = 2;
681 else
682 n_side = 1;
684 if (to_outside) {
685 to_extend = &data->cpml.data[n_side == 1 ? 7 : 9];
686 } else {
687 to_extend = &data->cpml.data[9];
688 cpml_pair_to_cairo(&middle, &data->cpml.data[9]);
689 cpml_pair_to_cairo(n_side == 1 ? &base1 : &base2,
690 &data->cpml.data[11]);
691 data->cpml.data[2].header.length = 6;
692 n = 10;
695 cpml_pair_from_cairo(&p_line, to_extend);
697 /* Extend the base line only if needed */
698 if (cpml_pair_squared_distance(&p_quote, &middle) >
699 cpml_pair_squared_distance(&p_line, &middle))
700 cpml_pair_to_cairo(&p_quote, to_extend);
701 } else {
702 /* Center the quote in the middle of the base line */
703 factor.x = 0.5;
704 cpml_pair_copy(&pair, &middle);
707 adg_alignment_set_factor(quote, &factor);
708 cairo_matrix_init_translate(&map, pair.x, pair.y);
709 cairo_matrix_rotate(&map, angle);
710 adg_entity_set_global_map(quote_entity, &map);
711 adg_entity_arrange(quote_entity);
712 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
714 adg_matrix_copy(&data->quote.global_map,
715 adg_entity_get_global_map(quote_entity));
718 /* Play with header lengths to show or hide the extension lines */
719 if (data->has_extension1) {
720 data->cpml.data[14].header.length = data->has_extension2 ? 2 : 6;
721 } else {
722 data->cpml.data[14].header.length = 2;
723 data->cpml.data[n].header.length += 4;
724 if (!data->has_extension2)
725 data->cpml.data[n].header.length += 4;
728 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
730 if (data->trail) {
731 const AdgMatrix *global;
732 CpmlExtents trail_extents;
734 global = adg_entity_get_global_matrix(entity);
735 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
737 cpml_extents_transform(&trail_extents, global);
738 cpml_extents_add(&extents, &trail_extents);
741 if (data->marker1 != NULL) {
742 AdgEntity *marker1_entity = (AdgEntity *) data->marker1;
743 adg_marker_set_segment(data->marker1, data->trail, to_outside ? 2 : 1);
744 adg_entity_local_changed(marker1_entity);
745 adg_entity_arrange(marker1_entity);
746 cpml_extents_add(&extents, adg_entity_get_extents(marker1_entity));
749 if (data->marker2 != NULL) {
750 AdgEntity *marker2_entity = (AdgEntity *) data->marker2;
751 adg_marker_set_segment(data->marker2, data->trail, to_outside ? 3 : 1);
752 adg_entity_local_changed(marker2_entity);
753 adg_entity_arrange(marker2_entity);
754 cpml_extents_add(&extents, adg_entity_get_extents(marker2_entity));
757 adg_entity_set_extents(entity, &extents);
760 static void
761 render(AdgEntity *entity, cairo_t *cr)
763 AdgLDim *ldim;
764 AdgDim *dim;
765 AdgLDimPrivate *data;
766 AdgDimStyle *dim_style;
767 AdgDress dress;
768 const cairo_path_t *cairo_path;
770 ldim = (AdgLDim *) entity;
771 dim = (AdgDim *) entity;
772 data = ldim->data;
773 dim_style = GET_DIM_STYLE(dim);
775 adg_style_apply((AdgStyle *) dim_style, entity, cr);
777 if (data->marker1 != NULL)
778 adg_entity_render((AdgEntity *) data->marker1, cr);
780 if (data->marker2 != NULL)
781 adg_entity_render((AdgEntity *) data->marker2, cr);
783 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
785 dress = adg_dim_style_get_line_dress(dim_style);
786 adg_entity_apply_dress(entity, dress, cr);
788 cairo_path = adg_trail_get_cairo_path(data->trail);
789 cairo_append_path(cr, cairo_path);
790 cairo_stroke(cr);
793 static gchar *
794 default_value(AdgDim *dim)
796 AdgLDim *ldim;
797 AdgLDimPrivate *data;
798 AdgDimStyle *dim_style;
799 const gchar *format;
801 ldim = (AdgLDim *) dim;
802 data = ldim->data;
803 dim_style = GET_DIM_STYLE(dim);
804 format = adg_dim_style_get_number_format(dim_style);
806 update_geometry(ldim);
808 return g_strdup_printf(format, data->geometry.distance);
811 static gboolean
812 _adg_set_direction(AdgLDim *ldim, gdouble direction)
814 AdgLDimPrivate *data = ldim->data;
816 direction = cpml_angle(direction);
818 if (data->direction == direction)
819 return FALSE;
821 data->direction = direction;
823 return TRUE;
826 static void
827 update_geometry(AdgLDim *ldim)
829 AdgLDimPrivate *data;
830 AdgDim *dim;
831 const AdgPair *ref1, *ref2;
832 const AdgPair *pos;
833 CpmlVector baseline, extension;
834 gdouble d, k;
836 data = ldim->data;
838 if (data->geometry.is_arranged)
839 return;
841 dim = (AdgDim *) ldim;
842 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
843 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
844 pos = adg_point_get_pair(adg_dim_get_pos(dim));
845 cpml_vector_from_angle(&extension, data->direction);
846 cpml_pair_copy(&baseline, &extension);
847 cpml_vector_normal(&baseline);
849 d = extension.y * baseline.x -
850 extension.x * baseline.y;
851 g_return_if_fail(d != 0);
853 k = ((pos->y - ref1->y) * baseline.x -
854 (pos->x - ref1->x) * baseline.y) / d;
855 data->geometry.base1.x = ref1->x + k * extension.x;
856 data->geometry.base1.y = ref1->y + k * extension.y;
858 k = ((pos->y - ref2->y) * baseline.x -
859 (pos->x - ref2->x) * baseline.y) / d;
860 data->geometry.base2.x = ref2->x + k * extension.x;
861 data->geometry.base2.y = ref2->y + k * extension.y;
863 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
864 &data->geometry.base2);
866 data->geometry.is_arranged = TRUE;
869 static void
870 update_shift(AdgLDim *ldim)
872 AdgLDimPrivate *data;
873 AdgDimStyle *dim_style;
874 gdouble from_offset, to_offset;
875 gdouble baseline_spacing, level;
876 CpmlVector vector;
878 data = ldim->data;
880 if (data->shift.is_arranged)
881 return;
883 dim_style = GET_DIM_STYLE(ldim);
884 from_offset = adg_dim_style_get_from_offset(dim_style);
885 to_offset = adg_dim_style_get_to_offset(dim_style);
886 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
887 level = adg_dim_get_level((AdgDim *) ldim);
889 cpml_vector_from_angle(&vector, data->direction);
891 cpml_vector_set_length(&vector, from_offset);
892 cpml_pair_copy(&data->shift.from, &vector);
894 cpml_vector_set_length(&vector, to_offset);
895 cpml_pair_copy(&data->shift.to, &vector);
897 cpml_vector_set_length(&vector, level * baseline_spacing);
898 cpml_pair_copy(&data->shift.base, &vector);
900 data->shift.is_arranged = TRUE;
903 static void
904 update_entities(AdgLDim *ldim)
906 AdgEntity *entity;
907 AdgLDimPrivate *data;
908 AdgDimStyle *dim_style;
910 entity = (AdgEntity *) ldim;
911 data = ldim->data;
912 dim_style = GET_DIM_STYLE(ldim);
914 if (data->trail == NULL)
915 data->trail = adg_trail_new(trail_callback, ldim);
917 if (data->marker1 == NULL) {
918 data->marker1 = adg_dim_style_marker1_new(dim_style);
919 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
922 if (data->marker2 == NULL) {
923 data->marker2 = adg_dim_style_marker2_new(dim_style);
924 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
928 static void
929 choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
931 AdgDim *dim;
932 AdgThreeState outside, detached;
933 AdgLDimPrivate *data;
934 AdgEntity *quote;
935 const AdgMatrix *local;
936 gdouble available_space, markers_space, quote_space;
938 dim = (AdgDim *) ldim;
939 outside = adg_dim_get_outside(dim);
940 detached = adg_dim_get_detached(dim);
942 *to_outside = outside == ADG_THREE_STATE_ON;
943 *to_detach = detached == ADG_THREE_STATE_ON;
945 /* On explicit flags, no further investigation is required */
946 if (outside != ADG_THREE_STATE_UNKNOWN &&
947 detached != ADG_THREE_STATE_UNKNOWN)
948 return;
950 data = ldim->data;
951 quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
952 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
953 available_space = data->geometry.distance * local->xx;
955 if (outside == ADG_THREE_STATE_ON)
956 markers_space = 0;
957 else
958 markers_space =
959 (data->marker1 == NULL ? 0 : adg_marker_get_size(data->marker1)) +
960 (data->marker2 == NULL ? 0 : adg_marker_get_size(data->marker2));
962 /* Leave at least 0.25 marker_space between the markers */
963 if (detached == ADG_THREE_STATE_ON)
964 quote_space = markers_space * 0.25;
965 else
966 quote_space = adg_entity_get_extents(quote)->size.x;
968 if (outside == ADG_THREE_STATE_UNKNOWN &&
969 detached == ADG_THREE_STATE_UNKNOWN) {
970 /* Both flags need to be choosed */
971 if (quote_space + markers_space < available_space) {
972 *to_detach = FALSE;
973 *to_outside = FALSE;
974 } else if (quote_space < available_space) {
975 *to_detach = FALSE;
976 *to_outside = TRUE;
977 } else {
978 *to_detach = TRUE;
979 *to_outside = markers_space * 1.25 > available_space;
981 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
982 /* Only the outside flag may be guessed */
983 *to_outside = quote_space + markers_space > available_space;
984 } else {
985 /* Only the detached flag may be guessed */
986 *to_detach = quote_space + markers_space > available_space;
990 static void
991 unset_trail(AdgLDim *ldim)
993 AdgLDimPrivate *data = ldim->data;
995 if (data->trail != NULL)
996 adg_model_clear((AdgModel *) data->trail);
998 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1001 static void
1002 dispose_markers(AdgLDim *ldim)
1004 AdgLDimPrivate *data = ldim->data;
1006 if (data->trail != NULL) {
1007 g_object_unref(data->trail);
1008 data->trail = NULL;
1011 if (data->marker1 != NULL) {
1012 g_object_unref(data->marker1);
1013 data->marker1 = NULL;
1016 if (data->marker2 != NULL) {
1017 g_object_unref(data->marker2);
1018 data->marker2 = NULL;
1022 static CpmlPath *
1023 trail_callback(AdgTrail *trail, gpointer user_data)
1025 AdgLDim *ldim;
1026 AdgLDimPrivate *data;
1028 ldim = (AdgLDim *) user_data;
1029 data = ldim->data;
1031 return &data->cpml.path;