[build] Let "make distcheck" work again
[adg.git] / adg / adg-adim.c
blobcaf8f5e74c49f8fa157ef8e55da1b5ff944f50c4
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 PARENT_OBJECT_CLASS ((GObjectClass *) adg_adim_parent_class)
42 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_adim_parent_class)
45 enum {
46 PROP_0,
47 PROP_ORG1,
48 PROP_ORG2,
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 (AdgADim *adim);
70 static void update_entities (AdgADim *adim);
71 static void unset_trail (AdgADim *adim);
72 static void dispose_markers (AdgADim *adim);
73 static gboolean get_info (AdgADim *adim,
74 CpmlVector vector[],
75 AdgPair *center,
76 gdouble *distance);
77 static CpmlPath * trail_callback (AdgTrail *trail,
78 gpointer user_data);
81 G_DEFINE_TYPE(AdgADim, adg_adim, ADG_TYPE_DIM);
84 static void
85 adg_adim_class_init(AdgADimClass *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(AdgADimPrivate));
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_boxed("org1",
110 P_("First Origin"),
111 P_("Where the first line comes from: this point is used toghether with \"ref1\" to align the first extension line"),
112 ADG_TYPE_POINT,
113 G_PARAM_READWRITE);
114 g_object_class_install_property(gobject_class, PROP_ORG1, param);
116 param = g_param_spec_boxed("org2",
117 P_("Second Origin"),
118 P_("Where the second line comes from: this point is used toghether with \"ref2\" to align the second extension line"),
119 ADG_TYPE_POINT,
120 G_PARAM_READWRITE);
121 g_object_class_install_property(gobject_class, PROP_ORG2, param);
124 static void
125 adg_adim_init(AdgADim *adim)
127 AdgADimPrivate *data;
128 cairo_path_data_t move_to, line_to, arc_to;
130 data = G_TYPE_INSTANCE_GET_PRIVATE(adim, ADG_TYPE_ADIM, AdgADimPrivate);
131 move_to.header.type = CPML_MOVE;
132 move_to.header.length = 2;
133 line_to.header.type = CPML_LINE;
134 line_to.header.length = 2;
135 arc_to.header.type = CPML_ARC;
136 arc_to.header.length = 3;
138 data->org1 = NULL;
139 data->org2 = NULL;
140 data->has_extension1 = TRUE;
141 data->has_extension2 = TRUE;
143 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
144 data->cpml.path.data = data->cpml.data;
145 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
146 data->cpml.path.data[0] = move_to;
147 data->cpml.path.data[2] = arc_to;
148 data->cpml.path.data[5] = move_to;
149 data->cpml.path.data[7] = line_to;
150 data->cpml.path.data[9] = move_to;
151 data->cpml.path.data[11] = line_to;
153 data->trail = NULL;
154 data->marker1 = NULL;
155 data->marker2 = NULL;
157 data->geometry_arranged = FALSE;
159 adim->data = data;
162 static void
163 dispose(GObject *object)
165 AdgADim *adim;
166 AdgADimPrivate *data;
168 adim = (AdgADim *) object;
169 data = adim->data;
171 dispose_markers((AdgADim *) object);
173 if (data->org1 != NULL) {
174 adg_point_destroy(data->org1);
175 data->org1 = NULL;
177 if (data->org2 != NULL) {
178 adg_point_destroy(data->org2);
179 data->org2 = NULL;
182 if (PARENT_OBJECT_CLASS->dispose)
183 PARENT_OBJECT_CLASS->dispose(object);
186 static void
187 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
189 AdgADimPrivate *data = ((AdgADim *) object)->data;
191 switch (prop_id) {
192 case PROP_ORG1:
193 g_value_set_boxed(value, data->org1);
194 break;
195 case PROP_ORG2:
196 g_value_set_boxed(value, data->org2);
197 break;
198 case PROP_HAS_EXTENSION1:
199 g_value_set_boolean(value, data->has_extension1);
200 break;
201 case PROP_HAS_EXTENSION2:
202 g_value_set_boolean(value, data->has_extension2);
203 break;
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
206 break;
210 static void
211 set_property(GObject *object, guint prop_id,
212 const GValue *value, GParamSpec *pspec)
214 AdgADim *adim;
215 AdgADimPrivate *data;
217 adim = (AdgADim *) object;
218 data = adim->data;
220 switch (prop_id) {
221 case PROP_ORG1:
222 adg_point_set(&data->org1, g_value_get_boxed(value));
223 break;
224 case PROP_ORG2:
225 adg_point_set(&data->org2, g_value_get_boxed(value));
226 break;
227 case PROP_HAS_EXTENSION1:
228 data->has_extension1 = g_value_get_boolean(value);
229 break;
230 case PROP_HAS_EXTENSION2:
231 data->has_extension2 = g_value_get_boolean(value);
232 break;
233 default:
234 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
235 break;
241 * adg_adim_new:
243 * Creates a new - undefined - angular dimension. You must, at least,
244 * define the first line by setting #AdgADim:org1 (start point) and
245 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
246 * (start point) and #AdgDim:ref2 (end point) and the position of
247 * the quote in #AdgDim:pos.
249 * Returns: the newly created angular dimension entity
251 AdgADim *
252 adg_adim_new(void)
254 return g_object_new(ADG_TYPE_ADIM, NULL);
258 * adg_adim_new_full:
259 * @ref1: first reference point
260 * @ref2: second reference point
261 * @org1: first origin point
262 * @org2: second origin point
264 * Creates a new angular dimension, specifing all the needed
265 * properties in one shot using #AdgPair.
267 * Returns: the newly created angular dimension entity
269 AdgADim *
270 adg_adim_new_full(const AdgPair *ref1, const AdgPair *ref2,
271 const AdgPair *org1, const AdgPair *org2,
272 const AdgPair *pos)
274 AdgADim *adim;
275 AdgDim *dim;
277 adim = g_object_new(ADG_TYPE_ADIM, NULL);
278 dim = (AdgDim *) adim;
280 adg_dim_set_ref1_from_pair(dim, ref1);
281 adg_dim_set_ref2_from_pair(dim, ref2);
282 adg_dim_set_pos_from_pair(dim, pos);
283 adg_adim_set_org1_from_pair(adim, org1);
284 adg_adim_set_org2_from_pair(adim, org2);
286 return adim;
290 * adg_adim_new_full_explicit:
291 * @ref1_x: the x coordinate of the first reference point
292 * @ref1_y: the y coordinate of the first reference point
293 * @ref2_x: the x coordinate of the second reference point
294 * @ref2_y: the y coordinate of the second reference point
295 * @direction: angle where to extend the dimension
296 * @pos_x: the x coordinate of the position reference
297 * @pos_y: the y coordinate of the position reference
299 * Wrappes adg_adim_new_full() with explicit values.
301 * Returns: the newly created linear dimension entity
303 AdgADim *
304 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
305 gdouble ref2_x, gdouble ref2_y,
306 gdouble org1_x, gdouble org1_y,
307 gdouble org2_x, gdouble org2_y,
308 gdouble pos_x, gdouble pos_y)
310 AdgPair ref1, ref2, org1, org2, pos;
312 ref1.x = ref1_x;
313 ref1.y = ref1_y;
314 ref2.x = ref2_x;
315 ref2.y = ref2_y;
316 org1.x = org1_x;
317 org1.y = org1_y;
318 org2.x = org2_x;
319 org2.y = org2_y;
320 pos.x = pos_x;
321 pos.y = pos_y;
323 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
327 * adg_adim_new_full_from_model:
328 * @model: the model from which the named pairs are taken
329 * @ref1: the end point of the first line
330 * @ref2: the end point of the second line
331 * @org1: the origin of the first line
332 * @org2: the origin of the second line
333 * @pos: the position reference
335 * Creates a new angular dimension, specifing all the needed properties
336 * in one shot and using named pairs from @model.
338 * Returns: the newly created angular dimension entity
340 AdgADim *
341 adg_adim_new_full_from_model(AdgModel *model,
342 const gchar *ref1, const gchar *ref2,
343 const gchar *org1, const gchar *org2,
344 const gchar *pos)
346 AdgADim *adim;
347 AdgDim *dim;
349 adim = g_object_new(ADG_TYPE_ADIM, NULL);
350 dim = (AdgDim *) adim;
352 adg_dim_set_ref1_from_model(dim, model, ref1);
353 adg_dim_set_ref2_from_model(dim, model, ref2);
354 adg_dim_set_pos_from_model(dim, model, pos);
355 adg_adim_set_org1_from_model(adim, model, org1);
356 adg_adim_set_org2_from_model(adim, model, org2);
358 return adim;
362 * adg_adim_set_org1:
363 * @adim: an #AdgADim
364 * @org1: the new point to use as first reference
366 * Sets the #AdgADim:org1 property to @org1. The old point
367 * is silently discarded, unreferencing its model if that
368 * point was bound to a named pair (hence, possibly destroying
369 * the model if this was the last reference).
371 * @org1 can be %NULL, in which case the point is unset.
373 void
374 adg_adim_set_org1(AdgADim *adim, const AdgPoint *org1)
376 AdgADimPrivate *data;
378 g_return_if_fail(ADG_IS_ADIM(adim));
380 data = adim->data;
382 if (adg_point_set(&data->org1, org1))
383 g_object_notify((GObject *) adim, "org1");
387 * adg_adim_set_org1_explicit:
388 * @adim: an #AdgADim
389 * @x: x coordinate of the first reference point
390 * @y: y coordinate of the first reference point
392 * Sets the #AdgADim:org1 property to the (@x, @y) explicit
393 * coordinates. The old point is silently discarded,
394 * unreferencing its model if that point was bound to a named
395 * pair (hence, possibly destroying the model if this was the
396 * last reference).
398 void
399 adg_adim_set_org1_explicit(AdgADim *adim, gdouble x, gdouble y)
401 AdgPoint *point = adg_point_new();
403 adg_point_set_pair_explicit(point, x, y);
404 adg_adim_set_org1(adim, point);
406 adg_point_destroy(point);
410 * adg_adim_set_org1_from_pair:
411 * @adim: an #AdgADim
412 * @org1: the coordinates pair of the first reference point
414 * Convenient function to set the #AdgADim:org1 property using a
415 * pair instead of explicit coordinates.
417 void
418 adg_adim_set_org1_from_pair(AdgADim *adim, const AdgPair *org1)
420 g_return_if_fail(org1 != NULL);
422 adg_adim_set_org1_explicit(adim, org1->x, org1->y);
426 * adg_adim_set_org1_from_model:
427 * @adim: an #AdgADim
428 * @model: the source #AdgModel
429 * @org1: a named pair in @model
431 * Binds #AdgADim:org1 to the @org1 named pair of @model. If @model
432 * is %NULL, the point will be unset. In any case, the old point
433 * is silently discarded, unreferencing its model if that point
434 * was bound to a named pair (hence, possibly destroying the model
435 * if this was the last reference).
437 * The assignment is lazy so @org1 could be not be present in @model.
438 * Anyway, at the first access to this point an error will be raised
439 * if the named pair is still missing.
441 void
442 adg_adim_set_org1_from_model(AdgADim *adim, AdgModel *model, const gchar *org1)
444 AdgPoint *point = adg_point_new();
446 adg_point_set_pair_from_model(point, model, org1);
447 adg_adim_set_org1(adim, point);
449 adg_point_destroy(point);
453 * adg_adim_get_org1:
454 * @adim: an #AdgADim
456 * Gets the #AdgADim:org1 point. The returned point is internally owned
457 * and must not be freed or modified. Anyway, it is not const because
458 * adg_point_get_pair() must be able to modify the internal cache of
459 * the returned point.
461 * Returns: the first reference point
463 AdgPoint *
464 adg_adim_get_org1(AdgADim *adim)
466 AdgADimPrivate *data;
468 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
470 data = adim->data;
472 return data->org1;
476 * adg_adim_set_org2:
477 * @adim: an #AdgADim
478 * @org2: the new point to use as first reference
480 * Sets the #AdgADim:org2 property to @org2. The old point
481 * is silently discarded, unreferencing its model if that
482 * point was bound to a named pair (hence, possibly destroying
483 * the model if this was the last reference).
485 * @org2 can be %NULL, in which case the point is unset.
487 void
488 adg_adim_set_org2(AdgADim *adim, const AdgPoint *org2)
490 AdgADimPrivate *data;
492 g_return_if_fail(ADG_IS_ADIM(adim));
494 data = adim->data;
496 if (adg_point_set(&data->org2, org2))
497 g_object_notify((GObject *) adim, "org2");
501 * adg_adim_set_org2_explicit:
502 * @adim: an #AdgADim
503 * @x: x coordinate of the first reference point
504 * @y: y coordinate of the first reference point
506 * Sets the #AdgADim:org2 property to the (@x, @y) explicit
507 * coordinates. The old point is silently discarded,
508 * unreferencing its model if that point was bound to a named
509 * pair (hence, possibly destroying the model if this was the
510 * last reference).
512 void
513 adg_adim_set_org2_explicit(AdgADim *adim, gdouble x, gdouble y)
515 AdgPoint *point = adg_point_new();
517 adg_point_set_pair_explicit(point, x, y);
518 adg_adim_set_org2(adim, point);
520 adg_point_destroy(point);
524 * adg_adim_set_org2_from_pair:
525 * @adim: an #AdgADim
526 * @org2: the coordinates pair of the first reference point
528 * Convenient function to set the #AdgADim:org2 property using a
529 * pair instead of explicit coordinates.
531 void
532 adg_adim_set_org2_from_pair(AdgADim *adim, const AdgPair *org2)
534 g_return_if_fail(org2 != NULL);
536 adg_adim_set_org2_explicit(adim, org2->x, org2->y);
540 * adg_adim_set_org2_from_model:
541 * @adim: an #AdgADim
542 * @model: the source #AdgModel
543 * @org2: a named pair in @model
545 * Binds #AdgADim:org2 to the @org2 named pair of @model. If @model
546 * is %NULL, the point will be unset. In any case, the old point
547 * is silently discarded, unreferencing its model if that point
548 * was bound to a named pair (hence, possibly destroying the model
549 * if this was the last reference).
551 * The assignment is lazy so @org2 could be not be present in @model.
552 * Anyway, at the first access to this point an error will be raised
553 * if the named pair is still missing.
555 void
556 adg_adim_set_org2_from_model(AdgADim *adim, AdgModel *model, const gchar *org2)
558 AdgPoint *point = adg_point_new();
560 adg_point_set_pair_from_model(point, model, org2);
561 adg_adim_set_org2(adim, point);
563 adg_point_destroy(point);
567 * adg_adim_get_org2:
568 * @adim: an #AdgADim
570 * Gets the #AdgADim:org2 point. The returned point is internally owned
571 * and must not be freed or modified. Anyway, it is not const because
572 * adg_point_get_pair() must be able to modify the internal cache of
573 * the returned point.
575 * Returns: the first reference point
577 AdgPoint *
578 adg_adim_get_org2(AdgADim *adim)
580 AdgADimPrivate *data;
582 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
584 data = adim->data;
586 return data->org2;
590 static void
591 local_changed(AdgEntity *entity)
593 unset_trail((AdgADim *) entity);
595 if (PARENT_ENTITY_CLASS->local_changed)
596 PARENT_ENTITY_CLASS->local_changed(entity);
599 static void
600 invalidate(AdgEntity *entity)
602 AdgADim *adim;
603 AdgADimPrivate *data;
605 adim = (AdgADim *) entity;
606 data = adim->data;
608 dispose_markers(adim);
609 data->geometry_arranged = FALSE;
610 unset_trail(adim);
612 adg_point_invalidate(data->org1);
613 adg_point_invalidate(data->org2);
615 if (PARENT_ENTITY_CLASS->invalidate)
616 PARENT_ENTITY_CLASS->invalidate(entity);
619 static void
620 arrange(AdgEntity *entity)
622 AdgADim *adim;
623 AdgDim *dim;
624 AdgADimPrivate *data;
625 AdgAlignment *quote;
626 const AdgMatrix *local;
627 AdgPair ref1, ref2, base1, base12, base2;
628 AdgPair pair;
630 if (PARENT_ENTITY_CLASS->arrange)
631 PARENT_ENTITY_CLASS->arrange(entity);
633 adim = (AdgADim *) entity;
634 dim = (AdgDim *) adim;
635 data = adim->data;
636 quote = adg_dim_get_quote(dim);
638 update_geometry(adim);
639 update_entities(adim);
641 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
642 AdgEntity *quote_entity = (AdgEntity *) quote;
643 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
644 return;
647 local = adg_entity_get_local_matrix(entity);
648 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
649 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
650 cpml_pair_copy(&base1, &data->point.base1);
651 cpml_pair_copy(&base12, &data->point.base12);
652 cpml_pair_copy(&base2, &data->point.base2);
654 /* Apply the local matrix to the relevant points */
655 cpml_pair_transform(&ref1, local);
656 cpml_pair_transform(&ref2, local);
657 cpml_pair_transform(&base1, local);
658 cpml_pair_transform(&base12, local);
659 cpml_pair_transform(&base2, local);
661 /* Combine points and global shifts to build the path */
662 pair.x = ref1.x + data->shift.from1.x;
663 pair.y = ref1.y + data->shift.from1.y;
664 cpml_pair_to_cairo(&pair, &data->cpml.data[6]);
666 pair.x = base1.x + data->shift.base1.x;
667 pair.y = base1.y + data->shift.base1.y;
668 cpml_pair_to_cairo(&pair, &data->cpml.data[1]);
670 pair.x += data->shift.to1.x;
671 pair.y += data->shift.to1.y;
672 cpml_pair_to_cairo(&pair, &data->cpml.data[8]);
674 pair.x = base12.x + data->shift.base12.x;
675 pair.y = base12.y + data->shift.base12.y;
676 cpml_pair_to_cairo(&pair, &data->cpml.data[3]);
678 pair.x = ref2.x + data->shift.from2.x;
679 pair.y = ref2.y + data->shift.from2.y;
680 cpml_pair_to_cairo(&pair, &data->cpml.data[10]);
682 pair.x = base2.x + data->shift.base2.x;
683 pair.y = base2.y + data->shift.base2.y;
684 cpml_pair_to_cairo(&pair, &data->cpml.data[4]);
686 pair.x += data->shift.to2.x;
687 pair.y += data->shift.to2.y;
688 cpml_pair_to_cairo(&pair, &data->cpml.data[12]);
690 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
692 if (quote != NULL) {
693 /* Update global and local map of the quote */
694 AdgEntity *quote_entity;
695 gdouble angle;
696 AdgMatrix map;
698 quote_entity = (AdgEntity *) quote;
699 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
700 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
702 adg_alignment_set_factor_explicit(quote, 0.5, 0);
704 cairo_matrix_init_translate(&map, pair.x, pair.y);
705 cairo_matrix_rotate(&map, angle);
706 adg_entity_set_global_map(quote_entity, &map);
708 adg_matrix_copy(&data->quote.global_map,
709 adg_entity_get_global_map(quote_entity));
712 /* Signal to the markers (if any) that the path has changed */
713 if (data->marker1 != NULL) {
714 adg_marker_set_segment(data->marker1, data->trail, 1);
715 adg_entity_local_changed((AdgEntity *) data->marker1);
718 if (data->marker2 != NULL) {
719 adg_marker_set_segment(data->marker2, data->trail, 1);
720 adg_entity_local_changed((AdgEntity *) data->marker2);
723 /* TODO: compute the extents */
726 static void
727 render(AdgEntity *entity, cairo_t *cr)
729 AdgADim *adim;
730 AdgDim *dim;
731 AdgADimPrivate *data;
732 AdgDimStyle *dim_style;
733 AdgDress dress;
734 const cairo_path_t *cairo_path;
736 adim = (AdgADim *) entity;
737 dim = (AdgDim *) entity;
738 data = adim->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 AdgADim *adim;
763 AdgADimPrivate *data;
764 AdgDimStyle *dim_style;
765 gdouble angle;
766 const gchar *format;
768 adim = (AdgADim *) dim;
769 data = adim->data;
770 dim_style = GET_DIM_STYLE(dim);
771 format = adg_dim_style_get_number_format(dim_style);
773 update_geometry(adim);
774 angle = (data->angle2 - data->angle1) * 180 / M_PI;
776 return g_strdup_printf(format, angle);
779 /* With "geometry" is considered any data (point, vector or angle)
780 * that can be cached: this is strictly related on how the arrange()
781 * method works */
782 static void
783 update_geometry(AdgADim *adim)
785 AdgADimPrivate *data;
786 AdgDimStyle *dim_style;
787 gdouble from_offset, to_offset;
788 gdouble spacing, level;
789 CpmlVector vector[3];
790 CpmlPair center;
791 gdouble distance;
793 data = adim->data;
795 if (data->geometry_arranged)
796 return;
798 if (!get_info(adim, vector, &center, &distance)) {
799 /* Parallel lines: hang with an error message */
800 g_warning("%s: trying to set an angular dimension on parallel lines",
801 G_STRLOC);
802 return;
805 dim_style = GET_DIM_STYLE(adim);
806 from_offset = adg_dim_style_get_from_offset(dim_style);
807 to_offset = adg_dim_style_get_to_offset(dim_style);
808 spacing = adg_dim_style_get_baseline_spacing(dim_style);
809 level = adg_dim_get_level((AdgDim *) adim);
811 /* shift.from1 */
812 cpml_vector_set_length(&vector[0], from_offset);
813 cpml_pair_copy(&data->shift.from1, &vector[0]);
815 /* shift.base1 */
816 cpml_vector_set_length(&vector[0], level * spacing);
817 cpml_pair_copy(&data->shift.base1, &vector[0]);
819 /* shift.to1 */
820 cpml_vector_set_length(&vector[0], to_offset);
821 cpml_pair_copy(&data->shift.to1, &vector[0]);
823 /* shift.from2 */
824 cpml_vector_set_length(&vector[2], from_offset);
825 cpml_pair_copy(&data->shift.from2, &vector[2]);
827 /* shift.base2 */
828 cpml_vector_set_length(&vector[2], level * spacing);
829 cpml_pair_copy(&data->shift.base2, &vector[2]);
831 /* shift.to2 */
832 cpml_vector_set_length(&vector[2], to_offset);
833 cpml_pair_copy(&data->shift.to2, &vector[2]);
835 /* shift.base12 */
836 cpml_vector_set_length(&vector[1], level * spacing);
837 cpml_pair_copy(&data->shift.base12, &vector[1]);
839 /* Distance can be 0, so the following will leave the
840 * vector array in undefined state */
842 /* point.base1 */
843 cpml_vector_set_length(&vector[0], distance);
844 data->point.base1.x = vector[0].x + center.x;
845 data->point.base1.y = vector[0].y + center.y;
847 /* point.base2 */
848 cpml_vector_set_length(&vector[2], distance);
849 data->point.base2.x = vector[2].x + center.x;
850 data->point.base2.y = vector[2].y + center.y;
852 /* point.base12 */
853 cpml_vector_set_length(&vector[1], distance);
854 data->point.base12.x = vector[1].x + center.x;
855 data->point.base12.y = vector[1].y + center.y;
857 data->geometry_arranged = TRUE;
860 static void
861 update_entities(AdgADim *adim)
863 AdgADimPrivate *data;
864 AdgDimStyle *dim_style;
866 data = adim->data;
867 dim_style = GET_DIM_STYLE(adim);
869 if (data->trail == NULL)
870 data->trail = adg_trail_new(trail_callback, adim);
872 if (data->marker1 == NULL)
873 data->marker1 = adg_dim_style_marker1_new(dim_style);
875 if (data->marker2 == NULL)
876 data->marker2 = adg_dim_style_marker2_new(dim_style);
879 static void
880 unset_trail(AdgADim *adim)
882 AdgADimPrivate *data = adim->data;
884 if (data->trail != NULL)
885 adg_model_clear((AdgModel *) data->trail);
887 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
890 static void
891 dispose_markers(AdgADim *adim)
893 AdgADimPrivate *data = adim->data;
895 if (data->trail != NULL) {
896 g_object_unref(data->trail);
897 data->trail = NULL;
900 if (data->marker1 != NULL) {
901 g_object_unref(data->marker1);
902 data->marker1 = NULL;
905 if (data->marker2 != NULL) {
906 g_object_unref(data->marker2);
907 data->marker2 = NULL;
911 static gboolean
912 get_info(AdgADim *adim, CpmlVector vector[],
913 AdgPair *center, gdouble *distance)
915 AdgDim *dim;
916 AdgADimPrivate *data;
917 const AdgPair *ref1, *ref2;
918 const AdgPair *org1, *org2;
919 gdouble factor;
921 dim = (AdgDim *) adim;
922 data = adim->data;
923 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
924 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
925 org1 = adg_point_get_pair(data->org1);
926 org2 = adg_point_get_pair(data->org2);
928 vector[0].x = ref1->x - org1->x;
929 vector[0].y = ref1->y - org1->y;
930 vector[2].x = ref2->x - org2->x;
931 vector[2].y = ref2->y - org2->y;
933 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
934 if (factor == 0)
935 return FALSE;
937 factor = ((ref1->y - ref2->y) * vector[2].x -
938 (ref1->x - ref2->x) * vector[2].y) / factor;
940 center->x = ref1->x + vector[0].x * factor;
941 center->y = ref1->y + vector[0].y * factor;
942 *distance = cpml_pair_distance(center, adg_point_get_pair(adg_dim_get_pos(dim)));
943 data->angle1 = cpml_vector_angle(&vector[0]);
944 data->angle2 = cpml_vector_angle(&vector[2]);
945 while (data->angle2 < data->angle1)
946 data->angle2 += M_PI * 2;
948 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
950 return TRUE;
953 static CpmlPath *
954 trail_callback(AdgTrail *trail, gpointer user_data)
956 AdgADim *adim;
957 AdgADimPrivate *data;
959 adim = (AdgADim *) user_data;
960 data = adim->data;
962 return &data->cpml.path;