[AdgMarker] Added missing APIs
[adg.git] / adg / adg-ldim.c
blob294e1f48080a51a37e122ad55f3145a9fca5f480
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 local_changed (AdgEntity *entity);
64 static void invalidate (AdgEntity *entity);
65 static void arrange (AdgEntity *entity);
66 static void render (AdgEntity *entity,
67 cairo_t *cr);
68 static gchar * default_value (AdgDim *dim);
69 static gboolean set_direction (AdgLDim *ldim,
70 gdouble direction);
71 static void update_geometry (AdgLDim *ldim);
72 static void update_shift (AdgLDim *ldim);
73 static void update_entities (AdgLDim *ldim);
74 static void choose_flags (AdgLDim *ldim,
75 gboolean *to_outside,
76 gboolean *to_detach);
77 static void unset_trail (AdgLDim *ldim);
78 static void dispose_markers (AdgLDim *ldim);
79 static CpmlPath * trail_callback (AdgTrail *trail,
80 gpointer user_data);
83 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
86 static void
87 adg_ldim_class_init(AdgLDimClass *klass)
89 GObjectClass *gobject_class;
90 AdgEntityClass *entity_class;
91 AdgDimClass *dim_class;
92 GParamSpec *param;
94 gobject_class = (GObjectClass *) klass;
95 entity_class = (AdgEntityClass *) klass;
96 dim_class = (AdgDimClass *) klass;
98 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
100 gobject_class->dispose = dispose;
101 gobject_class->get_property = get_property;
102 gobject_class->set_property = set_property;
104 entity_class->local_changed = local_changed;
105 entity_class->invalidate = invalidate;
106 entity_class->arrange = arrange;
107 entity_class->render = render;
109 dim_class->default_value = default_value;
111 param = g_param_spec_double("direction",
112 P_("Direction"),
113 P_("The inclination angle of the extension lines"),
114 -G_PI, G_PI, ADG_DIR_RIGHT,
115 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
116 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
118 param = g_param_spec_boolean("has-extension1",
119 P_("Has First Extension Line flag"),
120 P_("Show (TRUE) or hide (FALSE) the first extension line"),
121 TRUE, G_PARAM_READWRITE);
122 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
124 param = g_param_spec_boolean("has-extension2",
125 P_("Has Second Extension Line flag"),
126 P_("Show (TRUE) or hide (FALSE) the second extension line"),
127 TRUE, G_PARAM_READWRITE);
128 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
131 static void
132 adg_ldim_init(AdgLDim *ldim)
134 AdgLDimPrivate *data;
135 cairo_path_data_t move_to, line_to;
137 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
138 move_to.header.type = CPML_MOVE;
139 move_to.header.length = 2;
140 line_to.header.type = CPML_LINE;
141 line_to.header.length = 2;
143 data->direction = ADG_DIR_RIGHT;
144 data->has_extension1 = TRUE;
145 data->has_extension2 = TRUE;
147 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
148 data->cpml.path.data = data->cpml.data;
149 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
150 data->cpml.path.data[0] = move_to;
151 data->cpml.path.data[2] = line_to;
152 data->cpml.path.data[4] = move_to;
153 data->cpml.path.data[6] = line_to;
154 data->cpml.path.data[8] = move_to;
155 data->cpml.path.data[10] = line_to;
156 data->cpml.path.data[12] = move_to;
157 data->cpml.path.data[14] = line_to;
158 data->cpml.path.data[16] = move_to;
159 data->cpml.path.data[18] = line_to;
161 data->trail = NULL;
162 data->marker1 = NULL;
163 data->marker2 = NULL;
165 data->geometry.is_arranged = FALSE;
166 data->shift.is_arranged = FALSE;
168 ldim->data = data;
171 static void
172 dispose(GObject *object)
174 dispose_markers((AdgLDim *) object);
176 if (PARENT_OBJECT_CLASS->dispose)
177 PARENT_OBJECT_CLASS->dispose(object);
180 static void
181 get_property(GObject *object,
182 guint prop_id, GValue *value, GParamSpec *pspec)
184 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
186 switch (prop_id) {
187 case PROP_DIRECTION:
188 g_value_set_double(value, data->direction);
189 break;
190 case PROP_HAS_EXTENSION1:
191 g_value_set_boolean(value, data->has_extension1);
192 break;
193 case PROP_HAS_EXTENSION2:
194 g_value_set_boolean(value, data->has_extension2);
195 break;
196 default:
197 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
198 break;
202 static void
203 set_property(GObject *object,
204 guint prop_id, const GValue *value, GParamSpec *pspec)
206 AdgLDim *ldim;
207 AdgLDimPrivate *data;
209 ldim = (AdgLDim *) object;
210 data = ldim->data;
212 switch (prop_id) {
213 case PROP_DIRECTION:
214 set_direction(ldim, g_value_get_double(value));
215 break;
216 case PROP_HAS_EXTENSION1:
217 data->has_extension1 = g_value_get_boolean(value);
218 break;
219 case PROP_HAS_EXTENSION2:
220 data->has_extension2 = g_value_get_boolean(value);
221 break;
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
224 break;
230 * adg_ldim_new:
232 * Creates a new - undefined - linear dimension. You must, at least,
233 * define the start of the dimension in #AdgDim:ref1, the end in
234 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
235 * any valid #AdgDim method. The director of the dimension (that is,
236 * if it is horizontal, vertical or oblique at a specific angle)
237 * should be specified with adg_ldim_set_direction().
239 * Returns: the newly created linear dimension entity
241 AdgLDim *
242 adg_ldim_new(void)
244 return g_object_new(ADG_TYPE_LDIM, NULL);
248 * adg_ldim_new_full:
249 * @ref1: the first reference point
250 * @ref2: the second reference point
251 * @pos: the position reference
252 * @direction: angle where to extend the dimension
254 * Creates a new linear dimension, specifing all the needed properties in
255 * one shot.
257 * Returns: the newly created linear dimension entity
259 AdgLDim *
260 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
261 const AdgPair *pos, gdouble direction)
263 AdgLDim *ldim;
264 AdgDim *dim;
266 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
267 dim = (AdgDim *) ldim;
269 adg_dim_set_ref1_from_pair(dim, ref1);
270 adg_dim_set_ref2_from_pair(dim, ref2);
271 adg_dim_set_pos_from_pair(dim, pos);
273 return ldim;
277 * adg_ldim_new_full_explicit:
278 * @ref1_x: the x coordinate of the first reference point
279 * @ref1_y: the y coordinate of the first reference point
280 * @ref2_x: the x coordinate of the second reference point
281 * @ref2_y: the y coordinate of the second reference point
282 * @pos_x: the x coordinate of the position reference
283 * @pos_y: the y coordinate of the position reference
284 * @direction: angle where to extend the dimension
286 * Wrappes adg_ldim_new_full() with explicit values.
288 * Returns: the newly created linear dimension entity
290 AdgLDim *
291 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
292 gdouble ref2_x, gdouble ref2_y,
293 gdouble pos_x, gdouble pos_y, gdouble direction)
295 AdgPair ref1;
296 AdgPair ref2;
297 AdgPair pos;
299 ref1.x = ref1_x;
300 ref1.y = ref1_y;
301 ref2.x = ref2_x;
302 ref2.y = ref2_y;
303 pos.x = pos_x;
304 pos.y = pos_y;
306 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
310 * adg_ldim_new_full_from_model:
311 * @model: the model from which the named pairs are taken
312 * @ref1: the first reference point
313 * @ref2: the second reference point
314 * @pos: the position reference
315 * @direction: angle where to extend the dimension
317 * Creates a new linear dimension, specifing all the needed properties in
318 * one shot and using named pairs from @model.
320 * Returns: the newly created linear dimension entity
322 AdgLDim *
323 adg_ldim_new_full_from_model(AdgModel *model,
324 const gchar *ref1, const gchar *ref2,
325 const gchar *pos, gdouble direction)
327 AdgLDim *ldim;
328 AdgDim *dim;
330 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
331 dim = (AdgDim *) ldim;
333 adg_dim_set_ref1_from_model(dim, model, ref1);
334 adg_dim_set_ref2_from_model(dim, model, ref2);
335 adg_dim_set_pos_from_model(dim, model, pos);
337 return ldim;
341 * adg_ldim_set_direction:
342 * @ldim: an #AdgLDim entity
343 * @direction: an angle value, in radians
345 * Sets the direction angle where to extend @ldim.
346 * @direction is normalized by cpml_angle() before being used.
348 void
349 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
351 g_return_if_fail(ADG_IS_LDIM(ldim));
353 if (set_direction(ldim, direction))
354 g_object_notify((GObject *) ldim, "direction");
358 * adg_ldim_get_direction:
359 * @ldim: an #AdgLDim entity
361 * Gets the direction where @ldim will extend.
363 * Returns: the direction angle in radians
365 gdouble
366 adg_ldim_get_direction(AdgLDim *ldim)
368 AdgLDimPrivate *data;
370 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
372 data = ldim->data;
374 return data->direction;
378 * adg_ldim_switch_extension1:
379 * @ldim: an #AdgLDim entity
380 * @new_state: the new state
382 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
383 * the first extension line of @ldim.
385 void
386 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
388 AdgLDimPrivate *data;
390 g_return_if_fail(ADG_IS_LDIM(ldim));
391 g_return_if_fail(adg_is_boolean_value(new_state));
393 data = ldim->data;
395 if (data->has_extension1 != new_state) {
396 data->has_extension1 = new_state;
397 g_object_notify((GObject *) ldim, "has-extension1");
402 * adg_ldim_has_extension1:
403 * @ldim: an #AdgLDim entity
405 * Checks if @ldim should render also the first extension line.
407 * Returns: %TRUE on first extension line presents, %FALSE otherwise
409 gboolean
410 adg_ldim_has_extension1(AdgLDim *ldim)
412 AdgLDimPrivate *data;
414 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
416 data = ldim->data;
418 return data->has_extension1;
422 * adg_ldim_switch_extension2:
423 * @ldim: an #AdgLDim entity
424 * @new_state: the new new_state
426 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
427 * the second extension line of @ldim.
429 void
430 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
432 AdgLDimPrivate *data;
434 g_return_if_fail(ADG_IS_LDIM(ldim));
435 g_return_if_fail(adg_is_boolean_value(new_state));
437 data = ldim->data;
439 if (data->has_extension2 != new_state) {
440 data->has_extension2 = new_state;
441 g_object_notify((GObject *) ldim, "has-extension2");
446 * adg_ldim_has_extension2:
447 * @ldim: an #AdgLDim entity
449 * Checks if @ldim should render also the second extension line.
451 * Returns: %TRUE on first extension line presents, %FALSE otherwise
453 gboolean
454 adg_ldim_has_extension2(AdgLDim *ldim)
456 AdgLDimPrivate *data;
458 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
460 data = ldim->data;
462 return data->has_extension2;
466 static void
467 local_changed(AdgEntity *entity)
469 unset_trail((AdgLDim *) entity);
471 if (PARENT_ENTITY_CLASS->local_changed)
472 PARENT_ENTITY_CLASS->local_changed(entity);
475 static void
476 invalidate(AdgEntity *entity)
478 AdgLDim *ldim;
479 AdgLDimPrivate *data;
481 ldim = (AdgLDim *) entity;
482 data = ldim->data;
484 dispose_markers(ldim);
485 data->geometry.is_arranged = FALSE;
486 data->shift.is_arranged = FALSE;
487 unset_trail(ldim);
489 if (PARENT_ENTITY_CLASS->invalidate)
490 PARENT_ENTITY_CLASS->invalidate(entity);
493 /* TODO: split the following crap in more manageable functions */
494 static void
495 arrange(AdgEntity *entity)
497 AdgLDim *ldim;
498 AdgDim *dim;
499 AdgLDimPrivate *data;
500 AdgAlignment *quote;
501 AdgDimStyle *dim_style;
502 gboolean to_outside, to_detach;
503 const AdgMatrix *local;
504 AdgPair ref1, ref2, base1, base2;
505 AdgPair pair;
506 gint n;
507 CpmlExtents extents;
509 if (PARENT_ENTITY_CLASS->arrange)
510 PARENT_ENTITY_CLASS->arrange(entity);
512 ldim = (AdgLDim *) entity;
513 dim = (AdgDim *) ldim;
514 data = ldim->data;
515 quote = adg_dim_get_quote(dim);
517 update_geometry(ldim);
518 update_shift(ldim);
519 update_entities(ldim);
521 /* Check for cached result */
522 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
523 AdgEntity *quote_entity = (AdgEntity *) quote;
524 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
525 return;
528 choose_flags(ldim, &to_outside, &to_detach);
530 dim_style = GET_DIM_STYLE(dim);
531 local = adg_entity_get_local_matrix(entity);
532 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
533 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
534 cpml_pair_copy(&base1, &data->geometry.base1);
535 cpml_pair_copy(&base2, &data->geometry.base2);
537 cpml_pair_transform(&ref1, local);
538 cpml_pair_transform(&ref2, local);
539 cpml_pair_transform(&base1, local);
540 base1.x += data->shift.base.x;
541 base1.y += data->shift.base.y;
542 cpml_pair_transform(&base2, local);
543 base2.x += data->shift.base.x;
544 base2.y += data->shift.base.y;
546 pair.x = ref1.x + data->shift.from.x;
547 pair.y = ref1.y + data->shift.from.y;
548 cpml_pair_to_cairo(&pair, &data->cpml.data[13]);
550 cpml_pair_to_cairo(&base1, &data->cpml.data[1]);
552 pair.x = base1.x + data->shift.to.x;
553 pair.y = base1.y + data->shift.to.y;
554 cpml_pair_to_cairo(&pair, &data->cpml.data[15]);
556 pair.x = ref2.x + data->shift.from.x;
557 pair.y = ref2.y + data->shift.from.y;
558 cpml_pair_to_cairo(&pair, &data->cpml.data[17]);
560 cpml_pair_to_cairo(&base2, &data->cpml.data[3]);
562 pair.x = base2.x + data->shift.to.x;
563 pair.y = base2.y + data->shift.to.y;
564 cpml_pair_to_cairo(&pair, &data->cpml.data[19]);
566 /* Calculate the outside segments */
567 if (to_outside) {
568 gdouble beyond;
569 CpmlVector vector;
571 beyond = adg_dim_style_get_beyond(dim_style);
572 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
574 cpml_pair_from_cairo(&vector, &data->cpml.data[3]);
575 vector.x -= pair.x;
576 vector.y -= pair.y;
577 cpml_vector_set_length(&vector, beyond);
579 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
580 cpml_pair_to_cairo(&pair, &data->cpml.data[5]);
582 pair.x -= vector.x;
583 pair.y -= vector.y;
584 cpml_pair_to_cairo(&pair, &data->cpml.data[7]);
586 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
587 cpml_pair_to_cairo(&pair, &data->cpml.data[11]);
589 pair.x += vector.x;
590 pair.y += vector.y;
591 cpml_pair_to_cairo(&pair, &data->cpml.data[9]);
593 data->cpml.data[2].header.length = 2;
594 n = 10;
595 } else {
596 data->cpml.data[2].header.length = 10;
597 n = 2;
600 data->cpml.data[10].header.length = 2;
601 extents.is_defined = FALSE;
603 /* Add the quote */
604 if (quote != NULL) {
605 AdgEntity *quote_entity;
606 gdouble angle;
607 AdgPair middle, factor;
608 AdgMatrix map;
610 quote_entity = (AdgEntity *) quote;
611 angle = adg_dim_quote_angle(dim, data->direction + G_PI_2);
612 middle.x = (base1.x + base2.x) / 2;
613 middle.y = (base1.y + base2.y) / 2;
614 factor.x = factor.y = 0;
616 if (to_detach) {
617 /* Detached quote: position the quote at "pos" */
618 AdgPair p_line, p_quote;
619 gdouble same_sd, opposite_sd, quote_size;
620 gint n_side;
621 cairo_path_data_t *to_extend;
623 /* Set "pair" to the properly converted "pos" coordinates */
624 cpml_pair_copy(&pair, adg_point_get_pair(adg_dim_get_pos(dim)));
625 cpml_pair_transform(&pair, local);
626 pair.x += data->shift.base.x;
627 pair.y += data->shift.base.y;
629 /* Taking "middle" as the reference, check if the quote
630 * is on the _left_ or on the _right_ side. This is got
631 * by checking if (pos-middle) is closer to the quote
632 * vector or to the negated quote vector by using an
633 * algorithm based on the squared distances. */
634 p_line.x = pair.x - middle.x;
635 p_line.y = pair.y - middle.y;
636 cpml_vector_from_angle(&p_quote, angle);
638 same_sd = cpml_pair_squared_distance(&p_quote, &p_line);
639 p_quote.x = -p_quote.x;
640 p_quote.y = -p_quote.y;
641 opposite_sd = cpml_pair_squared_distance(&p_quote, &p_line);
642 quote_size = adg_entity_get_extents(quote_entity)->size.x;
644 /* Properly align the quote, depending on its side */
645 if (same_sd > opposite_sd) {
646 factor.x = 1;
647 } else {
648 factor.x = 0;
649 p_quote.x = -p_quote.x;
650 p_quote.y = -p_quote.y;
653 cpml_vector_set_length(&p_quote, quote_size);
654 p_quote.x += pair.x;
655 p_quote.y += pair.y;
657 /* Extends the base line to include the "p_quote" pair,
658 * that is underline a detached quote */
659 if (cpml_pair_squared_distance(&p_quote, &base1) >
660 cpml_pair_squared_distance(&p_quote, &base2))
661 n_side = 2;
662 else
663 n_side = 1;
665 if (to_outside) {
666 to_extend = &data->cpml.data[n_side == 1 ? 7 : 9];
667 } else {
668 to_extend = &data->cpml.data[9];
669 cpml_pair_to_cairo(&middle, &data->cpml.data[9]);
670 cpml_pair_to_cairo(n_side == 1 ? &base1 : &base2,
671 &data->cpml.data[11]);
672 data->cpml.data[2].header.length = 6;
673 n = 10;
676 cpml_pair_from_cairo(&p_line, to_extend);
678 /* Extend the base line only if needed */
679 if (cpml_pair_squared_distance(&p_quote, &middle) >
680 cpml_pair_squared_distance(&p_line, &middle))
681 cpml_pair_to_cairo(&p_quote, to_extend);
682 } else {
683 /* Center the quote in the middle of the base line */
684 factor.x = 0.5;
685 cpml_pair_copy(&pair, &middle);
688 adg_alignment_set_factor(quote, &factor);
689 cairo_matrix_init_translate(&map, pair.x, pair.y);
690 cairo_matrix_rotate(&map, angle);
691 adg_entity_set_global_map(quote_entity, &map);
692 adg_entity_arrange(quote_entity);
693 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
695 adg_matrix_copy(&data->quote.global_map,
696 adg_entity_get_global_map(quote_entity));
699 /* Play with header lengths to show or hide the extension lines */
700 if (data->has_extension1) {
701 data->cpml.data[14].header.length = data->has_extension2 ? 2 : 6;
702 } else {
703 data->cpml.data[14].header.length = 2;
704 data->cpml.data[n].header.length += 4;
705 if (!data->has_extension2)
706 data->cpml.data[n].header.length += 4;
709 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
710 cpml_extents_add(&extents, adg_trail_get_extents(data->trail));
712 if (data->marker1 != NULL) {
713 AdgEntity *marker1_entity = (AdgEntity *) data->marker1;
714 adg_marker_set_segment(data->marker1, data->trail, to_outside ? 2 : 1);
715 adg_entity_local_changed(marker1_entity);
716 adg_entity_arrange(marker1_entity);
717 cpml_extents_add(&extents, adg_entity_get_extents(marker1_entity));
720 if (data->marker2 != NULL) {
721 AdgEntity *marker2_entity = (AdgEntity *) data->marker2;
722 adg_marker_set_segment(data->marker2, data->trail, to_outside ? 3 : 1);
723 adg_entity_local_changed(marker2_entity);
724 adg_entity_arrange(marker2_entity);
725 cpml_extents_add(&extents, adg_entity_get_extents(marker2_entity));
728 adg_entity_set_extents(entity, &extents);
731 static void
732 render(AdgEntity *entity, cairo_t *cr)
734 AdgLDim *ldim;
735 AdgDim *dim;
736 AdgLDimPrivate *data;
737 AdgDimStyle *dim_style;
738 AdgDress dress;
739 const cairo_path_t *cairo_path;
741 ldim = (AdgLDim *) entity;
742 dim = (AdgDim *) entity;
743 data = ldim->data;
744 dim_style = GET_DIM_STYLE(dim);
746 adg_style_apply((AdgStyle *) dim_style, entity, cr);
748 if (data->marker1 != NULL)
749 adg_entity_render((AdgEntity *) data->marker1, cr);
751 if (data->marker2 != NULL)
752 adg_entity_render((AdgEntity *) data->marker2, cr);
754 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
756 dress = adg_dim_style_get_line_dress(dim_style);
757 adg_entity_apply_dress(entity, dress, cr);
759 cairo_path = adg_trail_get_cairo_path(data->trail);
760 cairo_append_path(cr, cairo_path);
761 cairo_stroke(cr);
764 static gchar *
765 default_value(AdgDim *dim)
767 AdgLDim *ldim;
768 AdgLDimPrivate *data;
769 AdgDimStyle *dim_style;
770 const gchar *format;
772 ldim = (AdgLDim *) dim;
773 data = ldim->data;
774 dim_style = GET_DIM_STYLE(dim);
775 format = adg_dim_style_get_number_format(dim_style);
777 update_geometry(ldim);
779 return g_strdup_printf(format, data->geometry.distance);
782 static gboolean
783 set_direction(AdgLDim *ldim, gdouble direction)
785 AdgLDimPrivate *data = ldim->data;
787 direction = cpml_angle(direction);
789 if (data->direction == direction)
790 return FALSE;
792 data->direction = direction;
794 return TRUE;
797 static void
798 update_geometry(AdgLDim *ldim)
800 AdgLDimPrivate *data;
801 AdgDim *dim;
802 const AdgPair *ref1, *ref2;
803 const AdgPair *pos;
804 CpmlVector baseline, extension;
805 gdouble d, k;
807 data = ldim->data;
809 if (data->geometry.is_arranged)
810 return;
812 dim = (AdgDim *) ldim;
813 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
814 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
815 pos = adg_point_get_pair(adg_dim_get_pos(dim));
816 cpml_vector_from_angle(&extension, data->direction);
817 cpml_pair_copy(&baseline, &extension);
818 cpml_vector_normal(&baseline);
820 d = extension.y * baseline.x -
821 extension.x * baseline.y;
822 g_return_if_fail(d != 0);
824 k = ((pos->y - ref1->y) * baseline.x -
825 (pos->x - ref1->x) * baseline.y) / d;
826 data->geometry.base1.x = ref1->x + k * extension.x;
827 data->geometry.base1.y = ref1->y + k * extension.y;
829 k = ((pos->y - ref2->y) * baseline.x -
830 (pos->x - ref2->x) * baseline.y) / d;
831 data->geometry.base2.x = ref2->x + k * extension.x;
832 data->geometry.base2.y = ref2->y + k * extension.y;
834 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
835 &data->geometry.base2);
837 data->geometry.is_arranged = TRUE;
840 static void
841 update_shift(AdgLDim *ldim)
843 AdgLDimPrivate *data;
844 AdgDimStyle *dim_style;
845 gdouble from_offset, to_offset;
846 gdouble baseline_spacing, level;
847 CpmlVector vector;
849 data = ldim->data;
851 if (data->shift.is_arranged)
852 return;
854 dim_style = GET_DIM_STYLE(ldim);
855 from_offset = adg_dim_style_get_from_offset(dim_style);
856 to_offset = adg_dim_style_get_to_offset(dim_style);
857 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
858 level = adg_dim_get_level((AdgDim *) ldim);
860 cpml_vector_from_angle(&vector, data->direction);
862 cpml_vector_set_length(&vector, from_offset);
863 cpml_pair_copy(&data->shift.from, &vector);
865 cpml_vector_set_length(&vector, to_offset);
866 cpml_pair_copy(&data->shift.to, &vector);
868 cpml_vector_set_length(&vector, level * baseline_spacing);
869 cpml_pair_copy(&data->shift.base, &vector);
871 data->shift.is_arranged = TRUE;
874 static void
875 update_entities(AdgLDim *ldim)
877 AdgLDimPrivate *data;
878 AdgDimStyle *dim_style;
880 data = ldim->data;
881 dim_style = GET_DIM_STYLE(ldim);
883 if (data->trail == NULL)
884 data->trail = adg_trail_new(trail_callback, ldim);
886 if (data->marker1 == NULL)
887 data->marker1 = adg_dim_style_marker1_new(dim_style);
889 if (data->marker2 == NULL)
890 data->marker2 = adg_dim_style_marker2_new(dim_style);
893 static void
894 choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
896 AdgDim *dim;
897 AdgThreeState outside, detached;
898 AdgLDimPrivate *data;
899 AdgEntity *quote;
900 const AdgMatrix *local;
901 gdouble available_space, markers_space, quote_space;
903 dim = (AdgDim *) ldim;
904 outside = adg_dim_get_outside(dim);
905 detached = adg_dim_get_detached(dim);
907 *to_outside = outside == ADG_THREE_STATE_ON;
908 *to_detach = detached == ADG_THREE_STATE_ON;
910 /* On explicit flags, no further investigation is required */
911 if (outside != ADG_THREE_STATE_UNKNOWN &&
912 detached != ADG_THREE_STATE_UNKNOWN)
913 return;
915 data = ldim->data;
916 quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
917 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
918 available_space = data->geometry.distance * local->xx;
920 if (outside == ADG_THREE_STATE_ON)
921 markers_space = 0;
922 else
923 markers_space =
924 (data->marker1 == NULL ? 0 : adg_marker_get_size(data->marker1)) +
925 (data->marker2 == NULL ? 0 : adg_marker_get_size(data->marker2));
927 /* Leave at least 0.25 marker_space between the markers */
928 if (detached == ADG_THREE_STATE_ON)
929 quote_space = markers_space * 0.25;
930 else
931 quote_space = adg_entity_get_extents(quote)->size.x;
933 if (outside == ADG_THREE_STATE_UNKNOWN &&
934 detached == ADG_THREE_STATE_UNKNOWN) {
935 /* Both flags need to be choosed */
936 if (quote_space + markers_space < available_space) {
937 *to_detach = FALSE;
938 *to_outside = FALSE;
939 } else if (quote_space < available_space) {
940 *to_detach = FALSE;
941 *to_outside = TRUE;
942 } else {
943 *to_detach = TRUE;
944 *to_outside = markers_space * 1.25 > available_space;
946 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
947 /* Only the outside flag may be guessed */
948 *to_outside = quote_space + markers_space > available_space;
949 } else {
950 /* Only the detached flag may be guessed */
951 *to_detach = quote_space + markers_space > available_space;
955 static void
956 unset_trail(AdgLDim *ldim)
958 AdgLDimPrivate *data = ldim->data;
960 if (data->trail != NULL)
961 adg_model_clear((AdgModel *) data->trail);
963 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
966 static void
967 dispose_markers(AdgLDim *ldim)
969 AdgLDimPrivate *data = ldim->data;
971 if (data->trail != NULL) {
972 g_object_unref(data->trail);
973 data->trail = NULL;
976 if (data->marker1 != NULL) {
977 g_object_unref(data->marker1);
978 data->marker1 = NULL;
981 if (data->marker2 != NULL) {
982 g_object_unref(data->marker2);
983 data->marker2 = NULL;
987 static CpmlPath *
988 trail_callback(AdgTrail *trail, gpointer user_data)
990 AdgLDim *ldim;
991 AdgLDimPrivate *data;
993 ldim = (AdgLDim *) user_data;
994 data = ldim->data;
996 return &data->cpml.path;