adg: use G_PRIVATE_ADD and friends
[adg.git] / src / adg / adg-adim.c
blob83a1384d39f7f0dfaf3122d3edcdc15b65f3ee9c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2019 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.
27 * Since: 1.0
30 /**
31 * AdgADim:
33 * All fields are privates and should not be used directly.
34 * Use its public methods instead.
36 * Since: 1.0
37 **/
40 #include "adg-internal.h"
41 #include "adg-container.h"
42 #include "adg-alignment.h"
43 #include "adg-model.h"
44 #include "adg-point.h"
45 #include "adg-trail.h"
46 #include "adg-marker.h"
47 #include "adg-style.h"
48 #include "adg-dim-style.h"
49 #include "adg-dress.h"
50 #include "adg-textual.h"
51 #include "adg-toy-text.h"
52 #include "adg-dim.h"
53 #include "adg-dim-private.h"
55 #include "adg-adim.h"
56 #include "adg-adim-private.h"
59 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_adim_parent_class)
60 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_adim_parent_class)
63 G_DEFINE_TYPE_WITH_PRIVATE(AdgADim, adg_adim, ADG_TYPE_DIM)
65 enum {
66 PROP_0,
67 PROP_ORG1,
68 PROP_ORG2,
69 PROP_HAS_EXTENSION1,
70 PROP_HAS_EXTENSION2
74 static void _adg_dispose (GObject *object);
75 static void _adg_get_property (GObject *object,
76 guint param_id,
77 GValue *value,
78 GParamSpec *pspec);
79 static void _adg_set_property (GObject *object,
80 guint param_id,
81 const GValue *value,
82 GParamSpec *pspec);
83 static void _adg_global_changed (AdgEntity *entity);
84 static void _adg_local_changed (AdgEntity *entity);
85 static void _adg_invalidate (AdgEntity *entity);
86 static void _adg_arrange (AdgEntity *entity);
87 static void _adg_render (AdgEntity *entity,
88 cairo_t *cr);
89 static gchar * _adg_default_value (AdgDim *dim);
90 static gboolean _adg_compute_geometry (AdgDim *dim);
91 static void _adg_update_entities (AdgADim *adim);
92 static void _adg_unset_trail (AdgADim *adim);
93 static void _adg_dispose_trail (AdgADim *adim);
94 static void _adg_dispose_markers (AdgADim *adim);
95 static gboolean _adg_get_info (AdgADim *adim,
96 CpmlVector vector[],
97 CpmlPair *center,
98 gdouble *distance);
99 static cairo_path_t * _adg_trail_callback (AdgTrail *trail,
100 gpointer user_data);
103 static void
104 adg_adim_class_init(AdgADimClass *klass)
106 GObjectClass *gobject_class;
107 AdgEntityClass *entity_class;
108 AdgDimClass *dim_class;
109 GParamSpec *param;
111 gobject_class = (GObjectClass *) klass;
112 entity_class = (AdgEntityClass *) klass;
113 dim_class = (AdgDimClass *) klass;
115 gobject_class->dispose = _adg_dispose;
116 gobject_class->get_property = _adg_get_property;
117 gobject_class->set_property = _adg_set_property;
119 entity_class->global_changed = _adg_global_changed;
120 entity_class->local_changed = _adg_local_changed;
121 entity_class->invalidate = _adg_invalidate;
122 entity_class->arrange = _adg_arrange;
123 entity_class->render = _adg_render;
125 dim_class->default_value = _adg_default_value;
126 dim_class->compute_geometry = _adg_compute_geometry;
128 param = g_param_spec_boxed("org1",
129 P_("First Origin"),
130 P_("Where the first line comes from: this point is used toghether with \"ref1\" to align the first extension line"),
131 ADG_TYPE_POINT,
132 G_PARAM_READWRITE);
133 g_object_class_install_property(gobject_class, PROP_ORG1, param);
135 param = g_param_spec_boxed("org2",
136 P_("Second Origin"),
137 P_("Where the second line comes from: this point is used toghether with \"ref2\" to align the second extension line"),
138 ADG_TYPE_POINT,
139 G_PARAM_READWRITE);
140 g_object_class_install_property(gobject_class, PROP_ORG2, param);
142 param = g_param_spec_boolean("has-extension1",
143 P_("Has First Extension Line flag"),
144 P_("Show (TRUE) or hide (FALSE) the first extension line"),
145 TRUE, G_PARAM_READWRITE);
146 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
148 param = g_param_spec_boolean("has-extension2",
149 P_("Has Second Extension Line flag"),
150 P_("Show (TRUE) or hide (FALSE) the second extension line"),
151 TRUE, G_PARAM_READWRITE);
152 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
155 static void
156 adg_adim_init(AdgADim *adim)
158 AdgADimPrivate *data;
159 AdgStyle *style;
160 AdgDimStyle *dim_style;
161 cairo_path_data_t move_to, line_to, arc_to;
163 data = adg_adim_get_instance_private(adim);
164 move_to.header.type = CPML_MOVE;
165 move_to.header.length = 2;
166 line_to.header.type = CPML_LINE;
167 line_to.header.length = 2;
168 arc_to.header.type = CPML_ARC;
169 arc_to.header.length = 3;
171 data->org1 = NULL;
172 data->org2 = NULL;
173 data->has_extension1 = TRUE;
174 data->has_extension2 = TRUE;
176 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
177 data->cairo.path.data = data->cairo.data;
178 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
179 data->cairo.path.data[0] = move_to;
180 data->cairo.path.data[2] = arc_to;
181 data->cairo.path.data[5] = move_to;
182 data->cairo.path.data[7] = line_to;
183 data->cairo.path.data[9] = move_to;
184 data->cairo.path.data[11] = line_to;
186 data->trail = NULL;
187 data->marker1 = NULL;
188 data->marker2 = NULL;
190 /* Override the default style of the dimension dress to express angles in
191 * sexagesimal units */
192 style = adg_dress_get_fallback(ADG_DRESS_DIMENSION);
193 dim_style = (AdgDimStyle *) adg_style_clone(style);
195 adg_dim_style_set_decimals(dim_style, 0);
196 adg_dim_style_set_rounding(dim_style, 3);
197 adg_dim_style_set_number_arguments(dim_style, "Dm");
198 adg_dim_style_set_number_format(dim_style, "%g°(%g')");
200 adg_entity_set_style((AdgEntity *) adim,
201 ADG_DRESS_DIMENSION,
202 (AdgStyle *) dim_style);
205 static void
206 _adg_dispose(GObject *object)
208 AdgEntity *entity;
209 AdgADim *adim;
210 AdgADimPrivate *data;
212 entity = (AdgEntity *) object;
213 adim = (AdgADim *) entity;
214 data = adg_adim_get_instance_private(adim);
216 _adg_dispose_trail(adim);
217 _adg_dispose_markers(adim);
219 if (data->org1 != NULL)
220 data->org1 = adg_entity_point(entity, data->org1, NULL);
222 if (data->org2 != NULL)
223 data->org2 = adg_entity_point(entity, data->org2, NULL);
225 if (_ADG_OLD_OBJECT_CLASS->dispose)
226 _ADG_OLD_OBJECT_CLASS->dispose(object);
229 static void
230 _adg_get_property(GObject *object, guint prop_id,
231 GValue *value, GParamSpec *pspec)
233 AdgADimPrivate *data = adg_adim_get_instance_private((AdgADim *) object);
235 switch (prop_id) {
236 case PROP_ORG1:
237 g_value_set_boxed(value, data->org1);
238 break;
239 case PROP_ORG2:
240 g_value_set_boxed(value, data->org2);
241 break;
242 case PROP_HAS_EXTENSION1:
243 g_value_set_boolean(value, data->has_extension1);
244 break;
245 case PROP_HAS_EXTENSION2:
246 g_value_set_boolean(value, data->has_extension2);
247 break;
248 default:
249 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
250 break;
254 static void
255 _adg_set_property(GObject *object, guint prop_id,
256 const GValue *value, GParamSpec *pspec)
258 AdgEntity *entity = (AdgEntity *) object;
259 AdgADimPrivate *data = adg_adim_get_instance_private((AdgADim *) object);
261 switch (prop_id) {
262 case PROP_ORG1:
263 data->org1 = adg_entity_point(entity, data->org1,
264 g_value_get_boxed(value));
265 break;
266 case PROP_ORG2:
267 data->org2 = adg_entity_point(entity, data->org2,
268 g_value_get_boxed(value));
269 break;
270 case PROP_HAS_EXTENSION1:
271 data->has_extension1 = g_value_get_boolean(value);
272 break;
273 case PROP_HAS_EXTENSION2:
274 data->has_extension2 = g_value_get_boolean(value);
275 break;
276 default:
277 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
278 break;
284 * adg_adim_new:
286 * Creates a new - undefined - angular dimension. You must, at least,
287 * define the first line by setting #AdgADim:org1 (start point) and
288 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
289 * (start point) and #AdgDim:ref2 (end point) and the position of
290 * the quote in #AdgDim:pos.
292 * Returns: the newly created angular dimension entity
294 * Since: 1.0
296 AdgADim *
297 adg_adim_new(void)
299 return g_object_new(ADG_TYPE_ADIM, NULL);
303 * adg_adim_new_full:
304 * @ref1: (allow-none): first reference point
305 * @ref2: (allow-none): second reference point
306 * @org1: (allow-none): first origin point
307 * @org2: (allow-none): second origin point
308 * @pos: (allow-none): the position point
310 * Creates a new angular dimension, specifing all the needed
311 * properties in one shot using #CpmlPair.
313 * Returns: the newly created angular dimension entity
315 * Since: 1.0
317 AdgADim *
318 adg_adim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
319 const CpmlPair *org1, const CpmlPair *org2,
320 const CpmlPair *pos)
322 AdgADim *adim;
323 AdgDim *dim;
325 adim = adg_adim_new();
326 dim = (AdgDim *) adim;
328 if (ref1 != NULL)
329 adg_dim_set_ref1_from_pair(dim, ref1);
331 if (ref2 != NULL)
332 adg_dim_set_ref2_from_pair(dim, ref2);
334 if (pos != NULL)
335 adg_dim_set_pos_from_pair(dim, pos);
337 if (org1 != NULL)
338 adg_adim_set_org1_from_pair(adim, org1);
340 if (org2 != NULL)
341 adg_adim_set_org2_from_pair(adim, org2);
343 return adim;
347 * adg_adim_new_full_explicit:
348 * @ref1_x: the x coordinate of end point of the first line
349 * @ref1_y: the y coordinate of end point of the first line
350 * @ref2_x: the x coordinate of end point of the second line
351 * @ref2_y: the y coordinate of end point of the second line
352 * @org1_x: the x coordinate of start point of the first line
353 * @org1_y: the y coordinate of start point of the first line
354 * @org2_x: the x coordinate of start point of the second line
355 * @org2_y: the y coordinate of start point of the second line
356 * @pos_x: the x coordinate of the position reference
357 * @pos_y: the y coordinate of the position reference
359 * Wrappes adg_adim_new_full() with explicit values.
361 * Returns: the newly created linear dimension entity
363 * Since: 1.0
365 AdgADim *
366 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
367 gdouble ref2_x, gdouble ref2_y,
368 gdouble org1_x, gdouble org1_y,
369 gdouble org2_x, gdouble org2_y,
370 gdouble pos_x, gdouble pos_y)
372 CpmlPair ref1, ref2, org1, org2, pos;
374 ref1.x = ref1_x;
375 ref1.y = ref1_y;
376 ref2.x = ref2_x;
377 ref2.y = ref2_y;
378 org1.x = org1_x;
379 org1.y = org1_y;
380 org2.x = org2_x;
381 org2.y = org2_y;
382 pos.x = pos_x;
383 pos.y = pos_y;
385 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
389 * adg_adim_new_full_from_model:
390 * @model: (transfer none): the model from which the named pairs are taken
391 * @ref1: (allow-none): the end point of the first line
392 * @ref2: (allow-none): the end point of the second line
393 * @org1: (allow-none): the origin of the first line
394 * @org2: (allow-none): the origin of the second line
395 * @pos: (allow-none): the position reference
397 * Creates a new angular dimension, specifing all the needed properties
398 * in one shot and using named pairs from @model.
400 * Returns: the newly created angular dimension entity
402 * Since: 1.0
404 AdgADim *
405 adg_adim_new_full_from_model(AdgModel *model,
406 const gchar *ref1, const gchar *ref2,
407 const gchar *org1, const gchar *org2,
408 const gchar *pos)
410 AdgADim *adim;
411 AdgDim *dim;
413 adim = adg_adim_new();
414 dim = (AdgDim *) adim;
416 if (ref1 != NULL)
417 adg_dim_set_ref1_from_model(dim, model, ref1);
419 if (ref2 != NULL)
420 adg_dim_set_ref2_from_model(dim, model, ref2);
422 if (pos != NULL)
423 adg_dim_set_pos_from_model(dim, model, pos);
425 if (org1 != NULL)
426 adg_adim_set_org1_from_model(adim, model, org1);
428 if (org2 != NULL)
429 adg_adim_set_org2_from_model(adim, model, org2);
431 return adim;
435 * adg_adim_set_org1:
436 * @adim: an #AdgADim
437 * @org1: the new point to use as first reference
439 * Sets the #AdgADim:org1 property to @org1. The old point
440 * is silently discarded, unreferencing its model if that
441 * point was bound to a named pair (hence, possibly destroying
442 * the model if this was the last reference).
444 * @org1 can be <constant>NULL</constant>, in which case
445 * the point is destroyed.
447 * Since: 1.0
449 void
450 adg_adim_set_org1(AdgADim *adim, const AdgPoint *org1)
452 g_return_if_fail(ADG_IS_ADIM(adim));
453 g_object_set(adim, "org1", org1, NULL);
457 * adg_adim_set_org1_explicit:
458 * @adim: an #AdgADim
459 * @x: x coordinate of the first reference point
460 * @y: y coordinate of the first reference point
462 * Sets the #AdgADim:org1 property to the (@x, @y) explicit
463 * coordinates. The old point is silently discarded,
464 * unreferencing its model if that point was bound to a named
465 * pair (hence, possibly destroying the model if this was the
466 * last reference).
468 * Since: 1.0
470 void
471 adg_adim_set_org1_explicit(AdgADim *adim, gdouble x, gdouble y)
473 AdgPoint *point = adg_point_new();
475 adg_point_set_pair_explicit(point, x, y);
476 adg_adim_set_org1(adim, point);
478 adg_point_destroy(point);
482 * adg_adim_set_org1_from_pair:
483 * @adim: an #AdgADim
484 * @org1: the coordinates pair of the first reference point
486 * Convenient function to set the #AdgADim:org1 property using a
487 * pair instead of explicit coordinates.
489 * Since: 1.0
491 void
492 adg_adim_set_org1_from_pair(AdgADim *adim, const CpmlPair *org1)
494 g_return_if_fail(org1 != NULL);
496 adg_adim_set_org1_explicit(adim, org1->x, org1->y);
500 * adg_adim_set_org1_from_model:
501 * @adim: an #AdgADim
502 * @model: the source #AdgModel
503 * @org1: a named pair in @model
505 * Binds #AdgADim:org1 to the @org1 named pair of @model. If @model
506 * is <constant>NULL</constant>, the point will be unset. In any
507 * case, the old point is silently discarded, unreferencing its
508 * model if that point was bound to a named pair (hence, possibly
509 * destroying the model if this was the last reference).
511 * The assignment is lazy so @org1 could be not be present in @model.
512 * Anyway, at the first access to this point an error will be raised
513 * if the named pair is still missing.
515 * Since: 1.0
517 void
518 adg_adim_set_org1_from_model(AdgADim *adim, AdgModel *model, const gchar *org1)
520 AdgPoint *point = adg_point_new();
522 adg_point_set_pair_from_model(point, model, org1);
523 adg_adim_set_org1(adim, point);
525 adg_point_destroy(point);
529 * adg_adim_get_org1:
530 * @adim: an #AdgADim
532 * Gets the #AdgADim:org1 point. The returned point is internally owned
533 * and must not be freed or modified.
535 * Returns: (transfer none): the first reference point
537 * Since: 1.0
539 AdgPoint *
540 adg_adim_get_org1(AdgADim *adim)
542 AdgADimPrivate *data;
544 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
546 data = adg_adim_get_instance_private(adim);
548 return data->org1;
552 * adg_adim_set_org2:
553 * @adim: an #AdgADim
554 * @org2: the new point to use as first reference
556 * Sets the #AdgADim:org2 property to @org2. The old point
557 * is silently discarded, unreferencing its model if that
558 * point was bound to a named pair (hence, possibly destroying
559 * the model if this was the last reference).
561 * @org2 can be <constant>NULL</constant>, in which case
562 * the point is destroyed.
564 * Since: 1.0
566 void
567 adg_adim_set_org2(AdgADim *adim, const AdgPoint *org2)
569 g_return_if_fail(ADG_IS_ADIM(adim));
570 g_object_set(adim, "org2", org2, NULL);
574 * adg_adim_set_org2_explicit:
575 * @adim: an #AdgADim
576 * @x: x coordinate of the first reference point
577 * @y: y coordinate of the first reference point
579 * Sets the #AdgADim:org2 property to the (@x, @y) explicit
580 * coordinates. The old point is silently discarded,
581 * unreferencing its model if that point was bound to a named
582 * pair (hence, possibly destroying the model if this was the
583 * last reference).
585 * Since: 1.0
587 void
588 adg_adim_set_org2_explicit(AdgADim *adim, gdouble x, gdouble y)
590 AdgPoint *point = adg_point_new();
592 adg_point_set_pair_explicit(point, x, y);
593 adg_adim_set_org2(adim, point);
595 adg_point_destroy(point);
599 * adg_adim_set_org2_from_pair:
600 * @adim: an #AdgADim
601 * @org2: the coordinates pair of the first reference point
603 * Convenient function to set the #AdgADim:org2 property using a
604 * pair instead of explicit coordinates.
606 * Since: 1.0
608 void
609 adg_adim_set_org2_from_pair(AdgADim *adim, const CpmlPair *org2)
611 g_return_if_fail(org2 != NULL);
613 adg_adim_set_org2_explicit(adim, org2->x, org2->y);
617 * adg_adim_set_org2_from_model:
618 * @adim: an #AdgADim
619 * @model: the source #AdgModel
620 * @org2: a named pair in @model
622 * Binds #AdgADim:org2 to the @org2 named pair of @model. If @model
623 * is <constant>NULL</constant>, the point will be unset. In any
624 * case, the old point is silently discarded, unreferencing its
625 * model if that point was bound to a named pair (hence, possibly
626 * destroying the model if this was the last reference).
628 * The assignment is lazy so @org2 could be not be present in @model.
629 * Anyway, at the first access to this point an error will be raised
630 * if the named pair is still missing.
632 * Since: 1.0
634 void
635 adg_adim_set_org2_from_model(AdgADim *adim, AdgModel *model, const gchar *org2)
637 AdgPoint *point = adg_point_new();
639 adg_point_set_pair_from_model(point, model, org2);
640 adg_adim_set_org2(adim, point);
642 adg_point_destroy(point);
646 * adg_adim_get_org2:
647 * @adim: an #AdgADim
649 * Gets the #AdgADim:org2 point. The returned point is internally owned
650 * and must not be freed or modified.
652 * Returns: (transfer none): the second reference point
654 * Since: 1.0
656 AdgPoint *
657 adg_adim_get_org2(AdgADim *adim)
659 AdgADimPrivate *data;
661 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
663 data = adg_adim_get_instance_private(adim);
665 return data->org2;
669 * adg_adim_switch_extension1:
670 * @adim: an #AdgADim entity
671 * @new_state: the new state
673 * Shows (if @new_state is <constant>TRUE</constant>) or hides
674 * (if @new_state is <constant>FALSE</constant>) the first
675 * extension line of @adim.
677 * Since: 1.0
679 void
680 adg_adim_switch_extension1(AdgADim *adim, gboolean new_state)
682 g_return_if_fail(ADG_IS_ADIM(adim));
683 g_return_if_fail(adg_is_boolean_value(new_state));
684 g_object_set(adim, "has-extension1", new_state, NULL);
688 * adg_adim_has_extension1:
689 * @adim: an #AdgADim entity
691 * Checks if @adim should render the first extension line.
693 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
695 * Since: 1.0
697 gboolean
698 adg_adim_has_extension1(AdgADim *adim)
700 AdgADimPrivate *data;
702 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
704 data = adg_adim_get_instance_private(adim);
706 return data->has_extension1;
710 * adg_adim_switch_extension2:
711 * @adim: an #AdgADim entity
712 * @new_state: the new new_state
714 * Shows (if @new_state is <constant>TRUE</constant>) or hides
715 * (if @new_state is <constant>FALSE</constant>) the second
716 * extension line of @adim.
718 * Since: 1.0
720 void
721 adg_adim_switch_extension2(AdgADim *adim, gboolean new_state)
723 g_return_if_fail(ADG_IS_ADIM(adim));
724 g_return_if_fail(adg_is_boolean_value(new_state));
725 g_object_set(adim, "has-extension2", new_state, NULL);
729 * adg_adim_has_extension2:
730 * @adim: an #AdgADim entity
732 * Checks if @adim should render the second extension line.
734 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
736 * Since: 1.0
738 gboolean
739 adg_adim_has_extension2(AdgADim *adim)
741 AdgADimPrivate *data;
743 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
745 data = adg_adim_get_instance_private(adim);
747 return data->has_extension2;
751 static void
752 _adg_global_changed(AdgEntity *entity)
754 AdgADimPrivate *data = adg_adim_get_instance_private((AdgADim *) entity);
756 _adg_unset_trail((AdgADim *) entity);
758 if (_ADG_OLD_ENTITY_CLASS->global_changed)
759 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
761 if (data->marker1 != NULL)
762 adg_entity_global_changed((AdgEntity *) data->marker1);
764 if (data->marker2 != NULL)
765 adg_entity_global_changed((AdgEntity *) data->marker2);
768 static void
769 _adg_local_changed(AdgEntity *entity)
771 _adg_unset_trail((AdgADim *) entity);
773 if (_ADG_OLD_ENTITY_CLASS->local_changed)
774 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
777 static void
778 _adg_invalidate(AdgEntity *entity)
780 AdgADim *adim = (AdgADim *) entity;
781 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
783 _adg_dispose_trail(adim);
784 _adg_dispose_markers(adim);
785 _adg_unset_trail(adim);
787 if (data->org1)
788 adg_point_invalidate(data->org1);
789 if (data->org2)
790 adg_point_invalidate(data->org2);
792 if (_ADG_OLD_ENTITY_CLASS->invalidate)
793 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
796 static void
797 _adg_arrange(AdgEntity *entity)
799 AdgDim *dim;
800 AdgADim *adim;
801 AdgADimPrivate *data;
802 AdgAlignment *quote;
803 const cairo_matrix_t *global, *local;
804 CpmlPair ref1, ref2, base1, base12, base2;
805 CpmlPair pair;
806 CpmlExtents extents;
807 AdgEntity *marker_entity;
809 if (_ADG_OLD_ENTITY_CLASS->arrange)
810 _ADG_OLD_ENTITY_CLASS->arrange(entity);
812 dim = (AdgDim *) entity;
813 if (! adg_dim_compute_geometry(dim))
814 return;
816 adim = (AdgADim *) entity;
817 data = adg_adim_get_instance_private(adim);
818 quote = adg_dim_get_quote(dim);
820 _adg_update_entities(adim);
822 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
823 AdgEntity *quote_entity = (AdgEntity *) quote;
824 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
825 return;
828 global = adg_entity_get_global_matrix(entity);
829 local = adg_entity_get_local_matrix(entity);
830 extents.is_defined = FALSE;
832 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
833 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
834 cpml_pair_copy(&base1, &data->point.base1);
835 cpml_pair_copy(&base12, &data->point.base12);
836 cpml_pair_copy(&base2, &data->point.base2);
838 /* Apply the local matrix to the relevant points */
839 cpml_pair_transform(&ref1, local);
840 cpml_pair_transform(&ref2, local);
841 cpml_pair_transform(&base1, local);
842 cpml_pair_transform(&base12, local);
843 cpml_pair_transform(&base2, local);
845 /* Combine points and global shifts to build the path */
846 pair.x = ref1.x + data->shift.from1.x;
847 pair.y = ref1.y + data->shift.from1.y;
848 cpml_pair_to_cairo(&pair, &data->cairo.data[6]);
850 pair.x = base1.x + data->shift.base1.x;
851 pair.y = base1.y + data->shift.base1.y;
852 cpml_pair_to_cairo(&pair, &data->cairo.data[1]);
854 pair.x += data->shift.to1.x;
855 pair.y += data->shift.to1.y;
856 cpml_pair_to_cairo(&pair, &data->cairo.data[8]);
858 pair.x = base12.x + data->shift.base12.x;
859 pair.y = base12.y + data->shift.base12.y;
860 cpml_pair_to_cairo(&pair, &data->cairo.data[3]);
862 pair.x = ref2.x + data->shift.from2.x;
863 pair.y = ref2.y + data->shift.from2.y;
864 cpml_pair_to_cairo(&pair, &data->cairo.data[10]);
866 pair.x = base2.x + data->shift.base2.x;
867 pair.y = base2.y + data->shift.base2.y;
868 cpml_pair_to_cairo(&pair, &data->cairo.data[4]);
870 pair.x += data->shift.to2.x;
871 pair.y += data->shift.to2.y;
872 cpml_pair_to_cairo(&pair, &data->cairo.data[12]);
874 /* Play with header lengths to show or hide the extension lines */
875 if (data->has_extension1) {
876 data->cairo.data[7].header.length = data->has_extension2 ? 2 : 6;
877 } else {
878 data->cairo.data[2].header.length = data->has_extension2 ? 7 : 11;
881 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
883 /* Arrange the quote */
884 if (quote != NULL) {
885 AdgEntity *quote_entity;
886 gdouble angle;
887 cairo_matrix_t map;
889 quote_entity = (AdgEntity *) quote;
890 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
891 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
893 adg_alignment_set_factor_explicit(quote, 0.5, 0);
895 cairo_matrix_init_translate(&map, pair.x, pair.y);
896 cairo_matrix_rotate(&map, angle);
897 adg_entity_set_global_map(quote_entity, &map);
898 adg_entity_arrange(quote_entity);
899 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
901 adg_matrix_copy(&data->quote.global_map, &map);
904 /* Arrange the trail */
905 if (data->trail != NULL) {
906 CpmlExtents trail_extents;
907 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
908 cpml_extents_transform(&trail_extents, global);
909 cpml_extents_add(&extents, &trail_extents);
910 } else {
911 _adg_dispose_markers(adim);
914 /* Arrange the markers */
915 if (data->marker1 != NULL) {
916 marker_entity = (AdgEntity *) data->marker1;
917 adg_marker_set_segment(data->marker1, data->trail, 1);
918 adg_entity_local_changed(marker_entity);
919 adg_entity_arrange(marker_entity);
920 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
922 if (data->marker2 != NULL) {
923 marker_entity = (AdgEntity *) data->marker2;
924 adg_marker_set_segment(data->marker2, data->trail, 1);
925 adg_entity_local_changed(marker_entity);
926 adg_entity_arrange(marker_entity);
927 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
930 adg_entity_set_extents(entity, &extents);
933 static void
934 _adg_render(AdgEntity *entity, cairo_t *cr)
936 AdgDim *dim;
937 AdgADim *adim;
938 AdgADimPrivate *data;
939 AdgDimStyle *dim_style;
940 AdgDress dress;
941 const cairo_path_t *cairo_path;
943 dim = (AdgDim *) entity;
944 if (! adg_dim_compute_geometry(dim)) {
945 /* Entity not arranged, probably due to undefined pair found */
946 return;
949 adim = (AdgADim *) entity;
950 data = adg_adim_get_instance_private(adim);
951 dim_style = adg_dim_get_dim_style(dim);
953 adg_style_apply((AdgStyle *) dim_style, entity, cr);
954 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
956 if (data->marker1 != NULL)
957 adg_entity_render((AdgEntity *) data->marker1, cr);
958 if (data->marker2 != NULL)
959 adg_entity_render((AdgEntity *) data->marker2, cr);
961 cairo_transform(cr, adg_entity_get_global_matrix(entity));
962 dress = adg_dim_style_get_line_dress(dim_style);
963 adg_entity_apply_dress(entity, dress, cr);
965 cairo_path = adg_trail_get_cairo_path(data->trail);
966 cairo_append_path(cr, cairo_path);
967 cairo_stroke(cr);
970 static gchar *
971 _adg_default_value(AdgDim *dim)
973 AdgADim *adim;
974 AdgADimPrivate *data;
975 gdouble angle;
977 if (! adg_dim_compute_geometry(dim)) {
978 return g_strdup("undef");
981 adim = (AdgADim *) dim;
982 data = adg_adim_get_instance_private(adim);
983 angle = (data->angle2 - data->angle1) * 180 / G_PI;
985 return adg_dim_get_text(dim, angle);
988 /* With "geometry" is considered any data (point, vector or angle)
989 * that can be cached: this is strictly related on how the arrange()
990 * method works */
991 static gboolean
992 _adg_compute_geometry(AdgDim *dim)
994 AdgADim *adim;
995 AdgADimPrivate *data;
996 AdgDimStyle *dim_style;
997 gdouble from_offset, to_offset;
998 gdouble spacing, level;
999 CpmlVector vector[3];
1000 CpmlPair center;
1001 gdouble distance;
1003 adim = (AdgADim *) dim;
1004 if (! _adg_get_info(adim, vector, &center, &distance))
1005 return FALSE;
1007 data = adg_adim_get_instance_private(adim);
1008 dim_style = adg_dim_get_dim_style(dim);
1009 from_offset = adg_dim_style_get_from_offset(dim_style);
1010 to_offset = adg_dim_style_get_to_offset(dim_style);
1011 spacing = adg_dim_style_get_baseline_spacing(dim_style);
1012 level = adg_dim_get_level((AdgDim *) adim);
1014 /* shift.from1 */
1015 cpml_vector_set_length(&vector[0], from_offset);
1016 cpml_pair_copy(&data->shift.from1, &vector[0]);
1018 /* shift.base1 */
1019 cpml_vector_set_length(&vector[0], level * spacing);
1020 cpml_pair_copy(&data->shift.base1, &vector[0]);
1022 /* shift.to1 */
1023 cpml_vector_set_length(&vector[0], to_offset);
1024 cpml_pair_copy(&data->shift.to1, &vector[0]);
1026 /* shift.from2 */
1027 cpml_vector_set_length(&vector[2], from_offset);
1028 cpml_pair_copy(&data->shift.from2, &vector[2]);
1030 /* shift.base2 */
1031 cpml_vector_set_length(&vector[2], level * spacing);
1032 cpml_pair_copy(&data->shift.base2, &vector[2]);
1034 /* shift.to2 */
1035 cpml_vector_set_length(&vector[2], to_offset);
1036 cpml_pair_copy(&data->shift.to2, &vector[2]);
1038 /* shift.base12 */
1039 cpml_vector_set_length(&vector[1], level * spacing);
1040 cpml_pair_copy(&data->shift.base12, &vector[1]);
1042 /* Distance can be 0, so the following will leave the
1043 * vector array in undefined state */
1045 /* point.base1 */
1046 cpml_vector_set_length(&vector[0], distance);
1047 data->point.base1.x = vector[0].x + center.x;
1048 data->point.base1.y = vector[0].y + center.y;
1050 /* point.base2 */
1051 cpml_vector_set_length(&vector[2], distance);
1052 data->point.base2.x = vector[2].x + center.x;
1053 data->point.base2.y = vector[2].y + center.y;
1055 /* point.base12 */
1056 cpml_vector_set_length(&vector[1], distance);
1057 data->point.base12.x = vector[1].x + center.x;
1058 data->point.base12.y = vector[1].y + center.y;
1060 return TRUE;
1063 static void
1064 _adg_update_entities(AdgADim *adim)
1066 AdgEntity *entity = (AdgEntity *) adim;
1067 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
1068 AdgDimStyle *dim_style = adg_dim_get_dim_style((AdgDim *) adim);
1070 if (data->trail == NULL)
1071 data->trail = adg_trail_new(_adg_trail_callback, adim);
1073 if (data->marker1 == NULL) {
1074 data->marker1 = adg_dim_style_marker1_new(dim_style);
1075 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
1078 if (data->marker2 == NULL) {
1079 data->marker2 = adg_dim_style_marker2_new(dim_style);
1080 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
1084 static void
1085 _adg_unset_trail(AdgADim *adim)
1087 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
1089 if (data->trail != NULL)
1090 adg_model_clear((AdgModel *) data->trail);
1092 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1095 static void
1096 _adg_dispose_trail(AdgADim *adim)
1098 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
1100 if (data->trail != NULL) {
1101 g_object_unref(data->trail);
1102 data->trail = NULL;
1106 static void
1107 _adg_dispose_markers(AdgADim *adim)
1109 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
1111 if (data->marker1 != NULL) {
1112 g_object_unref(data->marker1);
1113 data->marker1 = NULL;
1116 if (data->marker2 != NULL) {
1117 g_object_unref(data->marker2);
1118 data->marker2 = NULL;
1122 static gboolean
1123 _adg_get_info(AdgADim *adim, CpmlVector vector[],
1124 CpmlPair *center, gdouble *distance)
1126 AdgDim *dim;
1127 AdgADimPrivate *data;
1128 AdgPoint *ref1_point, *ref2_point, *pos_point;
1129 const CpmlPair *ref1, *ref2, *pos;
1130 const CpmlPair *org1, *org2;
1131 gdouble factor;
1133 dim = (AdgDim *) adim;
1134 data = adg_adim_get_instance_private(adim);
1135 ref1_point = adg_dim_get_ref1(dim);
1136 ref2_point = adg_dim_get_ref2(dim);
1137 pos_point = adg_dim_get_pos(dim);
1139 if (! adg_point_update(ref1_point)) {
1140 adg_dim_geometry_missing(dim, "ref1");
1141 return FALSE;
1144 if (! adg_point_update(ref2_point)) {
1145 adg_dim_geometry_missing(dim, "ref2");
1146 return FALSE;
1149 if (! adg_point_update(pos_point)) {
1150 adg_dim_geometry_missing(dim, "pos");
1151 return FALSE;
1154 if (! adg_point_update(data->org1)) {
1155 adg_dim_geometry_missing(dim, "org1");
1156 return FALSE;
1159 if (! adg_point_update(data->org2)) {
1160 adg_dim_geometry_missing(dim, "org2");
1161 return FALSE;
1164 ref1 = (CpmlPair *) ref1_point;
1165 ref2 = (CpmlPair *) ref2_point;
1166 pos = (CpmlPair *) pos_point;
1167 org1 = (CpmlPair *) data->org1;
1168 org2 = (CpmlPair *) data->org2;
1170 /* Check if the given points have valid coordinates */
1171 if (cpml_pair_equal(ref1, org1)) {
1172 adg_dim_geometry_coincident(dim, "ref1", "org1", ref1);
1173 return FALSE;
1176 if (cpml_pair_equal(ref2, org2)) {
1177 adg_dim_geometry_coincident(dim, "ref2", "org2", ref2);
1178 return FALSE;
1181 vector[0].x = ref1->x - org1->x;
1182 vector[0].y = ref1->y - org1->y;
1183 vector[2].x = ref2->x - org2->x;
1184 vector[2].y = ref2->y - org2->y;
1186 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
1187 if (factor == 0) {
1188 adg_dim_set_geometry_notice(dim, _("Trying to set an angular dimension on parallel lines"));
1189 return FALSE;
1192 factor = ((ref1->y - ref2->y) * vector[2].x -
1193 (ref1->x - ref2->x) * vector[2].y) / factor;
1195 center->x = ref1->x + vector[0].x * factor;
1196 center->y = ref1->y + vector[0].y * factor;
1197 *distance = cpml_pair_distance(center, pos);
1198 data->angle1 = cpml_vector_angle(&vector[0]);
1199 data->angle2 = cpml_vector_angle(&vector[2]);
1200 while (data->angle2 < data->angle1)
1201 data->angle2 += G_PI * 2;
1203 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
1205 return TRUE;
1208 static cairo_path_t *
1209 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1211 AdgADim *adim = (AdgADim *) user_data;
1212 AdgADimPrivate *data = adg_adim_get_instance_private(adim);
1214 return &data->cairo.path;