[AdgDim] "pos" and "ref1", "ref2" should be handled as AdgPoint
[adg.git] / adg / adg-ldim.c
blob8dd688eeba9d4575b44f07f491765c02981b0ec4
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 void update_geometry (AdgLDim *ldim);
70 static void update_shift (AdgLDim *ldim);
71 static void update_entities (AdgLDim *ldim);
72 static void choose_flags (AdgLDim *ldim,
73 gboolean *to_outside,
74 gboolean *to_detach);
75 static void unset_trail (AdgLDim *ldim);
76 static void dispose_markers (AdgLDim *ldim);
77 static CpmlPath * trail_callback (AdgTrail *trail,
78 gpointer user_data);
81 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
84 static void
85 adg_ldim_class_init(AdgLDimClass *klass)
87 GObjectClass *gobject_class;
88 AdgEntityClass *entity_class;
89 AdgDimClass *dim_class;
90 GParamSpec *param;
92 gobject_class = (GObjectClass *) klass;
93 entity_class = (AdgEntityClass *) klass;
94 dim_class = (AdgDimClass *) klass;
96 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
98 gobject_class->dispose = dispose;
99 gobject_class->get_property = get_property;
100 gobject_class->set_property = set_property;
102 entity_class->local_changed = local_changed;
103 entity_class->invalidate = invalidate;
104 entity_class->arrange = arrange;
105 entity_class->render = render;
107 dim_class->default_value = default_value;
109 param = g_param_spec_double("direction",
110 P_("Direction"),
111 P_("The inclination angle of the extension lines"),
112 -G_MAXDOUBLE, G_MAXDOUBLE, ADG_DIR_RIGHT,
113 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
114 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
116 param = g_param_spec_boolean("has-extension1",
117 P_("Has First Extension Line flag"),
118 P_("Show (TRUE) or hide (FALSE) the first extension line"),
119 TRUE, G_PARAM_READWRITE);
120 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
122 param = g_param_spec_boolean("has-extension2",
123 P_("Has Second Extension Line flag"),
124 P_("Show (TRUE) or hide (FALSE) the second extension line"),
125 TRUE, G_PARAM_READWRITE);
126 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
129 static void
130 adg_ldim_init(AdgLDim *ldim)
132 AdgLDimPrivate *data;
133 cairo_path_data_t move_to, line_to;
135 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
136 move_to.header.type = CPML_MOVE;
137 move_to.header.length = 2;
138 line_to.header.type = CPML_LINE;
139 line_to.header.length = 2;
141 data->direction = 0;
142 data->has_extension1 = TRUE;
143 data->has_extension2 = TRUE;
145 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
146 data->cpml.path.data = data->cpml.data;
147 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
148 data->cpml.path.data[0] = move_to;
149 data->cpml.path.data[2] = line_to;
150 data->cpml.path.data[4] = move_to;
151 data->cpml.path.data[6] = line_to;
152 data->cpml.path.data[8] = move_to;
153 data->cpml.path.data[10] = line_to;
154 data->cpml.path.data[12] = move_to;
155 data->cpml.path.data[14] = line_to;
156 data->cpml.path.data[16] = move_to;
157 data->cpml.path.data[18] = line_to;
159 data->trail = NULL;
160 data->marker1 = NULL;
161 data->marker2 = NULL;
163 data->geometry.is_arranged = FALSE;
164 data->shift.is_arranged = FALSE;
166 ldim->data = data;
169 static void
170 dispose(GObject *object)
172 dispose_markers((AdgLDim *) object);
174 if (PARENT_OBJECT_CLASS->dispose)
175 PARENT_OBJECT_CLASS->dispose(object);
178 static void
179 get_property(GObject *object,
180 guint prop_id, GValue *value, GParamSpec *pspec)
182 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
184 switch (prop_id) {
185 case PROP_DIRECTION:
186 g_value_set_double(value, data->direction);
187 break;
188 case PROP_HAS_EXTENSION1:
189 g_value_set_boolean(value, data->has_extension1);
190 break;
191 case PROP_HAS_EXTENSION2:
192 g_value_set_boolean(value, data->has_extension2);
193 break;
194 default:
195 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
196 break;
200 static void
201 set_property(GObject *object,
202 guint prop_id, const GValue *value, GParamSpec *pspec)
204 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
206 switch (prop_id) {
207 case PROP_DIRECTION:
208 data->direction = g_value_get_double(value);
209 break;
210 case PROP_HAS_EXTENSION1:
211 data->has_extension1 = g_value_get_boolean(value);
212 break;
213 case PROP_HAS_EXTENSION2:
214 data->has_extension2 = g_value_get_boolean(value);
215 break;
216 default:
217 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
218 break;
224 * adg_ldim_new:
226 * Creates a new - undefined - linear dimension. You must, at least,
227 * define the start of the dimension in #AdgDim:ref1, the end in
228 * #AdgDim:ref2 and the position of the quote in #AdgDim:pos using
229 * any valid #AdgDim method. The director of the dimension (that is,
230 * if it is horizontal, vertical or oblique at a specific angle)
231 * should be specified with adg_ldim_set_direction().
233 * Returns: the newly created linear dimension entity
235 AdgLDim *
236 adg_ldim_new(void)
238 return g_object_new(ADG_TYPE_LDIM, NULL);
242 * adg_ldim_new_full:
243 * @ref1: the first reference point
244 * @ref2: the second reference point
245 * @pos: the position reference
246 * @direction: angle where to extend the dimension
248 * Creates a new linear dimension, specifing all the needed properties in
249 * one shot.
251 * Returns: the newly created linear dimension entity
253 AdgLDim *
254 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
255 const AdgPair *pos, gdouble direction)
257 AdgLDim *ldim;
258 AdgDim *dim;
260 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
261 dim = (AdgDim *) ldim;
263 adg_dim_set_ref1_from_pair(dim, ref1);
264 adg_dim_set_ref2_from_pair(dim, ref2);
265 adg_dim_set_pos_from_pair(dim, pos);
267 return ldim;
271 * adg_ldim_new_full_explicit:
272 * @ref1_x: the x coordinate of the first reference point
273 * @ref1_y: the y coordinate of the first reference point
274 * @ref2_x: the x coordinate of the second reference point
275 * @ref2_y: the y coordinate of the second reference point
276 * @pos_x: the x coordinate of the position reference
277 * @pos_y: the y coordinate of the position reference
278 * @direction: angle where to extend the dimension
280 * Wrappes adg_ldim_new_full() with explicit values.
282 * Returns: the newly created linear dimension entity
284 AdgLDim *
285 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
286 gdouble ref2_x, gdouble ref2_y,
287 gdouble pos_x, gdouble pos_y, gdouble direction)
289 AdgPair ref1;
290 AdgPair ref2;
291 AdgPair pos;
293 ref1.x = ref1_x;
294 ref1.y = ref1_y;
295 ref2.x = ref2_x;
296 ref2.y = ref2_y;
297 pos.x = pos_x;
298 pos.y = pos_y;
300 return adg_ldim_new_full(&ref1, &ref2, &pos, direction);
304 * adg_ldim_new_full_from_model:
305 * @model: the model from which the named pairs are taken
306 * @ref1: the first reference point
307 * @ref2: the second reference point
308 * @pos: the position reference
309 * @direction: angle where to extend the dimension
311 * Creates a new linear dimension, specifing all the needed properties in
312 * one shot and using named pairs from @model.
314 * Returns: the newly created linear dimension entity
316 AdgLDim *
317 adg_ldim_new_full_from_model(AdgModel *model,
318 const gchar *ref1, const gchar *ref2,
319 const gchar *pos, gdouble direction)
321 AdgLDim *ldim;
322 AdgDim *dim;
324 ldim = g_object_new(ADG_TYPE_LDIM, "direction", direction, NULL);
325 dim = (AdgDim *) ldim;
327 adg_dim_set_ref1_from_model(dim, model, ref1);
328 adg_dim_set_ref2_from_model(dim, model, ref2);
329 adg_dim_set_pos_from_model(dim, model, pos);
331 return ldim;
335 * adg_ldim_set_direction:
336 * @ldim: an #AdgLDim entity
337 * @direction: an angle value, in radians
339 * Sets the direction angle where to extend @ldim.
341 void
342 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
344 AdgLDimPrivate *data;
346 g_return_if_fail(ADG_IS_LDIM(ldim));
348 data = ldim->data;
349 data->direction = direction;
351 g_object_notify((GObject *) ldim, "direction");
355 * adg_ldim_get_direction:
356 * @ldim: an #AdgLDim entity
358 * Gets the direction where @ldim will extend.
360 * Returns: the direction angle in radians
362 gdouble
363 adg_ldim_get_direction(AdgLDim *ldim)
365 AdgLDimPrivate *data;
367 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
369 data = ldim->data;
371 return data->direction;
375 * adg_ldim_switch_extension1:
376 * @ldim: an #AdgLDim entity
377 * @new_state: the new state
379 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
380 * the first extension line of @ldim.
382 void
383 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean new_state)
385 AdgLDimPrivate *data;
387 g_return_if_fail(ADG_IS_LDIM(ldim));
389 data = ldim->data;
391 if (data->has_extension1 != new_state) {
392 data->has_extension1 = new_state;
393 g_object_notify((GObject *) ldim, "has-extension1");
398 * adg_ldim_has_extension1:
399 * @ldim: an #AdgLDim entity
401 * Checks if @ldim should render also the first extension line.
403 * Returns: %TRUE on first extension line presents, %FALSE otherwise
405 gboolean
406 adg_ldim_has_extension1(AdgLDim *ldim)
408 AdgLDimPrivate *data;
410 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
412 data = ldim->data;
414 return data->has_extension1;
418 * adg_ldim_switch_extension2:
419 * @ldim: an #AdgLDim entity
420 * @new_state: the new new_state
422 * Shows (if @new_state is %TRUE) or hide (if @new_state is %FALSE)
423 * the second extension line of @ldim.
425 void
426 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean new_state)
428 AdgLDimPrivate *data;
430 g_return_if_fail(ADG_IS_LDIM(ldim));
432 data = ldim->data;
434 if (data->has_extension2 != new_state) {
435 data->has_extension2 = new_state;
436 g_object_notify((GObject *) ldim, "has-extension2");
441 * adg_ldim_has_extension2:
442 * @ldim: an #AdgLDim entity
444 * Checks if @ldim should render also the second extension line.
446 * Returns: %TRUE on first extension line presents, %FALSE otherwise
448 gboolean
449 adg_ldim_has_extension2(AdgLDim *ldim)
451 AdgLDimPrivate *data;
453 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
455 data = ldim->data;
457 return data->has_extension2;
461 static void
462 local_changed(AdgEntity *entity)
464 unset_trail((AdgLDim *) entity);
466 if (PARENT_ENTITY_CLASS->local_changed)
467 PARENT_ENTITY_CLASS->local_changed(entity);
470 static void
471 invalidate(AdgEntity *entity)
473 AdgLDim *ldim;
474 AdgLDimPrivate *data;
476 ldim = (AdgLDim *) entity;
477 data = ldim->data;
479 dispose_markers(ldim);
480 data->geometry.is_arranged = FALSE;
481 data->shift.is_arranged = FALSE;
482 unset_trail(ldim);
484 if (PARENT_ENTITY_CLASS->invalidate)
485 PARENT_ENTITY_CLASS->invalidate(entity);
488 /* TODO: split the following crap in more manageable functions */
489 static void
490 arrange(AdgEntity *entity)
492 AdgLDim *ldim;
493 AdgDim *dim;
494 AdgLDimPrivate *data;
495 AdgAlignment *quote;
496 AdgDimStyle *dim_style;
497 gboolean to_outside, to_detach;
498 const AdgMatrix *local;
499 AdgPair ref1, ref2, base1, base2;
500 AdgPair pair;
501 gint n;
502 CpmlExtents extents;
504 if (PARENT_ENTITY_CLASS->arrange)
505 PARENT_ENTITY_CLASS->arrange(entity);
507 ldim = (AdgLDim *) entity;
508 dim = (AdgDim *) ldim;
509 data = ldim->data;
510 quote = adg_dim_get_quote(dim);
512 update_geometry(ldim);
513 update_shift(ldim);
514 update_entities(ldim);
516 /* Check for cached result */
517 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
518 AdgEntity *quote_entity = (AdgEntity *) quote;
519 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
520 return;
523 choose_flags(ldim, &to_outside, &to_detach);
525 dim_style = GET_DIM_STYLE(dim);
526 local = adg_entity_get_local_matrix(entity);
527 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
528 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
529 cpml_pair_copy(&base1, &data->geometry.base1);
530 cpml_pair_copy(&base2, &data->geometry.base2);
532 cpml_pair_transform(&ref1, local);
533 cpml_pair_transform(&ref2, local);
534 cpml_pair_transform(&base1, local);
535 base1.x += data->shift.base.x;
536 base1.y += data->shift.base.y;
537 cpml_pair_transform(&base2, local);
538 base2.x += data->shift.base.x;
539 base2.y += data->shift.base.y;
541 pair.x = ref1.x + data->shift.from.x;
542 pair.y = ref1.y + data->shift.from.y;
543 cpml_pair_to_cairo(&pair, &data->cpml.data[13]);
545 cpml_pair_to_cairo(&base1, &data->cpml.data[1]);
547 pair.x = base1.x + data->shift.to.x;
548 pair.y = base1.y + data->shift.to.y;
549 cpml_pair_to_cairo(&pair, &data->cpml.data[15]);
551 pair.x = ref2.x + data->shift.from.x;
552 pair.y = ref2.y + data->shift.from.y;
553 cpml_pair_to_cairo(&pair, &data->cpml.data[17]);
555 cpml_pair_to_cairo(&base2, &data->cpml.data[3]);
557 pair.x = base2.x + data->shift.to.x;
558 pair.y = base2.y + data->shift.to.y;
559 cpml_pair_to_cairo(&pair, &data->cpml.data[19]);
561 /* Calculate the outside segments */
562 if (to_outside) {
563 gdouble beyond;
564 CpmlVector vector;
566 beyond = adg_dim_style_get_beyond(dim_style);
567 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
569 cpml_pair_from_cairo(&vector, &data->cpml.data[3]);
570 vector.x -= pair.x;
571 vector.y -= pair.y;
572 cpml_vector_set_length(&vector, beyond);
574 cpml_pair_from_cairo(&pair, &data->cpml.data[1]);
575 cpml_pair_to_cairo(&pair, &data->cpml.data[5]);
577 pair.x -= vector.x;
578 pair.y -= vector.y;
579 cpml_pair_to_cairo(&pair, &data->cpml.data[7]);
581 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
582 cpml_pair_to_cairo(&pair, &data->cpml.data[11]);
584 pair.x += vector.x;
585 pair.y += vector.y;
586 cpml_pair_to_cairo(&pair, &data->cpml.data[9]);
588 data->cpml.data[2].header.length = 2;
589 n = 10;
590 } else {
591 data->cpml.data[2].header.length = 10;
592 n = 2;
595 data->cpml.data[10].header.length = 2;
596 extents.is_defined = FALSE;
598 /* Add the quote */
599 if (quote != NULL) {
600 AdgEntity *quote_entity;
601 gdouble angle;
602 AdgPair middle, factor;
603 AdgMatrix map;
605 quote_entity = (AdgEntity *) quote;
606 angle = adg_dim_quote_angle(dim, data->direction + G_PI_2);
607 middle.x = (base1.x + base2.x) / 2;
608 middle.y = (base1.y + base2.y) / 2;
609 factor.x = factor.y = 0;
611 if (to_detach) {
612 /* Detached quote: position the quote at "pos" */
613 AdgPair p_line, p_quote;
614 gdouble same_sd, opposite_sd, quote_size;
615 gint n_side;
616 cairo_path_data_t *to_extend;
618 /* Set "pair" to the properly converted "pos" coordinates */
619 cpml_pair_copy(&pair, adg_point_get_pair(adg_dim_get_pos(dim)));
620 cpml_pair_transform(&pair, local);
621 pair.x += data->shift.base.x;
622 pair.y += data->shift.base.y;
624 /* Taking "middle" as the reference, check if the quote
625 * is on the _left_ or on the _right_ side. This is got
626 * by checking if (pos-middle) is closer to the quote
627 * vector or to the negated quote vector by using an
628 * algorithm based on the squared distances. */
629 p_line.x = pair.x - middle.x;
630 p_line.y = pair.y - middle.y;
631 cpml_vector_from_angle(&p_quote, angle);
633 same_sd = cpml_pair_squared_distance(&p_quote, &p_line);
634 p_quote.x = -p_quote.x;
635 p_quote.y = -p_quote.y;
636 opposite_sd = cpml_pair_squared_distance(&p_quote, &p_line);
637 quote_size = adg_entity_get_extents(quote_entity)->size.x;
639 /* Properly align the quote, depending on its side */
640 if (same_sd > opposite_sd) {
641 factor.x = 1;
642 } else {
643 factor.x = 0;
644 p_quote.x = -p_quote.x;
645 p_quote.y = -p_quote.y;
648 cpml_vector_set_length(&p_quote, quote_size);
649 p_quote.x += pair.x;
650 p_quote.y += pair.y;
652 /* Extends the base line to include the "p_quote" pair,
653 * that is underline a detached quote */
654 if (cpml_pair_squared_distance(&p_quote, &base1) >
655 cpml_pair_squared_distance(&p_quote, &base2))
656 n_side = 2;
657 else
658 n_side = 1;
660 if (to_outside) {
661 to_extend = &data->cpml.data[n_side == 1 ? 7 : 9];
662 } else {
663 to_extend = &data->cpml.data[9];
664 cpml_pair_to_cairo(&middle, &data->cpml.data[9]);
665 cpml_pair_to_cairo(n_side == 1 ? &base1 : &base2,
666 &data->cpml.data[11]);
667 data->cpml.data[2].header.length = 6;
668 n = 10;
671 cpml_pair_from_cairo(&p_line, to_extend);
673 /* Extend the base line only if needed */
674 if (cpml_pair_squared_distance(&p_quote, &middle) >
675 cpml_pair_squared_distance(&p_line, &middle))
676 cpml_pair_to_cairo(&p_quote, to_extend);
677 } else {
678 /* Center the quote in the middle of the base line */
679 factor.x = 0.5;
680 cpml_pair_copy(&pair, &middle);
683 adg_alignment_set_factor(quote, &factor);
684 cairo_matrix_init_translate(&map, pair.x, pair.y);
685 cairo_matrix_rotate(&map, angle);
686 adg_entity_set_global_map(quote_entity, &map);
687 adg_entity_arrange(quote_entity);
688 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
690 adg_matrix_copy(&data->quote.global_map,
691 adg_entity_get_global_map(quote_entity));
694 /* Play with header lengths to show or hide the extension lines */
695 if (data->has_extension1) {
696 data->cpml.data[14].header.length = data->has_extension2 ? 2 : 6;
697 } else {
698 data->cpml.data[14].header.length = 2;
699 data->cpml.data[n].header.length += 4;
700 if (!data->has_extension2)
701 data->cpml.data[n].header.length += 4;
704 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
705 cpml_extents_add(&extents, adg_trail_get_extents(data->trail));
707 if (data->marker1 != NULL) {
708 AdgEntity *marker1_entity = (AdgEntity *) data->marker1;
709 adg_marker_set_segment(data->marker1, data->trail, to_outside ? 2 : 1);
710 adg_entity_local_changed(marker1_entity);
711 adg_entity_arrange(marker1_entity);
712 cpml_extents_add(&extents, adg_entity_get_extents(marker1_entity));
715 if (data->marker2 != NULL) {
716 AdgEntity *marker2_entity = (AdgEntity *) data->marker2;
717 adg_marker_set_segment(data->marker2, data->trail, to_outside ? 3 : 1);
718 adg_entity_local_changed(marker2_entity);
719 adg_entity_arrange(marker2_entity);
720 cpml_extents_add(&extents, adg_entity_get_extents(marker2_entity));
723 adg_entity_set_extents(entity, &extents);
726 static void
727 render(AdgEntity *entity, cairo_t *cr)
729 AdgLDim *ldim;
730 AdgDim *dim;
731 AdgLDimPrivate *data;
732 AdgDimStyle *dim_style;
733 AdgDress dress;
734 const cairo_path_t *cairo_path;
736 ldim = (AdgLDim *) entity;
737 dim = (AdgDim *) entity;
738 data = ldim->data;
739 dim_style = GET_DIM_STYLE(dim);
741 adg_style_apply((AdgStyle *) dim_style, entity, cr);
743 if (data->marker1 != NULL)
744 adg_entity_render((AdgEntity *) data->marker1, cr);
746 if (data->marker2 != NULL)
747 adg_entity_render((AdgEntity *) data->marker2, cr);
749 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
751 dress = adg_dim_style_get_line_dress(dim_style);
752 adg_entity_apply_dress(entity, dress, cr);
754 cairo_path = adg_trail_get_cairo_path(data->trail);
755 cairo_append_path(cr, cairo_path);
756 cairo_stroke(cr);
759 static gchar *
760 default_value(AdgDim *dim)
762 AdgLDim *ldim;
763 AdgLDimPrivate *data;
764 AdgDimStyle *dim_style;
765 const gchar *format;
767 ldim = (AdgLDim *) dim;
768 data = ldim->data;
769 dim_style = GET_DIM_STYLE(dim);
770 format = adg_dim_style_get_number_format(dim_style);
772 update_geometry(ldim);
774 return g_strdup_printf(format, data->geometry.distance);
777 static void
778 update_geometry(AdgLDim *ldim)
780 AdgLDimPrivate *data;
781 AdgDim *dim;
782 const AdgPair *ref1, *ref2;
783 const AdgPair *pos;
784 CpmlVector baseline, extension;
785 gdouble d, k;
787 data = ldim->data;
789 if (data->geometry.is_arranged)
790 return;
792 dim = (AdgDim *) ldim;
793 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
794 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
795 pos = adg_point_get_pair(adg_dim_get_pos(dim));
796 cpml_vector_from_angle(&extension, data->direction);
797 cpml_pair_copy(&baseline, &extension);
798 cpml_vector_normal(&baseline);
800 d = extension.y * baseline.x -
801 extension.x * baseline.y;
802 g_return_if_fail(d != 0);
804 k = ((pos->y - ref1->y) * baseline.x -
805 (pos->x - ref1->x) * baseline.y) / d;
806 data->geometry.base1.x = ref1->x + k * extension.x;
807 data->geometry.base1.y = ref1->y + k * extension.y;
809 k = ((pos->y - ref2->y) * baseline.x -
810 (pos->x - ref2->x) * baseline.y) / d;
811 data->geometry.base2.x = ref2->x + k * extension.x;
812 data->geometry.base2.y = ref2->y + k * extension.y;
814 data->geometry.distance = cpml_pair_distance(&data->geometry.base1,
815 &data->geometry.base2);
817 data->geometry.is_arranged = TRUE;
820 static void
821 update_shift(AdgLDim *ldim)
823 AdgLDimPrivate *data;
824 AdgDimStyle *dim_style;
825 gdouble from_offset, to_offset;
826 gdouble baseline_spacing, level;
827 CpmlVector vector;
829 data = ldim->data;
831 if (data->shift.is_arranged)
832 return;
834 dim_style = GET_DIM_STYLE(ldim);
835 from_offset = adg_dim_style_get_from_offset(dim_style);
836 to_offset = adg_dim_style_get_to_offset(dim_style);
837 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
838 level = adg_dim_get_level((AdgDim *) ldim);
840 cpml_vector_from_angle(&vector, data->direction);
842 cpml_vector_set_length(&vector, from_offset);
843 cpml_pair_copy(&data->shift.from, &vector);
845 cpml_vector_set_length(&vector, to_offset);
846 cpml_pair_copy(&data->shift.to, &vector);
848 cpml_vector_set_length(&vector, level * baseline_spacing);
849 cpml_pair_copy(&data->shift.base, &vector);
851 data->shift.is_arranged = TRUE;
854 static void
855 update_entities(AdgLDim *ldim)
857 AdgLDimPrivate *data;
858 AdgDimStyle *dim_style;
860 data = ldim->data;
861 dim_style = GET_DIM_STYLE(ldim);
863 if (data->trail == NULL)
864 data->trail = adg_trail_new(trail_callback, ldim);
866 if (data->marker1 == NULL)
867 data->marker1 = adg_dim_style_marker1_new(dim_style);
869 if (data->marker2 == NULL)
870 data->marker2 = adg_dim_style_marker2_new(dim_style);
873 static void
874 choose_flags(AdgLDim *ldim, gboolean *to_outside, gboolean *to_detach)
876 AdgDim *dim;
877 AdgThreeState outside, detached;
878 AdgLDimPrivate *data;
879 AdgEntity *quote;
880 const AdgMatrix *local;
881 gdouble available_space, markers_space, quote_space;
883 dim = (AdgDim *) ldim;
884 outside = adg_dim_get_outside(dim);
885 detached = adg_dim_get_detached(dim);
887 *to_outside = outside == ADG_THREE_STATE_ON;
888 *to_detach = detached == ADG_THREE_STATE_ON;
890 /* On explicit flags, no further investigation is required */
891 if (outside != ADG_THREE_STATE_UNKNOWN &&
892 detached != ADG_THREE_STATE_UNKNOWN)
893 return;
895 data = ldim->data;
896 quote = (AdgEntity *) adg_dim_get_quote((AdgDim *) ldim);
897 local = adg_entity_get_local_matrix((AdgEntity *) ldim);
898 available_space = data->geometry.distance * local->xx;
900 if (outside == ADG_THREE_STATE_ON)
901 markers_space = 0;
902 else
903 markers_space =
904 (data->marker1 == NULL ? 0 : adg_marker_get_size(data->marker1)) +
905 (data->marker2 == NULL ? 0 : adg_marker_get_size(data->marker2));
907 /* Leave at least 0.25 marker_space between the markers */
908 if (detached == ADG_THREE_STATE_ON)
909 quote_space = markers_space * 0.25;
910 else
911 quote_space = adg_entity_get_extents(quote)->size.x;
913 if (outside == ADG_THREE_STATE_UNKNOWN &&
914 detached == ADG_THREE_STATE_UNKNOWN) {
915 /* Both flags need to be choosed */
916 if (quote_space + markers_space < available_space) {
917 *to_detach = FALSE;
918 *to_outside = FALSE;
919 } else if (quote_space < available_space) {
920 *to_detach = FALSE;
921 *to_outside = TRUE;
922 } else {
923 *to_detach = TRUE;
924 *to_outside = markers_space * 1.25 > available_space;
926 } else if (outside == ADG_THREE_STATE_UNKNOWN) {
927 /* Only the outside flag may be guessed */
928 *to_outside = quote_space + markers_space > available_space;
929 } else {
930 /* Only the detached flag may be guessed */
931 *to_detach = quote_space + markers_space > available_space;
935 static void
936 unset_trail(AdgLDim *ldim)
938 AdgLDimPrivate *data = ldim->data;
940 if (data->trail != NULL)
941 adg_model_clear((AdgModel *) data->trail);
943 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
946 static void
947 dispose_markers(AdgLDim *ldim)
949 AdgLDimPrivate *data = ldim->data;
951 if (data->trail != NULL) {
952 g_object_unref(data->trail);
953 data->trail = NULL;
956 if (data->marker1 != NULL) {
957 g_object_unref(data->marker1);
958 data->marker1 = NULL;
961 if (data->marker2 != NULL) {
962 g_object_unref(data->marker2);
963 data->marker2 = NULL;
967 static CpmlPath *
968 trail_callback(AdgTrail *trail, gpointer user_data)
970 AdgLDim *ldim;
971 AdgLDimPrivate *data;
973 ldim = (AdgLDim *) user_data;
974 data = ldim->data;
976 return &data->cpml.path;