[demo] Updated adg-demo to use named pairs where possible
[adg.git] / adg / adg-adim.c
blobafa608f1500c50b7037b60b4d551a8a92c55311c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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-adim.h"
37 #include "adg-adim-private.h"
38 #include "adg-dim-private.h"
39 #include "adg-intl.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 gboolean set_org1 (AdgADim *adim,
72 const AdgPair *org1);
73 static gboolean set_org2 (AdgADim *adim,
74 const AdgPair *org2);
75 static void unset_trail (AdgADim *adim);
76 static void dispose_markers (AdgADim *adim);
77 static gboolean get_info (AdgADim *adim,
78 CpmlVector vector[],
79 AdgPair *center,
80 gdouble *distance);
81 static CpmlPath * trail_callback (AdgTrail *trail,
82 gpointer user_data);
85 G_DEFINE_TYPE(AdgADim, adg_adim, ADG_TYPE_DIM);
88 static void
89 adg_adim_class_init(AdgADimClass *klass)
91 GObjectClass *gobject_class;
92 AdgEntityClass *entity_class;
93 AdgDimClass *dim_class;
94 GParamSpec *param;
96 gobject_class = (GObjectClass *) klass;
97 entity_class = (AdgEntityClass *) klass;
98 dim_class = (AdgDimClass *) klass;
100 g_type_class_add_private(klass, sizeof(AdgADimPrivate));
102 gobject_class->dispose = dispose;
103 gobject_class->get_property = get_property;
104 gobject_class->set_property = set_property;
106 entity_class->local_changed = local_changed;
107 entity_class->invalidate = invalidate;
108 entity_class->arrange = arrange;
109 entity_class->render = render;
111 dim_class->default_value = default_value;
113 param = g_param_spec_boxed("org1",
114 P_("First Origin"),
115 P_("Where the first line comes from: this point is used toghether with \"ref1\" to align the first extension line"),
116 ADG_TYPE_PAIR,
117 G_PARAM_READWRITE);
118 g_object_class_install_property(gobject_class, PROP_ORG1, param);
120 param = g_param_spec_boxed("org2",
121 P_("Second Origin"),
122 P_("Where the second line comes from: this point is used toghether with \"ref2\" to align the second extension line"),
123 ADG_TYPE_PAIR,
124 G_PARAM_READWRITE);
125 g_object_class_install_property(gobject_class, PROP_ORG2, param);
128 static void
129 adg_adim_init(AdgADim *adim)
131 AdgADimPrivate *data;
132 cairo_path_data_t move_to, line_to, arc_to;
134 data = G_TYPE_INSTANCE_GET_PRIVATE(adim, ADG_TYPE_ADIM, AdgADimPrivate);
135 move_to.header.type = CAIRO_PATH_MOVE_TO;
136 move_to.header.length = 2;
137 line_to.header.type = CAIRO_PATH_LINE_TO;
138 line_to.header.length = 2;
139 arc_to.header.type = CAIRO_PATH_ARC_TO;
140 arc_to.header.length = 3;
142 data->org1.x = data->org1.y = 0;
143 data->org2.x = data->org2.y = 0;
144 data->has_extension1 = TRUE;
145 data->has_extension2 = TRUE;
147 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
148 data->cpml.path.data = data->cpml.data;
149 data->cpml.path.num_data = G_N_ELEMENTS(data->cpml.data);
150 data->cpml.path.data[0] = move_to;
151 data->cpml.path.data[2] = arc_to;
152 data->cpml.path.data[5] = move_to;
153 data->cpml.path.data[7] = line_to;
154 data->cpml.path.data[9] = move_to;
155 data->cpml.path.data[11] = line_to;
157 data->trail = NULL;
158 data->marker1 = NULL;
159 data->marker2 = NULL;
161 data->geometry_arranged = FALSE;
163 adim->data = data;
166 static void
167 dispose(GObject *object)
169 dispose_markers((AdgADim *) object);
171 if (PARENT_OBJECT_CLASS->dispose != NULL)
172 PARENT_OBJECT_CLASS->dispose(object);
175 static void
176 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
178 AdgADimPrivate *data = ((AdgADim *) object)->data;
180 switch (prop_id) {
181 case PROP_ORG1:
182 g_value_set_boxed(value, &data->org1);
183 break;
184 case PROP_ORG2:
185 g_value_set_boxed(value, &data->org2);
186 break;
187 case PROP_HAS_EXTENSION1:
188 g_value_set_boolean(value, data->has_extension1);
189 break;
190 case PROP_HAS_EXTENSION2:
191 g_value_set_boolean(value, data->has_extension2);
192 break;
193 default:
194 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
195 break;
199 static void
200 set_property(GObject *object, guint prop_id,
201 const GValue *value, GParamSpec *pspec)
203 AdgADim *adim;
204 AdgADimPrivate *data;
206 adim = (AdgADim *) object;
207 data = adim->data;
209 switch (prop_id) {
210 case PROP_ORG1:
211 set_org1(adim, g_value_get_boxed(value));
212 break;
213 case PROP_ORG2:
214 set_org2(adim, g_value_get_boxed(value));
215 break;
216 case PROP_HAS_EXTENSION1:
217 data->has_extension1 = g_value_get_boolean(value);
218 break;
219 case PROP_HAS_EXTENSION2:
220 data->has_extension2 = g_value_get_boolean(value);
221 break;
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
224 break;
230 * adg_adim_new:
232 * Creates a new - undefined - angular dimension. You must, at least,
233 * define the reference points with adg_dim_set_ref(), the origins of
234 * the lines ending with the reference points with adg_adim_set_org()
235 * and the reference for positioning the quote with adg_dim_set_pos().
237 * Returns: the newly created angular dimension entity
239 AdgADim *
240 adg_adim_new(void)
242 return g_object_new(ADG_TYPE_ADIM, NULL);
246 * adg_adim_new_full:
247 * @ref1: first reference point
248 * @ref2: second reference point
249 * @org1: first origin point
250 * @org2: second origin point
252 * Creates a new angular dimension, specifing all the needed
253 * properties in one shot.
255 * Returns: the newly created angular dimension entity
257 AdgADim *
258 adg_adim_new_full(const AdgPair *ref1, const AdgPair *ref2,
259 const AdgPair *org1, const AdgPair *org2,
260 const AdgPair *pos)
262 AdgADim *adim;
263 AdgDim *dim;
265 adim = g_object_new(ADG_TYPE_ADIM, "org1", org1, "org2", org2, NULL);
266 dim = (AdgDim *) adim;
268 adg_dim_set_ref(dim, ref1, ref2);
269 adg_dim_set_pos(dim, pos);
271 return adim;
275 * adg_adim_new_full_explicit:
276 * @ref1_x: the x coordinate of the first reference point
277 * @ref1_y: the y coordinate of the first reference point
278 * @ref2_x: the x coordinate of the second reference point
279 * @ref2_y: the y coordinate of the second reference point
280 * @direction: angle where to extend the dimension
281 * @pos_x: the x coordinate of the position reference
282 * @pos_y: the y coordinate of the position reference
284 * Wrappes adg_adim_new_full() with explicit values.
286 * Returns: the newly created linear dimension entity
288 AdgADim *
289 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
290 gdouble ref2_x, gdouble ref2_y,
291 gdouble org1_x, gdouble org1_y,
292 gdouble org2_x, gdouble org2_y,
293 gdouble pos_x, gdouble pos_y)
295 AdgPair ref1, ref2, org1, org2, pos;
297 ref1.x = ref1_x;
298 ref1.y = ref1_y;
299 ref2.x = ref2_x;
300 ref2.y = ref2_y;
301 org1.x = org1_x;
302 org1.y = org1_y;
303 org2.x = org2_x;
304 org2.y = org2_y;
305 pos.x = pos_x;
306 pos.y = pos_y;
308 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
312 * adg_adim_get_org1:
313 * @adim: an #AdgADim
315 * Gets the first origin of @adim. The returned pair is owned by
316 * @adim and should not be modified or freed.
318 * Returns: a pointer to the internal #AdgPair or %NULL on errors
320 const AdgPair *
321 adg_adim_get_org1(AdgADim *adim)
323 AdgADimPrivate *data;
325 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
327 data = adim->data;
329 return &data->org1;
333 * adg_adim_get_org2:
334 * @adim: an #AdgADim
336 * Gets the second origin of @adim. The returned pair is owned by
337 * @adim and should not be modified or freed.
339 * Returns: a pointer to the internal #AdgPair or %NULL on errors
341 const AdgPair *
342 adg_adim_get_org2(AdgADim *adim)
344 AdgADimPrivate *data;
346 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
348 data = adim->data;
350 return &data->org2;
354 * adg_adim_set_org:
355 * @adim: an #AdgADim
356 * @org1: the first origin
357 * @org2: the second origin
359 * Sets at once the two origins on @adim.
361 void
362 adg_adim_set_org(AdgADim *adim, const AdgPair *org1, const AdgPair *org2)
364 GObject *object;
366 g_return_if_fail(ADG_IS_ADIM(adim));
368 object = (GObject *) adim;
370 g_object_freeze_notify(object);
372 if (set_org1(adim, org1))
373 g_object_notify(object, "org1");
375 if (set_org2(adim, org1))
376 g_object_notify(object, "org2");
378 g_object_thaw_notify(object);
382 static void
383 local_changed(AdgEntity *entity)
385 unset_trail((AdgADim *) entity);
387 PARENT_ENTITY_CLASS->local_changed(entity);
390 static void
391 invalidate(AdgEntity *entity)
393 AdgADim *adim;
394 AdgADimPrivate *data;
396 adim = (AdgADim *) entity;
397 data = adim->data;
399 dispose_markers(adim);
400 data->geometry_arranged = FALSE;
401 unset_trail(adim);
403 if (PARENT_ENTITY_CLASS->invalidate != NULL)
404 PARENT_ENTITY_CLASS->invalidate(entity);
407 static void
408 arrange(AdgEntity *entity)
410 AdgADim *adim;
411 AdgDim *dim;
412 AdgADimPrivate *data;
413 AdgContainer *quote;
414 const AdgMatrix *local;
415 AdgPair ref1, ref2, base1, base12, base2;
416 AdgPair pair;
418 PARENT_ENTITY_CLASS->arrange(entity);
420 adim = (AdgADim *) entity;
421 dim = (AdgDim *) adim;
422 data = adim->data;
423 quote = adg_dim_get_quote(dim);
425 update_geometry(adim);
426 update_entities(adim);
428 if (data->cpml.path.status == CAIRO_STATUS_SUCCESS) {
429 AdgEntity *quote_entity = (AdgEntity *) quote;
430 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
431 adg_entity_set_local_map(quote_entity, &data->quote.local_map);
432 return;
435 local = adg_entity_local_matrix(entity);
437 /* Apply the local matrix to the relevant points */
438 cpml_pair_transform(cpml_pair_copy(&ref1, adg_dim_get_ref1(dim)), local);
439 cpml_pair_transform(cpml_pair_copy(&ref2, adg_dim_get_ref2(dim)), local);
440 cpml_pair_transform(cpml_pair_copy(&base1, &data->point.base1), local);
441 cpml_pair_transform(cpml_pair_copy(&base12, &data->point.base12), local);
442 cpml_pair_transform(cpml_pair_copy(&base2, &data->point.base2), local);
444 /* Combine points and global shifts to build the path */
445 cpml_pair_add(cpml_pair_copy(&pair, &ref1), &data->shift.from1);
446 cpml_pair_to_cairo(&pair, &data->cpml.data[6]);
448 cpml_pair_add(cpml_pair_copy(&pair, &base1), &data->shift.base1);
449 cpml_pair_to_cairo(&pair, &data->cpml.data[1]);
451 cpml_pair_add(&pair, &data->shift.to1);
452 cpml_pair_to_cairo(&pair, &data->cpml.data[8]);
454 cpml_pair_add(cpml_pair_copy(&pair, &base12), &data->shift.base12);
455 cpml_pair_to_cairo(&pair, &data->cpml.data[3]);
457 cpml_pair_add(cpml_pair_copy(&pair, &ref2), &data->shift.from2);
458 cpml_pair_to_cairo(&pair, &data->cpml.data[10]);
460 cpml_pair_add(cpml_pair_copy(&pair, &base2), &data->shift.base2);
461 cpml_pair_to_cairo(&pair, &data->cpml.data[4]);
463 cpml_pair_add(&pair, &data->shift.to2);
464 cpml_pair_to_cairo(&pair, &data->cpml.data[12]);
466 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
468 if (quote != NULL) {
469 /* Update global and local map of the quote container */
470 AdgEntity *quote_entity;
471 gdouble angle;
472 AdgMatrix matrix;
474 quote_entity = (AdgEntity *) quote;
475 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
476 adg_matrix_copy(&matrix, local);
477 cairo_matrix_invert(&matrix);
479 cpml_pair_from_cairo(&pair, &data->cpml.data[3]);
480 cairo_matrix_transform_point(&matrix, &pair.x, &pair.y);
481 cairo_matrix_init_translate(&matrix, pair.x, pair.y);
482 adg_entity_set_local_map(quote_entity, &matrix);
484 cairo_matrix_init_rotate(&matrix, angle);
485 adg_entity_transform_global_map(quote_entity, &matrix, ADG_TRANSFORM_BEFORE);
487 adg_entity_get_global_map(quote_entity, &data->quote.global_map);
488 adg_entity_get_local_map(quote_entity, &data->quote.local_map);
491 /* Signal to the markers (if any) that the path has changed */
492 if (data->marker1 != NULL) {
493 adg_marker_set_segment(data->marker1, data->trail, 1);
494 adg_entity_local_changed((AdgEntity *) data->marker1);
497 if (data->marker2 != NULL) {
498 adg_marker_set_segment(data->marker2, data->trail, 1);
499 adg_entity_local_changed((AdgEntity *) data->marker2);
502 /* TODO: compute the extents */
505 static void
506 render(AdgEntity *entity, cairo_t *cr)
508 AdgADim *adim;
509 AdgDim *dim;
510 AdgADimPrivate *data;
511 AdgDimStyle *dim_style;
512 AdgDress dress;
513 const cairo_path_t *cairo_path;
515 adim = (AdgADim *) entity;
516 dim = (AdgDim *) entity;
517 data = adim->data;
518 dim_style = GET_DIM_STYLE(dim);
520 adg_style_apply((AdgStyle *) dim_style, entity, cr);
522 if (data->marker1 != NULL)
523 adg_entity_render((AdgEntity *) data->marker1, cr);
525 if (data->marker2 != NULL)
526 adg_entity_render((AdgEntity *) data->marker2, cr);
528 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
530 dress = adg_dim_style_get_line_dress(dim_style);
531 adg_entity_apply_dress(entity, dress, cr);
533 cairo_path = adg_trail_get_cairo_path(data->trail);
534 cairo_append_path(cr, cairo_path);
535 cairo_stroke(cr);
538 static gchar *
539 default_value(AdgDim *dim)
541 AdgADim *adim;
542 AdgADimPrivate *data;
543 AdgDimStyle *dim_style;
544 gdouble angle;
545 const gchar *format;
547 adim = (AdgADim *) dim;
548 data = adim->data;
549 dim_style = GET_DIM_STYLE(dim);
550 format = adg_dim_style_get_number_format(dim_style);
552 update_geometry(adim);
553 angle = (data->angle2 - data->angle1) * 180 / M_PI;
555 return g_strdup_printf(format, angle);
558 /* With "geometry" is considered any data (point, vector or angle)
559 * that can be cached: this is strictly related on how the arrange()
560 * method works */
561 static void
562 update_geometry(AdgADim *adim)
564 AdgADimPrivate *data;
565 AdgDimStyle *dim_style;
566 gdouble from_offset, to_offset;
567 gdouble spacing, level;
568 CpmlVector vector[3];
569 CpmlPair center;
570 gdouble distance;
572 data = adim->data;
574 if (data->geometry_arranged)
575 return;
577 if (!get_info(adim, vector, &center, &distance)) {
578 /* Parallel lines: hang with an error message */
579 g_warning("%s: trying to set an angular dimension on parallel lines",
580 G_STRLOC);
581 return;
584 dim_style = GET_DIM_STYLE(adim);
585 from_offset = adg_dim_style_get_from_offset(dim_style);
586 to_offset = adg_dim_style_get_to_offset(dim_style);
587 spacing = adg_dim_style_get_baseline_spacing(dim_style);
588 level = adg_dim_get_level((AdgDim *) adim);
590 /* shift.from1 */
591 cpml_vector_set_length(&vector[0], from_offset);
592 cpml_pair_copy(&data->shift.from1, &vector[0]);
594 /* shift.base1 */
595 cpml_vector_set_length(&vector[0], level * spacing);
596 cpml_pair_copy(&data->shift.base1, &vector[0]);
598 /* shift.to1 */
599 cpml_vector_set_length(&vector[0], to_offset);
600 cpml_pair_copy(&data->shift.to1, &vector[0]);
602 /* shift.from2 */
603 cpml_vector_set_length(&vector[2], from_offset);
604 cpml_pair_copy(&data->shift.from2, &vector[2]);
606 /* shift.base2 */
607 cpml_vector_set_length(&vector[2], level * spacing);
608 cpml_pair_copy(&data->shift.base2, &vector[2]);
610 /* shift.to2 */
611 cpml_vector_set_length(&vector[2], to_offset);
612 cpml_pair_copy(&data->shift.to2, &vector[2]);
614 /* shift.base12 */
615 cpml_vector_set_length(&vector[1], level * spacing);
616 cpml_pair_copy(&data->shift.base12, &vector[1]);
618 /* Distance can be 0, so the following will leave the
619 * vector array in undefined state */
621 /* point.base1 */
622 cpml_vector_set_length(&vector[0], distance);
623 cpml_pair_add(cpml_pair_copy(&data->point.base1, &vector[0]), &center);
625 /* point.base2 */
626 cpml_vector_set_length(&vector[2], distance);
627 cpml_pair_add(cpml_pair_copy(&data->point.base2, &vector[2]), &center);
629 /* point.base12 */
630 cpml_vector_set_length(&vector[1], distance);
631 cpml_pair_add(cpml_pair_copy(&data->point.base12, &vector[1]), &center);
633 data->geometry_arranged = TRUE;
636 static void
637 update_entities(AdgADim *adim)
639 AdgADimPrivate *data;
640 AdgDimStyle *dim_style;
642 data = adim->data;
643 dim_style = GET_DIM_STYLE(adim);
645 if (data->trail == NULL)
646 data->trail = adg_trail_new(trail_callback, adim);
648 if (data->marker1 == NULL)
649 data->marker1 = adg_dim_style_marker1_new(dim_style);
651 if (data->marker2 == NULL)
652 data->marker2 = adg_dim_style_marker2_new(dim_style);
655 static gboolean
656 set_org1(AdgADim *adim, const AdgPair *org1)
658 AdgADimPrivate *data = adim->data;
660 if (adg_pair_equal(&data->org1, org1))
661 return FALSE;
663 data->org1 = *org1;
665 return TRUE;
668 static gboolean
669 set_org2(AdgADim *adim, const AdgPair *org2)
671 AdgADimPrivate *data = adim->data;
673 if (adg_pair_equal(&data->org2, org2))
674 return FALSE;
676 data->org2 = *org2;
678 return TRUE;
681 static void
682 unset_trail(AdgADim *adim)
684 AdgADimPrivate *data = adim->data;
686 if (data->trail != NULL)
687 adg_model_clear((AdgModel *) data->trail);
689 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
692 static void
693 dispose_markers(AdgADim *adim)
695 AdgADimPrivate *data = adim->data;
697 if (data->trail != NULL) {
698 g_object_unref(data->trail);
699 data->trail = NULL;
702 if (data->marker1 != NULL) {
703 g_object_unref(data->marker1);
704 data->marker1 = NULL;
707 if (data->marker2 != NULL) {
708 g_object_unref(data->marker2);
709 data->marker2 = NULL;
713 static gboolean
714 get_info(AdgADim *adim, CpmlVector vector[],
715 AdgPair *center, gdouble *distance)
717 AdgDim *dim;
718 AdgADimPrivate *data;
719 const AdgPair *ref1, *ref2;
720 gdouble factor;
722 dim = (AdgDim *) adim;
723 data = adim->data;
724 ref1 = adg_dim_get_ref1(dim);
725 ref2 = adg_dim_get_ref2(dim);
727 cpml_pair_sub(cpml_pair_copy(&vector[0], ref1), &data->org1);
728 cpml_pair_sub(cpml_pair_copy(&vector[2], ref2), &data->org2);
730 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
731 if (factor == 0)
732 return FALSE;
734 factor = ((ref1->y - ref2->y) * vector[2].x -
735 (ref1->x - ref2->x) * vector[2].y) / factor;
737 center->x = ref1->x + vector[0].x * factor;
738 center->y = ref1->y + vector[0].y * factor;
739 *distance = cpml_pair_distance(center, adg_dim_get_pos(dim));
740 data->angle1 = cpml_vector_angle(&vector[0]);
741 data->angle2 = cpml_vector_angle(&vector[2]);
742 while (data->angle2 < data->angle1)
743 data->angle2 += M_PI * 2;
745 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
747 return TRUE;
750 static CpmlPath *
751 trail_callback(AdgTrail *trail, gpointer user_data)
753 AdgADim *adim;
754 AdgADimPrivate *data;
756 adim = (AdgADim *) user_data;
757 data = adim->data;
759 return &data->cpml.path;