[AdgDim] "pos" and "ref1", "ref2" should be handled as AdgPoint
[adg.git] / adg / adg-adim.c
blob23624d26cae0bb051fadba0109d36d09b15e3fbb
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 if (data->org1 != NULL)
223 adg_point_destroy(data->org1);
224 data->org1 = g_value_dup_boxed(value);
225 break;
226 case PROP_ORG2:
227 if (data->org2 != NULL)
228 adg_point_destroy(data->org2);
229 data->org2 = g_value_dup_boxed(value);
230 break;
231 case PROP_HAS_EXTENSION1:
232 data->has_extension1 = g_value_get_boolean(value);
233 break;
234 case PROP_HAS_EXTENSION2:
235 data->has_extension2 = g_value_get_boolean(value);
236 break;
237 default:
238 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
239 break;
245 * adg_adim_new:
247 * Creates a new - undefined - angular dimension. You must, at least,
248 * define the reference points with adg_dim_set_ref(), the origins of
249 * the lines ending with the reference points with adg_adim_set_org()
250 * and the reference for positioning the quote with adg_dim_set_pos().
252 * Returns: the newly created angular dimension entity
255 * adg_adim_new:
257 * Creates a new - undefined - angular dimension. You must, at least,
258 * define the first line by setting #AdgADim:org1 (start point) and
259 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
260 * (start point) and #AdgDim:ref2 (end point) and the position of
261 * the quote with #AdgDim:pos.
263 * Returns: the newly created angular dimension entity
265 AdgADim *
266 adg_adim_new(void)
268 return g_object_new(ADG_TYPE_ADIM, NULL);
272 * adg_adim_new_full:
273 * @ref1: first reference point
274 * @ref2: second reference point
275 * @org1: first origin point
276 * @org2: second origin point
278 * Creates a new angular dimension, specifing all the needed
279 * properties in one shot.
281 * Returns: the newly created angular dimension entity
283 AdgADim *
284 adg_adim_new_full(const AdgPair *ref1, const AdgPair *ref2,
285 const AdgPair *org1, const AdgPair *org2,
286 const AdgPair *pos)
288 AdgADim *adim;
289 AdgDim *dim;
291 adim = g_object_new(ADG_TYPE_ADIM, NULL);
292 dim = (AdgDim *) adim;
294 adg_dim_set_ref1_from_pair(dim, ref1);
295 adg_dim_set_ref2_from_pair(dim, ref2);
296 adg_dim_set_pos_from_pair(dim, pos);
297 adg_adim_set_org(adim, org1, org2);
299 return adim;
303 * adg_adim_new_full_explicit:
304 * @ref1_x: the x coordinate of the first reference point
305 * @ref1_y: the y coordinate of the first reference point
306 * @ref2_x: the x coordinate of the second reference point
307 * @ref2_y: the y coordinate of the second reference point
308 * @direction: angle where to extend the dimension
309 * @pos_x: the x coordinate of the position reference
310 * @pos_y: the y coordinate of the position reference
312 * Wrappes adg_adim_new_full() with explicit values.
314 * Returns: the newly created linear dimension entity
316 AdgADim *
317 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
318 gdouble ref2_x, gdouble ref2_y,
319 gdouble org1_x, gdouble org1_y,
320 gdouble org2_x, gdouble org2_y,
321 gdouble pos_x, gdouble pos_y)
323 AdgPair ref1, ref2, org1, org2, pos;
325 ref1.x = ref1_x;
326 ref1.y = ref1_y;
327 ref2.x = ref2_x;
328 ref2.y = ref2_y;
329 org1.x = org1_x;
330 org1.y = org1_y;
331 org2.x = org2_x;
332 org2.y = org2_y;
333 pos.x = pos_x;
334 pos.y = pos_y;
336 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
340 * adg_adim_new_full_from_model:
341 * @model: the model from which the named pairs are taken
342 * @ref1: the end point of the first line
343 * @ref2: the end point of the second line
344 * @org1: the origin of the first line
345 * @org2: the origin of the second line
346 * @pos: the position reference
348 * Creates a new angular dimension, specifing all the needed properties
349 * in one shot and using named pairs from @model.
351 * Returns: the newly created angular dimension entity
353 AdgADim *
354 adg_adim_new_full_from_model(AdgModel *model,
355 const gchar *ref1, const gchar *ref2,
356 const gchar *org1, const gchar *org2,
357 const gchar *pos)
359 AdgADim *adim;
360 AdgDim *dim;
362 adim = g_object_new(ADG_TYPE_ADIM, NULL);
363 dim = (AdgDim *) adim;
365 adg_dim_set_ref1_from_model(dim, model, ref1);
366 adg_dim_set_ref2_from_model(dim, model, ref2);
367 adg_dim_set_pos_from_model(dim, model, pos);
368 adg_adim_set_org_from_model(adim, model, org1, org2);
370 return adim;
374 * adg_adim_set_org:
375 * @adim: an #AdgADim
376 * @org1: the first origin
377 * @org2: the second origin
379 * Sets at once the two origins on @adim. One of @org1 or @org2
380 * (but not both) could be %NULL, in which case only the non-null
381 * origin is set.
383 void
384 adg_adim_set_org(AdgADim *adim, const AdgPair *org1, const AdgPair *org2)
386 GObject *object;
387 AdgADimPrivate *data;
389 g_return_if_fail(ADG_IS_ADIM(adim));
390 g_return_if_fail(org1 != NULL || org2 != NULL);
392 object = (GObject *) adim;
393 data = adim->data;
395 g_object_freeze_notify(object);
397 if (org1 != NULL) {
398 if (data->org1 == NULL)
399 data->org1 = adg_point_new();
401 adg_point_set_pair(data->org1, org1);
403 g_object_notify(object, "org1");
406 if (org2 != NULL) {
407 if (data->org2 == NULL)
408 data->org2 = adg_point_new();
410 adg_point_set_pair(data->org2, org2);
412 g_object_notify(object, "org2");
415 g_object_thaw_notify(object);
419 * adg_adim_set_org_explicit:
420 * @adim: an #AdgADim
421 * @org1_x: x coordinate of org1
422 * @org1_y: y coordinate of org1
423 * @org2_x: x coordinate of org2
424 * @org2_y: y coordinate of org2
426 * Works in the same way as adg_adim_set_org() but using
427 * explicit coordinates instead of #AdgPair args. The
428 * notable difference is that, by using gdouble values,
429 * you can't set only a single origin point.
431 void
432 adg_adim_set_org_explicit(AdgADim *adim,
433 gdouble org1_x, gdouble org1_y,
434 gdouble org2_x, gdouble org2_y)
436 AdgPair org1, org2;
438 org1.x = org1_x;
439 org1.y = org1_y;
440 org2.x = org2_x;
441 org2.y = org2_y;
443 adg_adim_set_org(adim, &org1, &org2);
447 * adg_adim_set_org_from_model:
448 * @adim: an #AdgADim
449 * @model: the source #AdgModel
450 * @org1: name of the pair in @model to use as org1
451 * @org2: name of the pair in @model to use as org2
453 * Sets #AdgADim:org1 and #AdgADim:org2 properties by linking
454 * them to the @org1 and @org2 named pairs in @model. @org1
455 * or @org2 could be %NULL (but not both), in which case
456 * only the non-null origin point is changed.
458 * Using this function twice you can also link the origin
459 * points to named pairs taken from different models:
461 * |[
462 * adg_adim_set_org_from_model(adim, model1, org1, NULL);
463 * adg_adim_set_org_from_model(adim, model2, NULL, org2);
464 * ]|
466 void
467 adg_adim_set_org_from_model(AdgADim *adim, AdgModel *model,
468 const gchar *org1, const gchar *org2)
470 GObject *object;
471 AdgADimPrivate *data;
473 g_return_if_fail(ADG_IS_ADIM(adim));
474 g_return_if_fail(ADG_IS_MODEL(model));
475 g_return_if_fail(org1 != NULL || org2 != NULL);
477 object = (GObject *) adim;
478 data = adim->data;
480 g_object_freeze_notify(object);
482 if (org1 != NULL) {
483 if (data->org1 == NULL)
484 data->org1 = adg_point_new();
486 adg_point_set_pair_from_model(data->org1, model, org1);
488 g_object_notify(object, "org1");
491 if (org2 != NULL) {
492 if (data->org2 == NULL)
493 data->org2 = adg_point_new();
495 adg_point_set_pair_from_model(data->org2, model, org2);
497 g_object_notify(object, "org2");
500 g_object_thaw_notify(object);
504 * adg_adim_get_org1:
505 * @adim: an #AdgADim
507 * Gets the first origin of @adim. The returned pair is owned by
508 * @adim and should not be modified or freed.
510 * Returns: a pointer to the internal #AdgPair or %NULL on errors
512 const AdgPair *
513 adg_adim_get_org1(AdgADim *adim)
515 AdgADimPrivate *data;
517 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
519 data = adim->data;
521 return adg_point_get_pair(data->org1);
525 * adg_adim_get_org2:
526 * @adim: an #AdgADim
528 * Gets the second origin of @adim. The returned pair is owned by
529 * @adim and should not be modified or freed.
531 * Returns: a pointer to the internal #AdgPair or %NULL on errors
533 const AdgPair *
534 adg_adim_get_org2(AdgADim *adim)
536 AdgADimPrivate *data;
538 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
540 data = adim->data;
542 return adg_point_get_pair(data->org2);
546 static void
547 local_changed(AdgEntity *entity)
549 unset_trail((AdgADim *) entity);
551 if (PARENT_ENTITY_CLASS->local_changed)
552 PARENT_ENTITY_CLASS->local_changed(entity);
555 static void
556 invalidate(AdgEntity *entity)
558 AdgADim *adim;
559 AdgADimPrivate *data;
561 adim = (AdgADim *) entity;
562 data = adim->data;
564 dispose_markers(adim);
565 data->geometry_arranged = FALSE;
566 unset_trail(adim);
568 adg_point_invalidate(data->org1);
569 adg_point_invalidate(data->org2);
571 if (PARENT_ENTITY_CLASS->invalidate)
572 PARENT_ENTITY_CLASS->invalidate(entity);
575 static void
576 arrange(AdgEntity *entity)
578 AdgADim *adim;
579 AdgDim *dim;
580 AdgADimPrivate *data;
581 AdgAlignment *quote;
582 const AdgMatrix *local;
583 AdgPair ref1, ref2, base1, base12, base2;
584 AdgPair pair;
586 if (PARENT_ENTITY_CLASS->arrange)
587 PARENT_ENTITY_CLASS->arrange(entity);
589 adim = (AdgADim *) entity;
590 dim = (AdgDim *) adim;
591 data = adim->data;
592 quote = adg_dim_get_quote(dim);
594 update_geometry(adim);
595 update_entities(adim);
597 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
598 AdgEntity *quote_entity = (AdgEntity *) quote;
599 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
600 return;
603 local = adg_entity_get_local_matrix(entity);
604 cpml_pair_copy(&ref1, adg_point_get_pair(adg_dim_get_ref1(dim)));
605 cpml_pair_copy(&ref2, adg_point_get_pair(adg_dim_get_ref2(dim)));
606 cpml_pair_copy(&base1, &data->point.base1);
607 cpml_pair_copy(&base12, &data->point.base12);
608 cpml_pair_copy(&base2, &data->point.base2);
610 /* Apply the local matrix to the relevant points */
611 cpml_pair_transform(&ref1, local);
612 cpml_pair_transform(&ref2, local);
613 cpml_pair_transform(&base1, local);
614 cpml_pair_transform(&base12, local);
615 cpml_pair_transform(&base2, local);
617 /* Combine points and global shifts to build the path */
618 pair.x = ref1.x + data->shift.from1.x;
619 pair.y = ref1.y + data->shift.from1.y;
620 cpml_pair_to_cairo(&pair, &data->cpml.data[6]);
622 pair.x = base1.x + data->shift.base1.x;
623 pair.y = base1.y + data->shift.base1.y;
624 cpml_pair_to_cairo(&pair, &data->cpml.data[1]);
626 pair.x += data->shift.to1.x;
627 pair.y += data->shift.to1.y;
628 cpml_pair_to_cairo(&pair, &data->cpml.data[8]);
630 pair.x = base12.x + data->shift.base12.x;
631 pair.y = base12.y + data->shift.base12.y;
632 cpml_pair_to_cairo(&pair, &data->cpml.data[3]);
634 pair.x = ref2.x + data->shift.from2.x;
635 pair.y = ref2.y + data->shift.from2.y;
636 cpml_pair_to_cairo(&pair, &data->cpml.data[10]);
638 pair.x = base2.x + data->shift.base2.x;
639 pair.y = base2.y + data->shift.base2.y;
640 cpml_pair_to_cairo(&pair, &data->cpml.data[4]);
642 pair.x += data->shift.to2.x;
643 pair.y += data->shift.to2.y;
644 cpml_pair_to_cairo(&pair, &data->cpml.data[12]);
646 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
648 if (quote != NULL) {
649 /* Update global and local map of the quote */
650 AdgEntity *quote_entity;
651 gdouble angle;
652 AdgMatrix map;
654 quote_entity = (AdgEntity *) quote;
655 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
656 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
658 adg_alignment_set_factor_explicit(quote, 0.5, 0);
660 cairo_matrix_init_translate(&map, pair.x, pair.y);
661 cairo_matrix_rotate(&map, angle);
662 adg_entity_set_global_map(quote_entity, &map);
664 adg_matrix_copy(&data->quote.global_map,
665 adg_entity_get_global_map(quote_entity));
668 /* Signal to the markers (if any) that the path has changed */
669 if (data->marker1 != NULL) {
670 adg_marker_set_segment(data->marker1, data->trail, 1);
671 adg_entity_local_changed((AdgEntity *) data->marker1);
674 if (data->marker2 != NULL) {
675 adg_marker_set_segment(data->marker2, data->trail, 1);
676 adg_entity_local_changed((AdgEntity *) data->marker2);
679 /* TODO: compute the extents */
682 static void
683 render(AdgEntity *entity, cairo_t *cr)
685 AdgADim *adim;
686 AdgDim *dim;
687 AdgADimPrivate *data;
688 AdgDimStyle *dim_style;
689 AdgDress dress;
690 const cairo_path_t *cairo_path;
692 adim = (AdgADim *) entity;
693 dim = (AdgDim *) entity;
694 data = adim->data;
695 dim_style = GET_DIM_STYLE(dim);
697 adg_style_apply((AdgStyle *) dim_style, entity, cr);
699 if (data->marker1 != NULL)
700 adg_entity_render((AdgEntity *) data->marker1, cr);
702 if (data->marker2 != NULL)
703 adg_entity_render((AdgEntity *) data->marker2, cr);
705 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
707 dress = adg_dim_style_get_line_dress(dim_style);
708 adg_entity_apply_dress(entity, dress, cr);
710 cairo_path = adg_trail_get_cairo_path(data->trail);
711 cairo_append_path(cr, cairo_path);
712 cairo_stroke(cr);
715 static gchar *
716 default_value(AdgDim *dim)
718 AdgADim *adim;
719 AdgADimPrivate *data;
720 AdgDimStyle *dim_style;
721 gdouble angle;
722 const gchar *format;
724 adim = (AdgADim *) dim;
725 data = adim->data;
726 dim_style = GET_DIM_STYLE(dim);
727 format = adg_dim_style_get_number_format(dim_style);
729 update_geometry(adim);
730 angle = (data->angle2 - data->angle1) * 180 / M_PI;
732 return g_strdup_printf(format, angle);
735 /* With "geometry" is considered any data (point, vector or angle)
736 * that can be cached: this is strictly related on how the arrange()
737 * method works */
738 static void
739 update_geometry(AdgADim *adim)
741 AdgADimPrivate *data;
742 AdgDimStyle *dim_style;
743 gdouble from_offset, to_offset;
744 gdouble spacing, level;
745 CpmlVector vector[3];
746 CpmlPair center;
747 gdouble distance;
749 data = adim->data;
751 if (data->geometry_arranged)
752 return;
754 if (!get_info(adim, vector, &center, &distance)) {
755 /* Parallel lines: hang with an error message */
756 g_warning("%s: trying to set an angular dimension on parallel lines",
757 G_STRLOC);
758 return;
761 dim_style = GET_DIM_STYLE(adim);
762 from_offset = adg_dim_style_get_from_offset(dim_style);
763 to_offset = adg_dim_style_get_to_offset(dim_style);
764 spacing = adg_dim_style_get_baseline_spacing(dim_style);
765 level = adg_dim_get_level((AdgDim *) adim);
767 /* shift.from1 */
768 cpml_vector_set_length(&vector[0], from_offset);
769 cpml_pair_copy(&data->shift.from1, &vector[0]);
771 /* shift.base1 */
772 cpml_vector_set_length(&vector[0], level * spacing);
773 cpml_pair_copy(&data->shift.base1, &vector[0]);
775 /* shift.to1 */
776 cpml_vector_set_length(&vector[0], to_offset);
777 cpml_pair_copy(&data->shift.to1, &vector[0]);
779 /* shift.from2 */
780 cpml_vector_set_length(&vector[2], from_offset);
781 cpml_pair_copy(&data->shift.from2, &vector[2]);
783 /* shift.base2 */
784 cpml_vector_set_length(&vector[2], level * spacing);
785 cpml_pair_copy(&data->shift.base2, &vector[2]);
787 /* shift.to2 */
788 cpml_vector_set_length(&vector[2], to_offset);
789 cpml_pair_copy(&data->shift.to2, &vector[2]);
791 /* shift.base12 */
792 cpml_vector_set_length(&vector[1], level * spacing);
793 cpml_pair_copy(&data->shift.base12, &vector[1]);
795 /* Distance can be 0, so the following will leave the
796 * vector array in undefined state */
798 /* point.base1 */
799 cpml_vector_set_length(&vector[0], distance);
800 data->point.base1.x = vector[0].x + center.x;
801 data->point.base1.y = vector[0].y + center.y;
803 /* point.base2 */
804 cpml_vector_set_length(&vector[2], distance);
805 data->point.base2.x = vector[2].x + center.x;
806 data->point.base2.y = vector[2].y + center.y;
808 /* point.base12 */
809 cpml_vector_set_length(&vector[1], distance);
810 data->point.base12.x = vector[1].x + center.x;
811 data->point.base12.y = vector[1].y + center.y;
813 data->geometry_arranged = TRUE;
816 static void
817 update_entities(AdgADim *adim)
819 AdgADimPrivate *data;
820 AdgDimStyle *dim_style;
822 data = adim->data;
823 dim_style = GET_DIM_STYLE(adim);
825 if (data->trail == NULL)
826 data->trail = adg_trail_new(trail_callback, adim);
828 if (data->marker1 == NULL)
829 data->marker1 = adg_dim_style_marker1_new(dim_style);
831 if (data->marker2 == NULL)
832 data->marker2 = adg_dim_style_marker2_new(dim_style);
835 static void
836 unset_trail(AdgADim *adim)
838 AdgADimPrivate *data = adim->data;
840 if (data->trail != NULL)
841 adg_model_clear((AdgModel *) data->trail);
843 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
846 static void
847 dispose_markers(AdgADim *adim)
849 AdgADimPrivate *data = adim->data;
851 if (data->trail != NULL) {
852 g_object_unref(data->trail);
853 data->trail = NULL;
856 if (data->marker1 != NULL) {
857 g_object_unref(data->marker1);
858 data->marker1 = NULL;
861 if (data->marker2 != NULL) {
862 g_object_unref(data->marker2);
863 data->marker2 = NULL;
867 static gboolean
868 get_info(AdgADim *adim, CpmlVector vector[],
869 AdgPair *center, gdouble *distance)
871 AdgDim *dim;
872 AdgADimPrivate *data;
873 const AdgPair *ref1, *ref2;
874 const AdgPair *org1, *org2;
875 gdouble factor;
877 dim = (AdgDim *) adim;
878 data = adim->data;
879 ref1 = adg_point_get_pair(adg_dim_get_ref1(dim));
880 ref2 = adg_point_get_pair(adg_dim_get_ref2(dim));
881 org1 = adg_point_get_pair(data->org1);
882 org2 = adg_point_get_pair(data->org2);
884 vector[0].x = ref1->x - org1->x;
885 vector[0].y = ref1->y - org1->y;
886 vector[2].x = ref2->x - org2->x;
887 vector[2].y = ref2->y - org2->y;
889 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
890 if (factor == 0)
891 return FALSE;
893 factor = ((ref1->y - ref2->y) * vector[2].x -
894 (ref1->x - ref2->x) * vector[2].y) / factor;
896 center->x = ref1->x + vector[0].x * factor;
897 center->y = ref1->y + vector[0].y * factor;
898 *distance = cpml_pair_distance(center, adg_point_get_pair(adg_dim_get_pos(dim)));
899 data->angle1 = cpml_vector_angle(&vector[0]);
900 data->angle2 = cpml_vector_angle(&vector[2]);
901 while (data->angle2 < data->angle1)
902 data->angle2 += M_PI * 2;
904 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
906 return TRUE;
909 static CpmlPath *
910 trail_callback(AdgTrail *trail, gpointer user_data)
912 AdgADim *adim;
913 AdgADimPrivate *data;
915 adim = (AdgADim *) user_data;
916 data = adim->data;
918 return &data->cpml.path;