doc: update copyright line for 2019
[adg.git] / src / adg / adg-adim.c
blob2d053fe84bf545f41434a9c9ebd0c889276b8337
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(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 g_type_class_add_private(klass, sizeof(AdgADimPrivate));
117 gobject_class->dispose = _adg_dispose;
118 gobject_class->get_property = _adg_get_property;
119 gobject_class->set_property = _adg_set_property;
121 entity_class->global_changed = _adg_global_changed;
122 entity_class->local_changed = _adg_local_changed;
123 entity_class->invalidate = _adg_invalidate;
124 entity_class->arrange = _adg_arrange;
125 entity_class->render = _adg_render;
127 dim_class->default_value = _adg_default_value;
128 dim_class->compute_geometry = _adg_compute_geometry;
130 param = g_param_spec_boxed("org1",
131 P_("First Origin"),
132 P_("Where the first line comes from: this point is used toghether with \"ref1\" to align the first extension line"),
133 ADG_TYPE_POINT,
134 G_PARAM_READWRITE);
135 g_object_class_install_property(gobject_class, PROP_ORG1, param);
137 param = g_param_spec_boxed("org2",
138 P_("Second Origin"),
139 P_("Where the second line comes from: this point is used toghether with \"ref2\" to align the second extension line"),
140 ADG_TYPE_POINT,
141 G_PARAM_READWRITE);
142 g_object_class_install_property(gobject_class, PROP_ORG2, param);
144 param = g_param_spec_boolean("has-extension1",
145 P_("Has First Extension Line flag"),
146 P_("Show (TRUE) or hide (FALSE) the first extension line"),
147 TRUE, G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
150 param = g_param_spec_boolean("has-extension2",
151 P_("Has Second Extension Line flag"),
152 P_("Show (TRUE) or hide (FALSE) the second extension line"),
153 TRUE, G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
157 static void
158 adg_adim_init(AdgADim *adim)
160 AdgADimPrivate *data;
161 AdgStyle *style;
162 AdgDimStyle *dim_style;
163 cairo_path_data_t move_to, line_to, arc_to;
165 data = G_TYPE_INSTANCE_GET_PRIVATE(adim, ADG_TYPE_ADIM, AdgADimPrivate);
166 move_to.header.type = CPML_MOVE;
167 move_to.header.length = 2;
168 line_to.header.type = CPML_LINE;
169 line_to.header.length = 2;
170 arc_to.header.type = CPML_ARC;
171 arc_to.header.length = 3;
173 data->org1 = NULL;
174 data->org2 = NULL;
175 data->has_extension1 = TRUE;
176 data->has_extension2 = TRUE;
178 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
179 data->cairo.path.data = data->cairo.data;
180 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
181 data->cairo.path.data[0] = move_to;
182 data->cairo.path.data[2] = arc_to;
183 data->cairo.path.data[5] = move_to;
184 data->cairo.path.data[7] = line_to;
185 data->cairo.path.data[9] = move_to;
186 data->cairo.path.data[11] = line_to;
188 data->trail = NULL;
189 data->marker1 = NULL;
190 data->marker2 = NULL;
192 adim->data = data;
194 /* Override the default style of the dimension dress to express angles in
195 * sexagesimal units */
196 style = adg_dress_get_fallback(ADG_DRESS_DIMENSION);
197 dim_style = (AdgDimStyle *) adg_style_clone(style);
199 adg_dim_style_set_decimals(dim_style, 0);
200 adg_dim_style_set_rounding(dim_style, 3);
201 adg_dim_style_set_number_arguments(dim_style, "Dm");
202 adg_dim_style_set_number_format(dim_style, "%g°(%g')");
204 adg_entity_set_style((AdgEntity *) adim,
205 ADG_DRESS_DIMENSION,
206 (AdgStyle *) dim_style);
209 static void
210 _adg_dispose(GObject *object)
212 AdgEntity *entity;
213 AdgADim *adim;
214 AdgADimPrivate *data;
216 entity = (AdgEntity *) object;
217 adim = (AdgADim *) entity;
218 data = adim->data;
220 _adg_dispose_trail(adim);
221 _adg_dispose_markers(adim);
223 if (data->org1 != NULL)
224 data->org1 = adg_entity_point(entity, data->org1, NULL);
226 if (data->org2 != NULL)
227 data->org2 = adg_entity_point(entity, data->org2, NULL);
229 if (_ADG_OLD_OBJECT_CLASS->dispose)
230 _ADG_OLD_OBJECT_CLASS->dispose(object);
233 static void
234 _adg_get_property(GObject *object, guint prop_id,
235 GValue *value, GParamSpec *pspec)
237 AdgADimPrivate *data = ((AdgADim *) object)->data;
239 switch (prop_id) {
240 case PROP_ORG1:
241 g_value_set_boxed(value, data->org1);
242 break;
243 case PROP_ORG2:
244 g_value_set_boxed(value, data->org2);
245 break;
246 case PROP_HAS_EXTENSION1:
247 g_value_set_boolean(value, data->has_extension1);
248 break;
249 case PROP_HAS_EXTENSION2:
250 g_value_set_boolean(value, data->has_extension2);
251 break;
252 default:
253 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
254 break;
258 static void
259 _adg_set_property(GObject *object, guint prop_id,
260 const GValue *value, GParamSpec *pspec)
262 AdgEntity *entity;
263 AdgADimPrivate *data;
265 entity = (AdgEntity *) object;
266 data = ((AdgADim *) object)->data;
268 switch (prop_id) {
269 case PROP_ORG1:
270 data->org1 = adg_entity_point(entity, data->org1,
271 g_value_get_boxed(value));
272 break;
273 case PROP_ORG2:
274 data->org2 = adg_entity_point(entity, data->org2,
275 g_value_get_boxed(value));
276 break;
277 case PROP_HAS_EXTENSION1:
278 data->has_extension1 = g_value_get_boolean(value);
279 break;
280 case PROP_HAS_EXTENSION2:
281 data->has_extension2 = g_value_get_boolean(value);
282 break;
283 default:
284 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
285 break;
291 * adg_adim_new:
293 * Creates a new - undefined - angular dimension. You must, at least,
294 * define the first line by setting #AdgADim:org1 (start point) and
295 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
296 * (start point) and #AdgDim:ref2 (end point) and the position of
297 * the quote in #AdgDim:pos.
299 * Returns: the newly created angular dimension entity
301 * Since: 1.0
303 AdgADim *
304 adg_adim_new(void)
306 return g_object_new(ADG_TYPE_ADIM, NULL);
310 * adg_adim_new_full:
311 * @ref1: (allow-none): first reference point
312 * @ref2: (allow-none): second reference point
313 * @org1: (allow-none): first origin point
314 * @org2: (allow-none): second origin point
315 * @pos: (allow-none): the position point
317 * Creates a new angular dimension, specifing all the needed
318 * properties in one shot using #CpmlPair.
320 * Returns: the newly created angular dimension entity
322 * Since: 1.0
324 AdgADim *
325 adg_adim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
326 const CpmlPair *org1, const CpmlPair *org2,
327 const CpmlPair *pos)
329 AdgADim *adim;
330 AdgDim *dim;
332 adim = adg_adim_new();
333 dim = (AdgDim *) adim;
335 if (ref1 != NULL)
336 adg_dim_set_ref1_from_pair(dim, ref1);
338 if (ref2 != NULL)
339 adg_dim_set_ref2_from_pair(dim, ref2);
341 if (pos != NULL)
342 adg_dim_set_pos_from_pair(dim, pos);
344 if (org1 != NULL)
345 adg_adim_set_org1_from_pair(adim, org1);
347 if (org2 != NULL)
348 adg_adim_set_org2_from_pair(adim, org2);
350 return adim;
354 * adg_adim_new_full_explicit:
355 * @ref1_x: the x coordinate of end point of the first line
356 * @ref1_y: the y coordinate of end point of the first line
357 * @ref2_x: the x coordinate of end point of the second line
358 * @ref2_y: the y coordinate of end point of the second line
359 * @org1_x: the x coordinate of start point of the first line
360 * @org1_y: the y coordinate of start point of the first line
361 * @org2_x: the x coordinate of start point of the second line
362 * @org2_y: the y coordinate of start point of the second line
363 * @pos_x: the x coordinate of the position reference
364 * @pos_y: the y coordinate of the position reference
366 * Wrappes adg_adim_new_full() with explicit values.
368 * Returns: the newly created linear dimension entity
370 * Since: 1.0
372 AdgADim *
373 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
374 gdouble ref2_x, gdouble ref2_y,
375 gdouble org1_x, gdouble org1_y,
376 gdouble org2_x, gdouble org2_y,
377 gdouble pos_x, gdouble pos_y)
379 CpmlPair ref1, ref2, org1, org2, pos;
381 ref1.x = ref1_x;
382 ref1.y = ref1_y;
383 ref2.x = ref2_x;
384 ref2.y = ref2_y;
385 org1.x = org1_x;
386 org1.y = org1_y;
387 org2.x = org2_x;
388 org2.y = org2_y;
389 pos.x = pos_x;
390 pos.y = pos_y;
392 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
396 * adg_adim_new_full_from_model:
397 * @model: (transfer none): the model from which the named pairs are taken
398 * @ref1: (allow-none): the end point of the first line
399 * @ref2: (allow-none): the end point of the second line
400 * @org1: (allow-none): the origin of the first line
401 * @org2: (allow-none): the origin of the second line
402 * @pos: (allow-none): the position reference
404 * Creates a new angular dimension, specifing all the needed properties
405 * in one shot and using named pairs from @model.
407 * Returns: the newly created angular dimension entity
409 * Since: 1.0
411 AdgADim *
412 adg_adim_new_full_from_model(AdgModel *model,
413 const gchar *ref1, const gchar *ref2,
414 const gchar *org1, const gchar *org2,
415 const gchar *pos)
417 AdgADim *adim;
418 AdgDim *dim;
420 adim = adg_adim_new();
421 dim = (AdgDim *) adim;
423 if (ref1 != NULL)
424 adg_dim_set_ref1_from_model(dim, model, ref1);
426 if (ref2 != NULL)
427 adg_dim_set_ref2_from_model(dim, model, ref2);
429 if (pos != NULL)
430 adg_dim_set_pos_from_model(dim, model, pos);
432 if (org1 != NULL)
433 adg_adim_set_org1_from_model(adim, model, org1);
435 if (org2 != NULL)
436 adg_adim_set_org2_from_model(adim, model, org2);
438 return adim;
442 * adg_adim_set_org1:
443 * @adim: an #AdgADim
444 * @org1: the new point to use as first reference
446 * Sets the #AdgADim:org1 property to @org1. The old point
447 * is silently discarded, unreferencing its model if that
448 * point was bound to a named pair (hence, possibly destroying
449 * the model if this was the last reference).
451 * @org1 can be <constant>NULL</constant>, in which case
452 * the point is destroyed.
454 * Since: 1.0
456 void
457 adg_adim_set_org1(AdgADim *adim, const AdgPoint *org1)
459 g_return_if_fail(ADG_IS_ADIM(adim));
460 g_object_set(adim, "org1", org1, NULL);
464 * adg_adim_set_org1_explicit:
465 * @adim: an #AdgADim
466 * @x: x coordinate of the first reference point
467 * @y: y coordinate of the first reference point
469 * Sets the #AdgADim:org1 property to the (@x, @y) explicit
470 * coordinates. The old point is silently discarded,
471 * unreferencing its model if that point was bound to a named
472 * pair (hence, possibly destroying the model if this was the
473 * last reference).
475 * Since: 1.0
477 void
478 adg_adim_set_org1_explicit(AdgADim *adim, gdouble x, gdouble y)
480 AdgPoint *point = adg_point_new();
482 adg_point_set_pair_explicit(point, x, y);
483 adg_adim_set_org1(adim, point);
485 adg_point_destroy(point);
489 * adg_adim_set_org1_from_pair:
490 * @adim: an #AdgADim
491 * @org1: the coordinates pair of the first reference point
493 * Convenient function to set the #AdgADim:org1 property using a
494 * pair instead of explicit coordinates.
496 * Since: 1.0
498 void
499 adg_adim_set_org1_from_pair(AdgADim *adim, const CpmlPair *org1)
501 g_return_if_fail(org1 != NULL);
503 adg_adim_set_org1_explicit(adim, org1->x, org1->y);
507 * adg_adim_set_org1_from_model:
508 * @adim: an #AdgADim
509 * @model: the source #AdgModel
510 * @org1: a named pair in @model
512 * Binds #AdgADim:org1 to the @org1 named pair of @model. If @model
513 * is <constant>NULL</constant>, the point will be unset. In any
514 * case, the old point is silently discarded, unreferencing its
515 * model if that point was bound to a named pair (hence, possibly
516 * destroying the model if this was the last reference).
518 * The assignment is lazy so @org1 could be not be present in @model.
519 * Anyway, at the first access to this point an error will be raised
520 * if the named pair is still missing.
522 * Since: 1.0
524 void
525 adg_adim_set_org1_from_model(AdgADim *adim, AdgModel *model, const gchar *org1)
527 AdgPoint *point = adg_point_new();
529 adg_point_set_pair_from_model(point, model, org1);
530 adg_adim_set_org1(adim, point);
532 adg_point_destroy(point);
536 * adg_adim_get_org1:
537 * @adim: an #AdgADim
539 * Gets the #AdgADim:org1 point. The returned point is internally owned
540 * and must not be freed or modified.
542 * Returns: (transfer none): the first reference point
544 * Since: 1.0
546 AdgPoint *
547 adg_adim_get_org1(AdgADim *adim)
549 AdgADimPrivate *data;
551 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
553 data = adim->data;
555 return data->org1;
559 * adg_adim_set_org2:
560 * @adim: an #AdgADim
561 * @org2: the new point to use as first reference
563 * Sets the #AdgADim:org2 property to @org2. The old point
564 * is silently discarded, unreferencing its model if that
565 * point was bound to a named pair (hence, possibly destroying
566 * the model if this was the last reference).
568 * @org2 can be <constant>NULL</constant>, in which case
569 * the point is destroyed.
571 * Since: 1.0
573 void
574 adg_adim_set_org2(AdgADim *adim, const AdgPoint *org2)
576 g_return_if_fail(ADG_IS_ADIM(adim));
577 g_object_set(adim, "org2", org2, NULL);
581 * adg_adim_set_org2_explicit:
582 * @adim: an #AdgADim
583 * @x: x coordinate of the first reference point
584 * @y: y coordinate of the first reference point
586 * Sets the #AdgADim:org2 property to the (@x, @y) explicit
587 * coordinates. The old point is silently discarded,
588 * unreferencing its model if that point was bound to a named
589 * pair (hence, possibly destroying the model if this was the
590 * last reference).
592 * Since: 1.0
594 void
595 adg_adim_set_org2_explicit(AdgADim *adim, gdouble x, gdouble y)
597 AdgPoint *point = adg_point_new();
599 adg_point_set_pair_explicit(point, x, y);
600 adg_adim_set_org2(adim, point);
602 adg_point_destroy(point);
606 * adg_adim_set_org2_from_pair:
607 * @adim: an #AdgADim
608 * @org2: the coordinates pair of the first reference point
610 * Convenient function to set the #AdgADim:org2 property using a
611 * pair instead of explicit coordinates.
613 * Since: 1.0
615 void
616 adg_adim_set_org2_from_pair(AdgADim *adim, const CpmlPair *org2)
618 g_return_if_fail(org2 != NULL);
620 adg_adim_set_org2_explicit(adim, org2->x, org2->y);
624 * adg_adim_set_org2_from_model:
625 * @adim: an #AdgADim
626 * @model: the source #AdgModel
627 * @org2: a named pair in @model
629 * Binds #AdgADim:org2 to the @org2 named pair of @model. If @model
630 * is <constant>NULL</constant>, the point will be unset. In any
631 * case, the old point is silently discarded, unreferencing its
632 * model if that point was bound to a named pair (hence, possibly
633 * destroying the model if this was the last reference).
635 * The assignment is lazy so @org2 could be not be present in @model.
636 * Anyway, at the first access to this point an error will be raised
637 * if the named pair is still missing.
639 * Since: 1.0
641 void
642 adg_adim_set_org2_from_model(AdgADim *adim, AdgModel *model, const gchar *org2)
644 AdgPoint *point = adg_point_new();
646 adg_point_set_pair_from_model(point, model, org2);
647 adg_adim_set_org2(adim, point);
649 adg_point_destroy(point);
653 * adg_adim_get_org2:
654 * @adim: an #AdgADim
656 * Gets the #AdgADim:org2 point. The returned point is internally owned
657 * and must not be freed or modified.
659 * Returns: (transfer none): the second reference point
661 * Since: 1.0
663 AdgPoint *
664 adg_adim_get_org2(AdgADim *adim)
666 AdgADimPrivate *data;
668 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
670 data = adim->data;
672 return data->org2;
676 * adg_adim_switch_extension1:
677 * @adim: an #AdgADim entity
678 * @new_state: the new state
680 * Shows (if @new_state is <constant>TRUE</constant>) or hides
681 * (if @new_state is <constant>FALSE</constant>) the first
682 * extension line of @adim.
684 * Since: 1.0
686 void
687 adg_adim_switch_extension1(AdgADim *adim, gboolean new_state)
689 g_return_if_fail(ADG_IS_ADIM(adim));
690 g_return_if_fail(adg_is_boolean_value(new_state));
691 g_object_set(adim, "has-extension1", new_state, NULL);
695 * adg_adim_has_extension1:
696 * @adim: an #AdgADim entity
698 * Checks if @adim should render the first extension line.
700 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
702 * Since: 1.0
704 gboolean
705 adg_adim_has_extension1(AdgADim *adim)
707 AdgADimPrivate *data;
709 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
711 data = adim->data;
713 return data->has_extension1;
717 * adg_adim_switch_extension2:
718 * @adim: an #AdgADim entity
719 * @new_state: the new new_state
721 * Shows (if @new_state is <constant>TRUE</constant>) or hides
722 * (if @new_state is <constant>FALSE</constant>) the second
723 * extension line of @adim.
725 * Since: 1.0
727 void
728 adg_adim_switch_extension2(AdgADim *adim, gboolean new_state)
730 g_return_if_fail(ADG_IS_ADIM(adim));
731 g_return_if_fail(adg_is_boolean_value(new_state));
732 g_object_set(adim, "has-extension2", new_state, NULL);
736 * adg_adim_has_extension2:
737 * @adim: an #AdgADim entity
739 * Checks if @adim should render the second extension line.
741 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
743 * Since: 1.0
745 gboolean
746 adg_adim_has_extension2(AdgADim *adim)
748 AdgADimPrivate *data;
750 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
752 data = adim->data;
754 return data->has_extension2;
758 static void
759 _adg_global_changed(AdgEntity *entity)
761 AdgADimPrivate *data = ((AdgADim *) entity)->data;
763 _adg_unset_trail((AdgADim *) entity);
765 if (_ADG_OLD_ENTITY_CLASS->global_changed)
766 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
768 if (data->marker1 != NULL)
769 adg_entity_global_changed((AdgEntity *) data->marker1);
771 if (data->marker2 != NULL)
772 adg_entity_global_changed((AdgEntity *) data->marker2);
775 static void
776 _adg_local_changed(AdgEntity *entity)
778 _adg_unset_trail((AdgADim *) entity);
780 if (_ADG_OLD_ENTITY_CLASS->local_changed)
781 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
784 static void
785 _adg_invalidate(AdgEntity *entity)
787 AdgADim *adim;
788 AdgADimPrivate *data;
790 adim = (AdgADim *) entity;
791 data = adim->data;
793 _adg_dispose_trail(adim);
794 _adg_dispose_markers(adim);
795 _adg_unset_trail(adim);
797 if (data->org1)
798 adg_point_invalidate(data->org1);
799 if (data->org2)
800 adg_point_invalidate(data->org2);
802 if (_ADG_OLD_ENTITY_CLASS->invalidate)
803 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
806 static void
807 _adg_arrange(AdgEntity *entity)
809 AdgDim *dim;
810 AdgADim *adim;
811 AdgADimPrivate *data;
812 AdgAlignment *quote;
813 const cairo_matrix_t *global, *local;
814 CpmlPair ref1, ref2, base1, base12, base2;
815 CpmlPair pair;
816 CpmlExtents extents;
817 AdgEntity *marker_entity;
819 if (_ADG_OLD_ENTITY_CLASS->arrange)
820 _ADG_OLD_ENTITY_CLASS->arrange(entity);
822 dim = (AdgDim *) entity;
823 if (! adg_dim_compute_geometry(dim))
824 return;
826 adim = (AdgADim *) entity;
827 data = adim->data;
828 quote = adg_dim_get_quote(dim);
830 _adg_update_entities(adim);
832 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
833 AdgEntity *quote_entity = (AdgEntity *) quote;
834 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
835 return;
838 global = adg_entity_get_global_matrix(entity);
839 local = adg_entity_get_local_matrix(entity);
840 extents.is_defined = FALSE;
842 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
843 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
844 cpml_pair_copy(&base1, &data->point.base1);
845 cpml_pair_copy(&base12, &data->point.base12);
846 cpml_pair_copy(&base2, &data->point.base2);
848 /* Apply the local matrix to the relevant points */
849 cpml_pair_transform(&ref1, local);
850 cpml_pair_transform(&ref2, local);
851 cpml_pair_transform(&base1, local);
852 cpml_pair_transform(&base12, local);
853 cpml_pair_transform(&base2, local);
855 /* Combine points and global shifts to build the path */
856 pair.x = ref1.x + data->shift.from1.x;
857 pair.y = ref1.y + data->shift.from1.y;
858 cpml_pair_to_cairo(&pair, &data->cairo.data[6]);
860 pair.x = base1.x + data->shift.base1.x;
861 pair.y = base1.y + data->shift.base1.y;
862 cpml_pair_to_cairo(&pair, &data->cairo.data[1]);
864 pair.x += data->shift.to1.x;
865 pair.y += data->shift.to1.y;
866 cpml_pair_to_cairo(&pair, &data->cairo.data[8]);
868 pair.x = base12.x + data->shift.base12.x;
869 pair.y = base12.y + data->shift.base12.y;
870 cpml_pair_to_cairo(&pair, &data->cairo.data[3]);
872 pair.x = ref2.x + data->shift.from2.x;
873 pair.y = ref2.y + data->shift.from2.y;
874 cpml_pair_to_cairo(&pair, &data->cairo.data[10]);
876 pair.x = base2.x + data->shift.base2.x;
877 pair.y = base2.y + data->shift.base2.y;
878 cpml_pair_to_cairo(&pair, &data->cairo.data[4]);
880 pair.x += data->shift.to2.x;
881 pair.y += data->shift.to2.y;
882 cpml_pair_to_cairo(&pair, &data->cairo.data[12]);
884 /* Play with header lengths to show or hide the extension lines */
885 if (data->has_extension1) {
886 data->cairo.data[7].header.length = data->has_extension2 ? 2 : 6;
887 } else {
888 data->cairo.data[2].header.length = data->has_extension2 ? 7 : 11;
891 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
893 /* Arrange the quote */
894 if (quote != NULL) {
895 AdgEntity *quote_entity;
896 gdouble angle;
897 cairo_matrix_t map;
899 quote_entity = (AdgEntity *) quote;
900 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
901 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
903 adg_alignment_set_factor_explicit(quote, 0.5, 0);
905 cairo_matrix_init_translate(&map, pair.x, pair.y);
906 cairo_matrix_rotate(&map, angle);
907 adg_entity_set_global_map(quote_entity, &map);
908 adg_entity_arrange(quote_entity);
909 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
911 adg_matrix_copy(&data->quote.global_map, &map);
914 /* Arrange the trail */
915 if (data->trail != NULL) {
916 CpmlExtents trail_extents;
917 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
918 cpml_extents_transform(&trail_extents, global);
919 cpml_extents_add(&extents, &trail_extents);
920 } else {
921 _adg_dispose_markers(adim);
924 /* Arrange the markers */
925 if (data->marker1 != NULL) {
926 marker_entity = (AdgEntity *) data->marker1;
927 adg_marker_set_segment(data->marker1, data->trail, 1);
928 adg_entity_local_changed(marker_entity);
929 adg_entity_arrange(marker_entity);
930 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
932 if (data->marker2 != NULL) {
933 marker_entity = (AdgEntity *) data->marker2;
934 adg_marker_set_segment(data->marker2, data->trail, 1);
935 adg_entity_local_changed(marker_entity);
936 adg_entity_arrange(marker_entity);
937 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
940 adg_entity_set_extents(entity, &extents);
943 static void
944 _adg_render(AdgEntity *entity, cairo_t *cr)
946 AdgDim *dim;
947 AdgADim *adim;
948 AdgADimPrivate *data;
949 AdgDimStyle *dim_style;
950 AdgDress dress;
951 const cairo_path_t *cairo_path;
953 dim = (AdgDim *) entity;
954 if (! adg_dim_compute_geometry(dim)) {
955 /* Entity not arranged, probably due to undefined pair found */
956 return;
959 adim = (AdgADim *) entity;
960 data = adim->data;
961 dim_style = _ADG_GET_DIM_STYLE(dim);
963 adg_style_apply((AdgStyle *) dim_style, entity, cr);
964 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
966 if (data->marker1 != NULL)
967 adg_entity_render((AdgEntity *) data->marker1, cr);
968 if (data->marker2 != NULL)
969 adg_entity_render((AdgEntity *) data->marker2, cr);
971 cairo_transform(cr, adg_entity_get_global_matrix(entity));
972 dress = adg_dim_style_get_line_dress(dim_style);
973 adg_entity_apply_dress(entity, dress, cr);
975 cairo_path = adg_trail_get_cairo_path(data->trail);
976 cairo_append_path(cr, cairo_path);
977 cairo_stroke(cr);
980 static gchar *
981 _adg_default_value(AdgDim *dim)
983 AdgADim *adim;
984 AdgADimPrivate *data;
985 gdouble angle;
987 if (! adg_dim_compute_geometry(dim)) {
988 return g_strdup("undef");
991 adim = (AdgADim *) dim;
992 data = adim->data;
993 angle = (data->angle2 - data->angle1) * 180 / G_PI;
995 return adg_dim_get_text(dim, angle);
998 /* With "geometry" is considered any data (point, vector or angle)
999 * that can be cached: this is strictly related on how the arrange()
1000 * method works */
1001 static gboolean
1002 _adg_compute_geometry(AdgDim *dim)
1004 AdgADim *adim;
1005 AdgADimPrivate *data;
1006 AdgDimStyle *dim_style;
1007 gdouble from_offset, to_offset;
1008 gdouble spacing, level;
1009 CpmlVector vector[3];
1010 CpmlPair center;
1011 gdouble distance;
1013 adim = (AdgADim *) dim;
1014 if (! _adg_get_info(adim, vector, &center, &distance))
1015 return FALSE;
1017 data = adim->data;
1018 dim_style = _ADG_GET_DIM_STYLE(adim);
1019 from_offset = adg_dim_style_get_from_offset(dim_style);
1020 to_offset = adg_dim_style_get_to_offset(dim_style);
1021 spacing = adg_dim_style_get_baseline_spacing(dim_style);
1022 level = adg_dim_get_level((AdgDim *) adim);
1024 /* shift.from1 */
1025 cpml_vector_set_length(&vector[0], from_offset);
1026 cpml_pair_copy(&data->shift.from1, &vector[0]);
1028 /* shift.base1 */
1029 cpml_vector_set_length(&vector[0], level * spacing);
1030 cpml_pair_copy(&data->shift.base1, &vector[0]);
1032 /* shift.to1 */
1033 cpml_vector_set_length(&vector[0], to_offset);
1034 cpml_pair_copy(&data->shift.to1, &vector[0]);
1036 /* shift.from2 */
1037 cpml_vector_set_length(&vector[2], from_offset);
1038 cpml_pair_copy(&data->shift.from2, &vector[2]);
1040 /* shift.base2 */
1041 cpml_vector_set_length(&vector[2], level * spacing);
1042 cpml_pair_copy(&data->shift.base2, &vector[2]);
1044 /* shift.to2 */
1045 cpml_vector_set_length(&vector[2], to_offset);
1046 cpml_pair_copy(&data->shift.to2, &vector[2]);
1048 /* shift.base12 */
1049 cpml_vector_set_length(&vector[1], level * spacing);
1050 cpml_pair_copy(&data->shift.base12, &vector[1]);
1052 /* Distance can be 0, so the following will leave the
1053 * vector array in undefined state */
1055 /* point.base1 */
1056 cpml_vector_set_length(&vector[0], distance);
1057 data->point.base1.x = vector[0].x + center.x;
1058 data->point.base1.y = vector[0].y + center.y;
1060 /* point.base2 */
1061 cpml_vector_set_length(&vector[2], distance);
1062 data->point.base2.x = vector[2].x + center.x;
1063 data->point.base2.y = vector[2].y + center.y;
1065 /* point.base12 */
1066 cpml_vector_set_length(&vector[1], distance);
1067 data->point.base12.x = vector[1].x + center.x;
1068 data->point.base12.y = vector[1].y + center.y;
1070 return TRUE;
1073 static void
1074 _adg_update_entities(AdgADim *adim)
1076 AdgEntity *entity;
1077 AdgADimPrivate *data;
1078 AdgDimStyle *dim_style;
1080 entity = (AdgEntity *) adim;
1081 data = adim->data;
1082 dim_style = _ADG_GET_DIM_STYLE(adim);
1084 if (data->trail == NULL)
1085 data->trail = adg_trail_new(_adg_trail_callback, adim);
1087 if (data->marker1 == NULL) {
1088 data->marker1 = adg_dim_style_marker1_new(dim_style);
1089 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
1092 if (data->marker2 == NULL) {
1093 data->marker2 = adg_dim_style_marker2_new(dim_style);
1094 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
1098 static void
1099 _adg_unset_trail(AdgADim *adim)
1101 AdgADimPrivate *data = adim->data;
1103 if (data->trail != NULL)
1104 adg_model_clear((AdgModel *) data->trail);
1106 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1109 static void
1110 _adg_dispose_trail(AdgADim *adim)
1112 AdgADimPrivate *data = adim->data;
1114 if (data->trail != NULL) {
1115 g_object_unref(data->trail);
1116 data->trail = NULL;
1120 static void
1121 _adg_dispose_markers(AdgADim *adim)
1123 AdgADimPrivate *data = adim->data;
1125 if (data->marker1 != NULL) {
1126 g_object_unref(data->marker1);
1127 data->marker1 = NULL;
1130 if (data->marker2 != NULL) {
1131 g_object_unref(data->marker2);
1132 data->marker2 = NULL;
1136 static gboolean
1137 _adg_get_info(AdgADim *adim, CpmlVector vector[],
1138 CpmlPair *center, gdouble *distance)
1140 AdgDim *dim;
1141 AdgADimPrivate *data;
1142 AdgPoint *ref1_point, *ref2_point, *pos_point;
1143 const CpmlPair *ref1, *ref2, *pos;
1144 const CpmlPair *org1, *org2;
1145 gdouble factor;
1147 dim = (AdgDim *) adim;
1148 data = adim->data;
1149 ref1_point = adg_dim_get_ref1(dim);
1150 ref2_point = adg_dim_get_ref2(dim);
1151 pos_point = adg_dim_get_pos(dim);
1153 if (! adg_point_update(ref1_point)) {
1154 adg_dim_geometry_missing(dim, "ref1");
1155 return FALSE;
1158 if (! adg_point_update(ref2_point)) {
1159 adg_dim_geometry_missing(dim, "ref2");
1160 return FALSE;
1163 if (! adg_point_update(pos_point)) {
1164 adg_dim_geometry_missing(dim, "pos");
1165 return FALSE;
1168 if (! adg_point_update(data->org1)) {
1169 adg_dim_geometry_missing(dim, "org1");
1170 return FALSE;
1173 if (! adg_point_update(data->org2)) {
1174 adg_dim_geometry_missing(dim, "org2");
1175 return FALSE;
1178 ref1 = (CpmlPair *) ref1_point;
1179 ref2 = (CpmlPair *) ref2_point;
1180 pos = (CpmlPair *) pos_point;
1181 org1 = (CpmlPair *) data->org1;
1182 org2 = (CpmlPair *) data->org2;
1184 /* Check if the given points have valid coordinates */
1185 if (cpml_pair_equal(ref1, org1)) {
1186 adg_dim_geometry_coincident(dim, "ref1", "org1", ref1);
1187 return FALSE;
1190 if (cpml_pair_equal(ref2, org2)) {
1191 adg_dim_geometry_coincident(dim, "ref2", "org2", ref2);
1192 return FALSE;
1195 vector[0].x = ref1->x - org1->x;
1196 vector[0].y = ref1->y - org1->y;
1197 vector[2].x = ref2->x - org2->x;
1198 vector[2].y = ref2->y - org2->y;
1200 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
1201 if (factor == 0) {
1202 adg_dim_set_geometry_notice(dim, _("Trying to set an angular dimension on parallel lines"));
1203 return FALSE;
1206 factor = ((ref1->y - ref2->y) * vector[2].x -
1207 (ref1->x - ref2->x) * vector[2].y) / factor;
1209 center->x = ref1->x + vector[0].x * factor;
1210 center->y = ref1->y + vector[0].y * factor;
1211 *distance = cpml_pair_distance(center, pos);
1212 data->angle1 = cpml_vector_angle(&vector[0]);
1213 data->angle2 = cpml_vector_angle(&vector[2]);
1214 while (data->angle2 < data->angle1)
1215 data->angle2 += G_PI * 2;
1217 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
1219 return TRUE;
1222 static cairo_path_t *
1223 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1225 AdgADim *adim;
1226 AdgADimPrivate *data;
1228 adim = (AdgADim *) user_data;
1229 data = adim->data;
1231 return &data->cairo.path;