[ADG] Avoid to forcibly set the CTM on the cr
[adg.git] / src / adg / adg-adim.c
bloba282fe5924abed0d793214ec6c2172fc0915c624
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-adim
23 * @short_description: Angular dimensions
25 * The #AdgADim entity defines an angular dimension.
28 /**
29 * AdgADim:
31 * All fields are privates and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-internal.h"
37 #include "adg-adim.h"
38 #include "adg-adim-private.h"
39 #include "adg-dim-private.h"
41 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_adim_parent_class)
42 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_adim_parent_class)
45 G_DEFINE_TYPE(AdgADim, adg_adim, ADG_TYPE_DIM);
47 enum {
48 PROP_0,
49 PROP_VALUE,
50 PROP_ORG1,
51 PROP_ORG2,
52 PROP_HAS_EXTENSION1,
53 PROP_HAS_EXTENSION2
57 static void _adg_dispose (GObject *object);
58 static void _adg_get_property (GObject *object,
59 guint param_id,
60 GValue *value,
61 GParamSpec *pspec);
62 static void _adg_set_property (GObject *object,
63 guint param_id,
64 const GValue *value,
65 GParamSpec *pspec);
66 static void _adg_global_changed (AdgEntity *entity);
67 static void _adg_local_changed (AdgEntity *entity);
68 static void _adg_invalidate (AdgEntity *entity);
69 static void _adg_arrange (AdgEntity *entity);
70 static void _adg_render (AdgEntity *entity,
71 cairo_t *cr);
72 static gchar * _adg_default_value (AdgDim *dim);
73 static void _adg_update_geometry (AdgADim *adim);
74 static void _adg_update_entities (AdgADim *adim);
75 static void _adg_unset_trail (AdgADim *adim);
76 static void _adg_dispose_markers (AdgADim *adim);
77 static gboolean _adg_get_info (AdgADim *adim,
78 CpmlVector vector[],
79 AdgPair *center,
80 gdouble *distance);
81 static CpmlPath * _adg_trail_callback (AdgTrail *trail,
82 gpointer user_data);
85 static void
86 adg_adim_class_init(AdgADimClass *klass)
88 GObjectClass *gobject_class;
89 AdgEntityClass *entity_class;
90 AdgDimClass *dim_class;
91 GParamSpec *param, *old_param;
93 gobject_class = (GObjectClass *) klass;
94 entity_class = (AdgEntityClass *) klass;
95 dim_class = (AdgDimClass *) klass;
97 g_type_class_add_private(klass, sizeof(AdgADimPrivate));
99 gobject_class->dispose = _adg_dispose;
100 gobject_class->get_property = _adg_get_property;
101 gobject_class->set_property = _adg_set_property;
103 entity_class->global_changed = _adg_global_changed;
104 entity_class->local_changed = _adg_local_changed;
105 entity_class->invalidate = _adg_invalidate;
106 entity_class->arrange = _adg_arrange;
107 entity_class->render = _adg_render;
109 dim_class->default_value = _adg_default_value;
111 /* Override #AdgDim:value to append a degree symbol
112 * to the default set value template string */
113 old_param = g_object_class_find_property(gobject_class, "value");
114 param = g_param_spec_string(old_param->name,
115 g_param_spec_get_nick(old_param),
116 g_param_spec_get_blurb(old_param),
117 "<>" ADG_UTF8_DEGREE,
118 old_param->flags);
119 g_object_class_install_property(gobject_class, PROP_VALUE, param);
121 param = g_param_spec_boxed("org1",
122 P_("First Origin"),
123 P_("Where the first line comes from: this point is used toghether with \"ref1\" to align the first extension line"),
124 ADG_TYPE_POINT,
125 G_PARAM_READWRITE);
126 g_object_class_install_property(gobject_class, PROP_ORG1, param);
128 param = g_param_spec_boxed("org2",
129 P_("Second Origin"),
130 P_("Where the second line comes from: this point is used toghether with \"ref2\" to align the second extension line"),
131 ADG_TYPE_POINT,
132 G_PARAM_READWRITE);
133 g_object_class_install_property(gobject_class, PROP_ORG2, param);
136 static void
137 adg_adim_init(AdgADim *adim)
139 AdgADimPrivate *data;
140 cairo_path_data_t move_to, line_to, arc_to;
142 data = G_TYPE_INSTANCE_GET_PRIVATE(adim, ADG_TYPE_ADIM, AdgADimPrivate);
143 move_to.header.type = CPML_MOVE;
144 move_to.header.length = 2;
145 line_to.header.type = CPML_LINE;
146 line_to.header.length = 2;
147 arc_to.header.type = CPML_ARC;
148 arc_to.header.length = 3;
150 data->org1 = NULL;
151 data->org2 = NULL;
152 data->has_extension1 = TRUE;
153 data->has_extension2 = TRUE;
155 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
156 data->cpml.path.data = data->cpml.data;
157 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
158 data->cpml.path.data[0] = move_to;
159 data->cpml.path.data[2] = arc_to;
160 data->cpml.path.data[5] = move_to;
161 data->cpml.path.data[7] = line_to;
162 data->cpml.path.data[9] = move_to;
163 data->cpml.path.data[11] = line_to;
165 data->trail = NULL;
166 data->marker1 = NULL;
167 data->marker2 = NULL;
169 data->geometry_arranged = FALSE;
171 adim->data = data;
174 static void
175 _adg_dispose(GObject *object)
177 AdgEntity *entity;
178 AdgADimPrivate *data;
180 entity = (AdgEntity *) object;
181 data = ((AdgADim *) object)->data;
183 _adg_dispose_markers((AdgADim *) object);
185 if (data->org1 != NULL)
186 data->org1 = adg_entity_point(entity, data->org1, NULL);
188 if (data->org2 != NULL)
189 data->org2 = adg_entity_point(entity, data->org2, NULL);
191 if (_ADG_OLD_OBJECT_CLASS->dispose)
192 _ADG_OLD_OBJECT_CLASS->dispose(object);
195 static void
196 _adg_get_property(GObject *object, guint prop_id,
197 GValue *value, GParamSpec *pspec)
199 AdgADimPrivate *data = ((AdgADim *) object)->data;
201 switch (prop_id) {
202 case PROP_VALUE:
203 g_value_set_string(value, adg_dim_get_value((AdgDim *) object));
204 break;
205 case PROP_ORG1:
206 g_value_set_boxed(value, data->org1);
207 break;
208 case PROP_ORG2:
209 g_value_set_boxed(value, data->org2);
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 AdgEntity *entity;
228 AdgADimPrivate *data;
230 entity = (AdgEntity *) object;
231 data = ((AdgADim *) object)->data;
233 switch (prop_id) {
234 case PROP_VALUE:
235 adg_dim_set_value((AdgDim *) object, g_value_get_string(value));
236 break;
237 case PROP_ORG1:
238 data->org1 = adg_entity_point(entity, data->org1,
239 g_value_get_boxed(value));
240 break;
241 case PROP_ORG2:
242 data->org2 = adg_entity_point(entity, data->org2,
243 g_value_get_boxed(value));
244 break;
245 case PROP_HAS_EXTENSION1:
246 data->has_extension1 = g_value_get_boolean(value);
247 break;
248 case PROP_HAS_EXTENSION2:
249 data->has_extension2 = g_value_get_boolean(value);
250 break;
251 default:
252 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
253 break;
259 * adg_adim_new:
261 * Creates a new - undefined - angular dimension. You must, at least,
262 * define the first line by setting #AdgADim:org1 (start point) and
263 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
264 * (start point) and #AdgDim:ref2 (end point) and the position of
265 * the quote in #AdgDim:pos.
267 * Returns: the newly created angular dimension entity
269 AdgADim *
270 adg_adim_new(void)
272 return g_object_new(ADG_TYPE_ADIM, NULL);
276 * adg_adim_new_full:
277 * @ref1: first reference point
278 * @ref2: second reference point
279 * @org1: first origin point
280 * @org2: second origin point
282 * Creates a new angular dimension, specifing all the needed
283 * properties in one shot using #AdgPair.
285 * Returns: the newly created angular dimension entity
287 AdgADim *
288 adg_adim_new_full(const AdgPair *ref1, const AdgPair *ref2,
289 const AdgPair *org1, const AdgPair *org2,
290 const AdgPair *pos)
292 AdgADim *adim;
293 AdgDim *dim;
295 adim = g_object_new(ADG_TYPE_ADIM, NULL);
296 dim = (AdgDim *) adim;
298 adg_dim_set_ref1_from_pair(dim, ref1);
299 adg_dim_set_ref2_from_pair(dim, ref2);
300 adg_dim_set_pos_from_pair(dim, pos);
301 adg_adim_set_org1_from_pair(adim, org1);
302 adg_adim_set_org2_from_pair(adim, org2);
304 return adim;
308 * adg_adim_new_full_explicit:
309 * @ref1_x: the x coordinate of the first reference point
310 * @ref1_y: the y coordinate of the first reference point
311 * @ref2_x: the x coordinate of the second reference point
312 * @ref2_y: the y coordinate of the second reference point
313 * @direction: angle where to extend the dimension
314 * @pos_x: the x coordinate of the position reference
315 * @pos_y: the y coordinate of the position reference
317 * Wrappes adg_adim_new_full() with explicit values.
319 * Returns: the newly created linear dimension entity
321 AdgADim *
322 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
323 gdouble ref2_x, gdouble ref2_y,
324 gdouble org1_x, gdouble org1_y,
325 gdouble org2_x, gdouble org2_y,
326 gdouble pos_x, gdouble pos_y)
328 AdgPair ref1, ref2, org1, org2, pos;
330 ref1.x = ref1_x;
331 ref1.y = ref1_y;
332 ref2.x = ref2_x;
333 ref2.y = ref2_y;
334 org1.x = org1_x;
335 org1.y = org1_y;
336 org2.x = org2_x;
337 org2.y = org2_y;
338 pos.x = pos_x;
339 pos.y = pos_y;
341 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
345 * adg_adim_new_full_from_model:
346 * @model: the model from which the named pairs are taken
347 * @ref1: the end point of the first line
348 * @ref2: the end point of the second line
349 * @org1: the origin of the first line
350 * @org2: the origin of the second line
351 * @pos: the position reference
353 * Creates a new angular dimension, specifing all the needed properties
354 * in one shot and using named pairs from @model.
356 * Returns: the newly created angular dimension entity
358 AdgADim *
359 adg_adim_new_full_from_model(AdgModel *model,
360 const gchar *ref1, const gchar *ref2,
361 const gchar *org1, const gchar *org2,
362 const gchar *pos)
364 AdgADim *adim;
365 AdgDim *dim;
367 adim = g_object_new(ADG_TYPE_ADIM, NULL);
368 dim = (AdgDim *) adim;
370 adg_dim_set_ref1_from_model(dim, model, ref1);
371 adg_dim_set_ref2_from_model(dim, model, ref2);
372 adg_dim_set_pos_from_model(dim, model, pos);
373 adg_adim_set_org1_from_model(adim, model, org1);
374 adg_adim_set_org2_from_model(adim, model, org2);
376 return adim;
380 * adg_adim_set_org1:
381 * @adim: an #AdgADim
382 * @org1: the new point to use as first reference
384 * Sets the #AdgADim:org1 property to @org1. The old point
385 * is silently discarded, unreferencing its model if that
386 * point was bound to a named pair (hence, possibly destroying
387 * the model if this was the last reference).
389 * @org1 can be %NULL, in which case the point is destroyed.
391 void
392 adg_adim_set_org1(AdgADim *adim, const AdgPoint *org1)
394 g_return_if_fail(ADG_IS_ADIM(adim));
395 g_object_set((GObject *) adim, "org1", org1, NULL);
399 * adg_adim_set_org1_explicit:
400 * @adim: an #AdgADim
401 * @x: x coordinate of the first reference point
402 * @y: y coordinate of the first reference point
404 * Sets the #AdgADim:org1 property to the (@x, @y) explicit
405 * coordinates. The old point is silently discarded,
406 * unreferencing its model if that point was bound to a named
407 * pair (hence, possibly destroying the model if this was the
408 * last reference).
410 void
411 adg_adim_set_org1_explicit(AdgADim *adim, gdouble x, gdouble y)
413 AdgPoint *point = adg_point_new();
415 adg_point_set_pair_explicit(point, x, y);
416 adg_adim_set_org1(adim, point);
418 adg_point_destroy(point);
422 * adg_adim_set_org1_from_pair:
423 * @adim: an #AdgADim
424 * @org1: the coordinates pair of the first reference point
426 * Convenient function to set the #AdgADim:org1 property using a
427 * pair instead of explicit coordinates.
429 void
430 adg_adim_set_org1_from_pair(AdgADim *adim, const AdgPair *org1)
432 g_return_if_fail(org1 != NULL);
434 adg_adim_set_org1_explicit(adim, org1->x, org1->y);
438 * adg_adim_set_org1_from_model:
439 * @adim: an #AdgADim
440 * @model: the source #AdgModel
441 * @org1: a named pair in @model
443 * Binds #AdgADim:org1 to the @org1 named pair of @model. If @model
444 * is %NULL, the point will be unset. In any case, the old point
445 * is silently discarded, unreferencing its model if that point
446 * was bound to a named pair (hence, possibly destroying the model
447 * if this was the last reference).
449 * The assignment is lazy so @org1 could be not be present in @model.
450 * Anyway, at the first access to this point an error will be raised
451 * if the named pair is still missing.
453 void
454 adg_adim_set_org1_from_model(AdgADim *adim, AdgModel *model, const gchar *org1)
456 AdgPoint *point = adg_point_new();
458 adg_point_set_pair_from_model(point, model, org1);
459 adg_adim_set_org1(adim, point);
461 adg_point_destroy(point);
465 * adg_adim_get_org1:
466 * @adim: an #AdgADim
468 * Gets the #AdgADim:org1 point. The returned point is internally owned
469 * and must not be freed or modified. Anyway, it is not const because
470 * adg_point_get_pair() must be able to modify the internal cache of
471 * the returned point.
473 * Returns: the first reference point
475 AdgPoint *
476 adg_adim_get_org1(AdgADim *adim)
478 AdgADimPrivate *data;
480 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
482 data = adim->data;
484 return data->org1;
488 * adg_adim_set_org2:
489 * @adim: an #AdgADim
490 * @org2: the new point to use as first reference
492 * Sets the #AdgADim:org2 property to @org2. The old point
493 * is silently discarded, unreferencing its model if that
494 * point was bound to a named pair (hence, possibly destroying
495 * the model if this was the last reference).
497 * @org2 can be %NULL, in which case the point is destroyed.
499 void
500 adg_adim_set_org2(AdgADim *adim, const AdgPoint *org2)
502 g_return_if_fail(ADG_IS_ADIM(adim));
503 g_object_set((GObject *) adim, "org2", org2, NULL);
507 * adg_adim_set_org2_explicit:
508 * @adim: an #AdgADim
509 * @x: x coordinate of the first reference point
510 * @y: y coordinate of the first reference point
512 * Sets the #AdgADim:org2 property to the (@x, @y) explicit
513 * coordinates. The old point is silently discarded,
514 * unreferencing its model if that point was bound to a named
515 * pair (hence, possibly destroying the model if this was the
516 * last reference).
518 void
519 adg_adim_set_org2_explicit(AdgADim *adim, gdouble x, gdouble y)
521 AdgPoint *point = adg_point_new();
523 adg_point_set_pair_explicit(point, x, y);
524 adg_adim_set_org2(adim, point);
526 adg_point_destroy(point);
530 * adg_adim_set_org2_from_pair:
531 * @adim: an #AdgADim
532 * @org2: the coordinates pair of the first reference point
534 * Convenient function to set the #AdgADim:org2 property using a
535 * pair instead of explicit coordinates.
537 void
538 adg_adim_set_org2_from_pair(AdgADim *adim, const AdgPair *org2)
540 g_return_if_fail(org2 != NULL);
542 adg_adim_set_org2_explicit(adim, org2->x, org2->y);
546 * adg_adim_set_org2_from_model:
547 * @adim: an #AdgADim
548 * @model: the source #AdgModel
549 * @org2: a named pair in @model
551 * Binds #AdgADim:org2 to the @org2 named pair of @model. If @model
552 * is %NULL, the point will be unset. In any case, the old point
553 * is silently discarded, unreferencing its model if that point
554 * was bound to a named pair (hence, possibly destroying the model
555 * if this was the last reference).
557 * The assignment is lazy so @org2 could be not be present in @model.
558 * Anyway, at the first access to this point an error will be raised
559 * if the named pair is still missing.
561 void
562 adg_adim_set_org2_from_model(AdgADim *adim, AdgModel *model, const gchar *org2)
564 AdgPoint *point = adg_point_new();
566 adg_point_set_pair_from_model(point, model, org2);
567 adg_adim_set_org2(adim, point);
569 adg_point_destroy(point);
573 * adg_adim_get_org2:
574 * @adim: an #AdgADim
576 * Gets the #AdgADim:org2 point. The returned point is internally owned
577 * and must not be freed or modified. Anyway, it is not const because
578 * adg_point_get_pair() must be able to modify the internal cache of
579 * the returned point.
581 * Returns: the first reference point
583 AdgPoint *
584 adg_adim_get_org2(AdgADim *adim)
586 AdgADimPrivate *data;
588 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
590 data = adim->data;
592 return data->org2;
596 static void
597 _adg_global_changed(AdgEntity *entity)
599 AdgADimPrivate *data = ((AdgADim *) entity)->data;
601 _adg_unset_trail((AdgADim *) entity);
603 if (_ADG_OLD_ENTITY_CLASS->global_changed)
604 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
606 if (data->marker1 != NULL)
607 adg_entity_global_changed((AdgEntity *) data->marker1);
609 if (data->marker2 != NULL)
610 adg_entity_global_changed((AdgEntity *) data->marker2);
613 static void
614 _adg_local_changed(AdgEntity *entity)
616 _adg_unset_trail((AdgADim *) entity);
618 if (_ADG_OLD_ENTITY_CLASS->local_changed)
619 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
622 static void
623 _adg_invalidate(AdgEntity *entity)
625 AdgADim *adim;
626 AdgADimPrivate *data;
628 adim = (AdgADim *) entity;
629 data = adim->data;
631 _adg_dispose_markers(adim);
632 data->geometry_arranged = FALSE;
633 _adg_unset_trail(adim);
635 if (data->org1)
636 adg_point_invalidate(data->org1);
637 if (data->org2)
638 adg_point_invalidate(data->org2);
640 if (_ADG_OLD_ENTITY_CLASS->invalidate)
641 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
644 static void
645 _adg_arrange(AdgEntity *entity)
647 AdgADim *adim;
648 AdgDim *dim;
649 AdgADimPrivate *data;
650 AdgAlignment *quote;
651 const AdgMatrix *local;
652 AdgPair ref1, ref2, base1, base12, base2;
653 AdgPair pair;
655 if (_ADG_OLD_ENTITY_CLASS->arrange)
656 _ADG_OLD_ENTITY_CLASS->arrange(entity);
658 adim = (AdgADim *) entity;
659 dim = (AdgDim *) adim;
660 data = adim->data;
661 quote = adg_dim_get_quote(dim);
663 _adg_update_geometry(adim);
664 _adg_update_entities(adim);
666 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
667 AdgEntity *quote_entity = (AdgEntity *) quote;
668 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
669 return;
672 local = adg_entity_get_local_matrix(entity);
673 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
674 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
675 cpml_pair_copy(&base1, &data->point.base1);
676 cpml_pair_copy(&base12, &data->point.base12);
677 cpml_pair_copy(&base2, &data->point.base2);
679 /* Apply the local matrix to the relevant points */
680 cpml_pair_transform(&ref1, local);
681 cpml_pair_transform(&ref2, local);
682 cpml_pair_transform(&base1, local);
683 cpml_pair_transform(&base12, local);
684 cpml_pair_transform(&base2, local);
686 /* Combine points and global shifts to build the path */
687 pair.x = ref1.x + data->shift.from1.x;
688 pair.y = ref1.y + data->shift.from1.y;
689 cpml_pair_to_cairo(&pair, &data->cpml.data[6]);
691 pair.x = base1.x + data->shift.base1.x;
692 pair.y = base1.y + data->shift.base1.y;
693 cpml_pair_to_cairo(&pair, &data->cpml.data[1]);
695 pair.x += data->shift.to1.x;
696 pair.y += data->shift.to1.y;
697 cpml_pair_to_cairo(&pair, &data->cpml.data[8]);
699 pair.x = base12.x + data->shift.base12.x;
700 pair.y = base12.y + data->shift.base12.y;
701 cpml_pair_to_cairo(&pair, &data->cpml.data[3]);
703 pair.x = ref2.x + data->shift.from2.x;
704 pair.y = ref2.y + data->shift.from2.y;
705 cpml_pair_to_cairo(&pair, &data->cpml.data[10]);
707 pair.x = base2.x + data->shift.base2.x;
708 pair.y = base2.y + data->shift.base2.y;
709 cpml_pair_to_cairo(&pair, &data->cpml.data[4]);
711 pair.x += data->shift.to2.x;
712 pair.y += data->shift.to2.y;
713 cpml_pair_to_cairo(&pair, &data->cpml.data[12]);
715 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
717 if (quote != NULL) {
718 /* Update global and local map of the quote */
719 AdgEntity *quote_entity;
720 gdouble angle;
721 AdgMatrix map;
723 quote_entity = (AdgEntity *) quote;
724 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
725 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
727 adg_alignment_set_factor_explicit(quote, 0.5, 0);
729 cairo_matrix_init_translate(&map, pair.x, pair.y);
730 cairo_matrix_rotate(&map, angle);
731 adg_entity_set_global_map(quote_entity, &map);
733 adg_matrix_copy(&data->quote.global_map,
734 adg_entity_get_global_map(quote_entity));
737 /* Signal to the markers (if any) that the path has changed */
738 if (data->marker1 != NULL) {
739 adg_marker_set_segment(data->marker1, data->trail, 1);
740 adg_entity_local_changed((AdgEntity *) data->marker1);
743 if (data->marker2 != NULL) {
744 adg_marker_set_segment(data->marker2, data->trail, 1);
745 adg_entity_local_changed((AdgEntity *) data->marker2);
748 /* TODO: compute the extents */
751 static void
752 _adg_render(AdgEntity *entity, cairo_t *cr)
754 AdgADim *adim;
755 AdgDim *dim;
756 AdgADimPrivate *data;
757 AdgDimStyle *dim_style;
758 AdgDress dress;
759 const cairo_path_t *cairo_path;
761 adim = (AdgADim *) entity;
762 dim = (AdgDim *) entity;
763 data = adim->data;
764 dim_style = _ADG_GET_DIM_STYLE(dim);
766 adg_style_apply((AdgStyle *) dim_style, entity, cr);
767 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
769 if (data->marker1 != NULL)
770 adg_entity_render((AdgEntity *) data->marker1, cr);
771 if (data->marker2 != NULL)
772 adg_entity_render((AdgEntity *) data->marker2, cr);
774 cairo_transform(cr, adg_entity_get_global_matrix(entity));
775 dress = adg_dim_style_get_line_dress(dim_style);
776 adg_entity_apply_dress(entity, dress, cr);
778 cairo_path = adg_trail_get_cairo_path(data->trail);
779 cairo_append_path(cr, cairo_path);
780 cairo_stroke(cr);
783 static gchar *
784 _adg_default_value(AdgDim *dim)
786 AdgADim *adim;
787 AdgADimPrivate *data;
788 AdgDimStyle *dim_style;
789 gdouble angle;
790 const gchar *format;
792 adim = (AdgADim *) dim;
793 data = adim->data;
794 dim_style = _ADG_GET_DIM_STYLE(dim);
795 format = adg_dim_style_get_number_format(dim_style);
797 _adg_update_geometry(adim);
798 angle = (data->angle2 - data->angle1) * 180 / M_PI;
800 return g_strdup_printf(format, angle);
803 /* With "geometry" is considered any data (point, vector or angle)
804 * that can be cached: this is strictly related on how the arrange()
805 * method works */
806 static void
807 _adg_update_geometry(AdgADim *adim)
809 AdgADimPrivate *data;
810 AdgDimStyle *dim_style;
811 gdouble from_offset, to_offset;
812 gdouble spacing, level;
813 CpmlVector vector[3];
814 CpmlPair center;
815 gdouble distance;
817 data = adim->data;
819 if (data->geometry_arranged)
820 return;
822 if (!_adg_get_info(adim, vector, &center, &distance)) {
823 /* Parallel lines: hang with an error message */
824 g_warning("%s: trying to set an angular dimension on parallel lines",
825 G_STRLOC);
826 return;
829 dim_style = _ADG_GET_DIM_STYLE(adim);
830 from_offset = adg_dim_style_get_from_offset(dim_style);
831 to_offset = adg_dim_style_get_to_offset(dim_style);
832 spacing = adg_dim_style_get_baseline_spacing(dim_style);
833 level = adg_dim_get_level((AdgDim *) adim);
835 /* shift.from1 */
836 cpml_vector_set_length(&vector[0], from_offset);
837 cpml_pair_copy(&data->shift.from1, &vector[0]);
839 /* shift.base1 */
840 cpml_vector_set_length(&vector[0], level * spacing);
841 cpml_pair_copy(&data->shift.base1, &vector[0]);
843 /* shift.to1 */
844 cpml_vector_set_length(&vector[0], to_offset);
845 cpml_pair_copy(&data->shift.to1, &vector[0]);
847 /* shift.from2 */
848 cpml_vector_set_length(&vector[2], from_offset);
849 cpml_pair_copy(&data->shift.from2, &vector[2]);
851 /* shift.base2 */
852 cpml_vector_set_length(&vector[2], level * spacing);
853 cpml_pair_copy(&data->shift.base2, &vector[2]);
855 /* shift.to2 */
856 cpml_vector_set_length(&vector[2], to_offset);
857 cpml_pair_copy(&data->shift.to2, &vector[2]);
859 /* shift.base12 */
860 cpml_vector_set_length(&vector[1], level * spacing);
861 cpml_pair_copy(&data->shift.base12, &vector[1]);
863 /* Distance can be 0, so the following will leave the
864 * vector array in undefined state */
866 /* point.base1 */
867 cpml_vector_set_length(&vector[0], distance);
868 data->point.base1.x = vector[0].x + center.x;
869 data->point.base1.y = vector[0].y + center.y;
871 /* point.base2 */
872 cpml_vector_set_length(&vector[2], distance);
873 data->point.base2.x = vector[2].x + center.x;
874 data->point.base2.y = vector[2].y + center.y;
876 /* point.base12 */
877 cpml_vector_set_length(&vector[1], distance);
878 data->point.base12.x = vector[1].x + center.x;
879 data->point.base12.y = vector[1].y + center.y;
881 data->geometry_arranged = TRUE;
884 static void
885 _adg_update_entities(AdgADim *adim)
887 AdgEntity *entity;
888 AdgADimPrivate *data;
889 AdgDimStyle *dim_style;
891 entity = (AdgEntity *) adim;
892 data = adim->data;
893 dim_style = _ADG_GET_DIM_STYLE(adim);
895 if (data->trail == NULL)
896 data->trail = adg_trail_new(_adg_trail_callback, adim);
898 if (data->marker1 == NULL) {
899 data->marker1 = adg_dim_style_marker1_new(dim_style);
900 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
903 if (data->marker2 == NULL) {
904 data->marker2 = adg_dim_style_marker2_new(dim_style);
905 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
909 static void
910 _adg_unset_trail(AdgADim *adim)
912 AdgADimPrivate *data = adim->data;
914 if (data->trail != NULL)
915 adg_model_clear((AdgModel *) data->trail);
917 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
920 static void
921 _adg_dispose_markers(AdgADim *adim)
923 AdgADimPrivate *data = adim->data;
925 if (data->trail != NULL) {
926 g_object_unref(data->trail);
927 data->trail = NULL;
930 if (data->marker1 != NULL) {
931 g_object_unref(data->marker1);
932 data->marker1 = NULL;
935 if (data->marker2 != NULL) {
936 g_object_unref(data->marker2);
937 data->marker2 = NULL;
941 static gboolean
942 _adg_get_info(AdgADim *adim, CpmlVector vector[],
943 AdgPair *center, gdouble *distance)
945 AdgDim *dim;
946 AdgADimPrivate *data;
947 const AdgPair *ref1, *ref2;
948 const AdgPair *org1, *org2;
949 gdouble factor;
951 dim = (AdgDim *) adim;
952 data = adim->data;
953 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
954 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
955 org1 = adg_point_get_pair(data->org1);
956 org2 = adg_point_get_pair(data->org2);
958 vector[0].x = ref1->x - org1->x;
959 vector[0].y = ref1->y - org1->y;
960 vector[2].x = ref2->x - org2->x;
961 vector[2].y = ref2->y - org2->y;
963 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
964 if (factor == 0)
965 return FALSE;
967 factor = ((ref1->y - ref2->y) * vector[2].x -
968 (ref1->x - ref2->x) * vector[2].y) / factor;
970 center->x = ref1->x + vector[0].x * factor;
971 center->y = ref1->y + vector[0].y * factor;
972 *distance = cpml_pair_distance(center, adg_point_get_pair(adg_dim_get_pos(dim)));
973 data->angle1 = cpml_vector_angle(&vector[0]);
974 data->angle2 = cpml_vector_angle(&vector[2]);
975 while (data->angle2 < data->angle1)
976 data->angle2 += M_PI * 2;
978 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
980 return TRUE;
983 static CpmlPath *
984 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
986 AdgADim *adim;
987 AdgADimPrivate *data;
989 adim = (AdgADim *) user_data;
990 data = adim->data;
992 return &data->cpml.path;