adg: provide example on how to customize a style
[adg.git] / src / adg / adg-adim.c
blobdf10c8b01320273f94cbc925827ba8b735ebf48b
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2017 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-textual.h"
50 #include "adg-toy-text.h"
51 #include "adg-dim.h"
52 #include "adg-dim-private.h"
54 #include "adg-adim.h"
55 #include "adg-adim-private.h"
58 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_adim_parent_class)
59 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_adim_parent_class)
62 G_DEFINE_TYPE(AdgADim, adg_adim, ADG_TYPE_DIM)
64 enum {
65 PROP_0,
66 PROP_ORG1,
67 PROP_ORG2,
68 PROP_HAS_EXTENSION1,
69 PROP_HAS_EXTENSION2
73 static void _adg_dispose (GObject *object);
74 static void _adg_get_property (GObject *object,
75 guint param_id,
76 GValue *value,
77 GParamSpec *pspec);
78 static void _adg_set_property (GObject *object,
79 guint param_id,
80 const GValue *value,
81 GParamSpec *pspec);
82 static void _adg_global_changed (AdgEntity *entity);
83 static void _adg_local_changed (AdgEntity *entity);
84 static void _adg_invalidate (AdgEntity *entity);
85 static void _adg_arrange (AdgEntity *entity);
86 static void _adg_render (AdgEntity *entity,
87 cairo_t *cr);
88 static gchar * _adg_default_value (AdgDim *dim);
89 static gboolean _adg_update_geometry (AdgADim *adim);
90 static void _adg_update_entities (AdgADim *adim);
91 static void _adg_unset_trail (AdgADim *adim);
92 static void _adg_dispose_trail (AdgADim *adim);
93 static void _adg_dispose_markers (AdgADim *adim);
94 static gboolean _adg_get_info (AdgADim *adim,
95 CpmlVector vector[],
96 CpmlPair *center,
97 gdouble *distance);
98 static cairo_path_t * _adg_trail_callback (AdgTrail *trail,
99 gpointer user_data);
102 static void
103 adg_adim_class_init(AdgADimClass *klass)
105 GObjectClass *gobject_class;
106 AdgEntityClass *entity_class;
107 AdgDimClass *dim_class;
108 GParamSpec *param;
110 gobject_class = (GObjectClass *) klass;
111 entity_class = (AdgEntityClass *) klass;
112 dim_class = (AdgDimClass *) klass;
114 g_type_class_add_private(klass, sizeof(AdgADimPrivate));
116 gobject_class->dispose = _adg_dispose;
117 gobject_class->get_property = _adg_get_property;
118 gobject_class->set_property = _adg_set_property;
120 entity_class->global_changed = _adg_global_changed;
121 entity_class->local_changed = _adg_local_changed;
122 entity_class->invalidate = _adg_invalidate;
123 entity_class->arrange = _adg_arrange;
124 entity_class->render = _adg_render;
126 dim_class->default_value = _adg_default_value;
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 cairo_path_data_t move_to, line_to, arc_to;
161 data = G_TYPE_INSTANCE_GET_PRIVATE(adim, ADG_TYPE_ADIM, AdgADimPrivate);
162 move_to.header.type = CPML_MOVE;
163 move_to.header.length = 2;
164 line_to.header.type = CPML_LINE;
165 line_to.header.length = 2;
166 arc_to.header.type = CPML_ARC;
167 arc_to.header.length = 3;
169 data->org1 = NULL;
170 data->org2 = NULL;
171 data->has_extension1 = TRUE;
172 data->has_extension2 = TRUE;
174 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
175 data->cairo.path.data = data->cairo.data;
176 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
177 data->cairo.path.data[0] = move_to;
178 data->cairo.path.data[2] = arc_to;
179 data->cairo.path.data[5] = move_to;
180 data->cairo.path.data[7] = line_to;
181 data->cairo.path.data[9] = move_to;
182 data->cairo.path.data[11] = line_to;
184 data->trail = NULL;
185 data->marker1 = NULL;
186 data->marker2 = NULL;
188 data->geometry_arranged = FALSE;
190 adim->data = data;
192 /* Override the generic default dress with a more specific one */
193 adg_dim_set_dim_dress((AdgDim *) adim, ADG_DRESS_DIMENSION_ANGULAR);
196 static void
197 _adg_dispose(GObject *object)
199 AdgEntity *entity;
200 AdgADim *adim;
201 AdgADimPrivate *data;
203 entity = (AdgEntity *) object;
204 adim = (AdgADim *) entity;
205 data = adim->data;
207 _adg_dispose_trail(adim);
208 _adg_dispose_markers(adim);
210 if (data->org1 != NULL)
211 data->org1 = adg_entity_point(entity, data->org1, NULL);
213 if (data->org2 != NULL)
214 data->org2 = adg_entity_point(entity, data->org2, NULL);
216 if (_ADG_OLD_OBJECT_CLASS->dispose)
217 _ADG_OLD_OBJECT_CLASS->dispose(object);
220 static void
221 _adg_get_property(GObject *object, guint prop_id,
222 GValue *value, GParamSpec *pspec)
224 AdgADimPrivate *data = ((AdgADim *) object)->data;
226 switch (prop_id) {
227 case PROP_ORG1:
228 g_value_set_boxed(value, data->org1);
229 break;
230 case PROP_ORG2:
231 g_value_set_boxed(value, data->org2);
232 break;
233 case PROP_HAS_EXTENSION1:
234 g_value_set_boolean(value, data->has_extension1);
235 break;
236 case PROP_HAS_EXTENSION2:
237 g_value_set_boolean(value, data->has_extension2);
238 break;
239 default:
240 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
241 break;
245 static void
246 _adg_set_property(GObject *object, guint prop_id,
247 const GValue *value, GParamSpec *pspec)
249 AdgEntity *entity;
250 AdgADimPrivate *data;
252 entity = (AdgEntity *) object;
253 data = ((AdgADim *) object)->data;
255 switch (prop_id) {
256 case PROP_ORG1:
257 data->org1 = adg_entity_point(entity, data->org1,
258 g_value_get_boxed(value));
259 break;
260 case PROP_ORG2:
261 data->org2 = adg_entity_point(entity, data->org2,
262 g_value_get_boxed(value));
263 break;
264 case PROP_HAS_EXTENSION1:
265 data->has_extension1 = g_value_get_boolean(value);
266 break;
267 case PROP_HAS_EXTENSION2:
268 data->has_extension2 = g_value_get_boolean(value);
269 break;
270 default:
271 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
272 break;
278 * adg_adim_new:
280 * Creates a new - undefined - angular dimension. You must, at least,
281 * define the first line by setting #AdgADim:org1 (start point) and
282 * #AdgDim:ref1 (end point), the second line by setting #AdgADim:org2
283 * (start point) and #AdgDim:ref2 (end point) and the position of
284 * the quote in #AdgDim:pos.
286 * Returns: the newly created angular dimension entity
288 * Since: 1.0
290 AdgADim *
291 adg_adim_new(void)
293 return g_object_new(ADG_TYPE_ADIM, NULL);
297 * adg_adim_new_full:
298 * @ref1: (allow-none): first reference point
299 * @ref2: (allow-none): second reference point
300 * @org1: (allow-none): first origin point
301 * @org2: (allow-none): second origin point
302 * @pos: (allow-none): the position point
304 * Creates a new angular dimension, specifing all the needed
305 * properties in one shot using #CpmlPair.
307 * Returns: the newly created angular dimension entity
309 * Since: 1.0
311 AdgADim *
312 adg_adim_new_full(const CpmlPair *ref1, const CpmlPair *ref2,
313 const CpmlPair *org1, const CpmlPair *org2,
314 const CpmlPair *pos)
316 AdgADim *adim;
317 AdgDim *dim;
319 adim = adg_adim_new();
320 dim = (AdgDim *) adim;
322 if (ref1 != NULL)
323 adg_dim_set_ref1_from_pair(dim, ref1);
325 if (ref2 != NULL)
326 adg_dim_set_ref2_from_pair(dim, ref2);
328 if (pos != NULL)
329 adg_dim_set_pos_from_pair(dim, pos);
331 if (org1 != NULL)
332 adg_adim_set_org1_from_pair(adim, org1);
334 if (org2 != NULL)
335 adg_adim_set_org2_from_pair(adim, org2);
337 return adim;
341 * adg_adim_new_full_explicit:
342 * @ref1_x: the x coordinate of end point of the first line
343 * @ref1_y: the y coordinate of end point of the first line
344 * @ref2_x: the x coordinate of end point of the second line
345 * @ref2_y: the y coordinate of end point of the second line
346 * @org1_x: the x coordinate of start point of the first line
347 * @org1_y: the y coordinate of start point of the first line
348 * @org2_x: the x coordinate of start point of the second line
349 * @org2_y: the y coordinate of start point of the second line
350 * @pos_x: the x coordinate of the position reference
351 * @pos_y: the y coordinate of the position reference
353 * Wrappes adg_adim_new_full() with explicit values.
355 * Returns: the newly created linear dimension entity
357 * Since: 1.0
359 AdgADim *
360 adg_adim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
361 gdouble ref2_x, gdouble ref2_y,
362 gdouble org1_x, gdouble org1_y,
363 gdouble org2_x, gdouble org2_y,
364 gdouble pos_x, gdouble pos_y)
366 CpmlPair ref1, ref2, org1, org2, pos;
368 ref1.x = ref1_x;
369 ref1.y = ref1_y;
370 ref2.x = ref2_x;
371 ref2.y = ref2_y;
372 org1.x = org1_x;
373 org1.y = org1_y;
374 org2.x = org2_x;
375 org2.y = org2_y;
376 pos.x = pos_x;
377 pos.y = pos_y;
379 return adg_adim_new_full(&ref1, &ref2, &org1, &org2, &pos);
383 * adg_adim_new_full_from_model:
384 * @model: (transfer none): the model from which the named pairs are taken
385 * @ref1: (allow-none): the end point of the first line
386 * @ref2: (allow-none): the end point of the second line
387 * @org1: (allow-none): the origin of the first line
388 * @org2: (allow-none): the origin of the second line
389 * @pos: (allow-none): the position reference
391 * Creates a new angular dimension, specifing all the needed properties
392 * in one shot and using named pairs from @model.
394 * Returns: the newly created angular dimension entity
396 * Since: 1.0
398 AdgADim *
399 adg_adim_new_full_from_model(AdgModel *model,
400 const gchar *ref1, const gchar *ref2,
401 const gchar *org1, const gchar *org2,
402 const gchar *pos)
404 AdgADim *adim;
405 AdgDim *dim;
407 adim = adg_adim_new();
408 dim = (AdgDim *) adim;
410 if (ref1 != NULL)
411 adg_dim_set_ref1_from_model(dim, model, ref1);
413 if (ref2 != NULL)
414 adg_dim_set_ref2_from_model(dim, model, ref2);
416 if (pos != NULL)
417 adg_dim_set_pos_from_model(dim, model, pos);
419 if (org1 != NULL)
420 adg_adim_set_org1_from_model(adim, model, org1);
422 if (org2 != NULL)
423 adg_adim_set_org2_from_model(adim, model, org2);
425 return adim;
429 * adg_adim_set_org1:
430 * @adim: an #AdgADim
431 * @org1: the new point to use as first reference
433 * Sets the #AdgADim:org1 property to @org1. The old point
434 * is silently discarded, unreferencing its model if that
435 * point was bound to a named pair (hence, possibly destroying
436 * the model if this was the last reference).
438 * @org1 can be <constant>NULL</constant>, in which case
439 * the point is destroyed.
441 * Since: 1.0
443 void
444 adg_adim_set_org1(AdgADim *adim, const AdgPoint *org1)
446 g_return_if_fail(ADG_IS_ADIM(adim));
447 g_object_set(adim, "org1", org1, NULL);
451 * adg_adim_set_org1_explicit:
452 * @adim: an #AdgADim
453 * @x: x coordinate of the first reference point
454 * @y: y coordinate of the first reference point
456 * Sets the #AdgADim:org1 property to the (@x, @y) explicit
457 * coordinates. The old point is silently discarded,
458 * unreferencing its model if that point was bound to a named
459 * pair (hence, possibly destroying the model if this was the
460 * last reference).
462 * Since: 1.0
464 void
465 adg_adim_set_org1_explicit(AdgADim *adim, gdouble x, gdouble y)
467 AdgPoint *point = adg_point_new();
469 adg_point_set_pair_explicit(point, x, y);
470 adg_adim_set_org1(adim, point);
472 adg_point_destroy(point);
476 * adg_adim_set_org1_from_pair:
477 * @adim: an #AdgADim
478 * @org1: the coordinates pair of the first reference point
480 * Convenient function to set the #AdgADim:org1 property using a
481 * pair instead of explicit coordinates.
483 * Since: 1.0
485 void
486 adg_adim_set_org1_from_pair(AdgADim *adim, const CpmlPair *org1)
488 g_return_if_fail(org1 != NULL);
490 adg_adim_set_org1_explicit(adim, org1->x, org1->y);
494 * adg_adim_set_org1_from_model:
495 * @adim: an #AdgADim
496 * @model: the source #AdgModel
497 * @org1: a named pair in @model
499 * Binds #AdgADim:org1 to the @org1 named pair of @model. If @model
500 * is <constant>NULL</constant>, the point will be unset. In any
501 * case, the old point is silently discarded, unreferencing its
502 * model if that point was bound to a named pair (hence, possibly
503 * destroying the model if this was the last reference).
505 * The assignment is lazy so @org1 could be not be present in @model.
506 * Anyway, at the first access to this point an error will be raised
507 * if the named pair is still missing.
509 * Since: 1.0
511 void
512 adg_adim_set_org1_from_model(AdgADim *adim, AdgModel *model, const gchar *org1)
514 AdgPoint *point = adg_point_new();
516 adg_point_set_pair_from_model(point, model, org1);
517 adg_adim_set_org1(adim, point);
519 adg_point_destroy(point);
523 * adg_adim_get_org1:
524 * @adim: an #AdgADim
526 * Gets the #AdgADim:org1 point. The returned point is internally owned
527 * and must not be freed or modified.
529 * Returns: (transfer none): the first reference point
531 * Since: 1.0
533 AdgPoint *
534 adg_adim_get_org1(AdgADim *adim)
536 AdgADimPrivate *data;
538 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
540 data = adim->data;
542 return data->org1;
546 * adg_adim_set_org2:
547 * @adim: an #AdgADim
548 * @org2: the new point to use as first reference
550 * Sets the #AdgADim:org2 property to @org2. The old point
551 * is silently discarded, unreferencing its model if that
552 * point was bound to a named pair (hence, possibly destroying
553 * the model if this was the last reference).
555 * @org2 can be <constant>NULL</constant>, in which case
556 * the point is destroyed.
558 * Since: 1.0
560 void
561 adg_adim_set_org2(AdgADim *adim, const AdgPoint *org2)
563 g_return_if_fail(ADG_IS_ADIM(adim));
564 g_object_set(adim, "org2", org2, NULL);
568 * adg_adim_set_org2_explicit:
569 * @adim: an #AdgADim
570 * @x: x coordinate of the first reference point
571 * @y: y coordinate of the first reference point
573 * Sets the #AdgADim:org2 property to the (@x, @y) explicit
574 * coordinates. The old point is silently discarded,
575 * unreferencing its model if that point was bound to a named
576 * pair (hence, possibly destroying the model if this was the
577 * last reference).
579 * Since: 1.0
581 void
582 adg_adim_set_org2_explicit(AdgADim *adim, gdouble x, gdouble y)
584 AdgPoint *point = adg_point_new();
586 adg_point_set_pair_explicit(point, x, y);
587 adg_adim_set_org2(adim, point);
589 adg_point_destroy(point);
593 * adg_adim_set_org2_from_pair:
594 * @adim: an #AdgADim
595 * @org2: the coordinates pair of the first reference point
597 * Convenient function to set the #AdgADim:org2 property using a
598 * pair instead of explicit coordinates.
600 * Since: 1.0
602 void
603 adg_adim_set_org2_from_pair(AdgADim *adim, const CpmlPair *org2)
605 g_return_if_fail(org2 != NULL);
607 adg_adim_set_org2_explicit(adim, org2->x, org2->y);
611 * adg_adim_set_org2_from_model:
612 * @adim: an #AdgADim
613 * @model: the source #AdgModel
614 * @org2: a named pair in @model
616 * Binds #AdgADim:org2 to the @org2 named pair of @model. If @model
617 * is <constant>NULL</constant>, the point will be unset. In any
618 * case, the old point is silently discarded, unreferencing its
619 * model if that point was bound to a named pair (hence, possibly
620 * destroying the model if this was the last reference).
622 * The assignment is lazy so @org2 could be not be present in @model.
623 * Anyway, at the first access to this point an error will be raised
624 * if the named pair is still missing.
626 * Since: 1.0
628 void
629 adg_adim_set_org2_from_model(AdgADim *adim, AdgModel *model, const gchar *org2)
631 AdgPoint *point = adg_point_new();
633 adg_point_set_pair_from_model(point, model, org2);
634 adg_adim_set_org2(adim, point);
636 adg_point_destroy(point);
640 * adg_adim_get_org2:
641 * @adim: an #AdgADim
643 * Gets the #AdgADim:org2 point. The returned point is internally owned
644 * and must not be freed or modified.
646 * Returns: (transfer none): the second reference point
648 * Since: 1.0
650 AdgPoint *
651 adg_adim_get_org2(AdgADim *adim)
653 AdgADimPrivate *data;
655 g_return_val_if_fail(ADG_IS_ADIM(adim), NULL);
657 data = adim->data;
659 return data->org2;
663 * adg_adim_switch_extension1:
664 * @adim: an #AdgADim entity
665 * @new_state: the new state
667 * Shows (if @new_state is <constant>TRUE</constant>) or hides
668 * (if @new_state is <constant>FALSE</constant>) the first
669 * extension line of @adim.
671 * Since: 1.0
673 void
674 adg_adim_switch_extension1(AdgADim *adim, gboolean new_state)
676 g_return_if_fail(ADG_IS_ADIM(adim));
677 g_return_if_fail(adg_is_boolean_value(new_state));
678 g_object_set(adim, "has-extension1", new_state, NULL);
682 * adg_adim_has_extension1:
683 * @adim: an #AdgADim entity
685 * Checks if @adim should render the first extension line.
687 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
689 * Since: 1.0
691 gboolean
692 adg_adim_has_extension1(AdgADim *adim)
694 AdgADimPrivate *data;
696 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
698 data = adim->data;
700 return data->has_extension1;
704 * adg_adim_switch_extension2:
705 * @adim: an #AdgADim entity
706 * @new_state: the new new_state
708 * Shows (if @new_state is <constant>TRUE</constant>) or hides
709 * (if @new_state is <constant>FALSE</constant>) the second
710 * extension line of @adim.
712 * Since: 1.0
714 void
715 adg_adim_switch_extension2(AdgADim *adim, gboolean new_state)
717 g_return_if_fail(ADG_IS_ADIM(adim));
718 g_return_if_fail(adg_is_boolean_value(new_state));
719 g_object_set(adim, "has-extension2", new_state, NULL);
723 * adg_adim_has_extension2:
724 * @adim: an #AdgADim entity
726 * Checks if @adim should render the second extension line.
728 * Returns: <constant>TRUE</constant> on first extension line presents, <constant>FALSE</constant> otherwise.
730 * Since: 1.0
732 gboolean
733 adg_adim_has_extension2(AdgADim *adim)
735 AdgADimPrivate *data;
737 g_return_val_if_fail(ADG_IS_ADIM(adim), FALSE);
739 data = adim->data;
741 return data->has_extension2;
745 static void
746 _adg_global_changed(AdgEntity *entity)
748 AdgADimPrivate *data = ((AdgADim *) entity)->data;
750 _adg_unset_trail((AdgADim *) entity);
752 if (_ADG_OLD_ENTITY_CLASS->global_changed)
753 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
755 if (data->marker1 != NULL)
756 adg_entity_global_changed((AdgEntity *) data->marker1);
758 if (data->marker2 != NULL)
759 adg_entity_global_changed((AdgEntity *) data->marker2);
762 static void
763 _adg_local_changed(AdgEntity *entity)
765 _adg_unset_trail((AdgADim *) entity);
767 if (_ADG_OLD_ENTITY_CLASS->local_changed)
768 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
771 static void
772 _adg_invalidate(AdgEntity *entity)
774 AdgADim *adim;
775 AdgADimPrivate *data;
777 adim = (AdgADim *) entity;
778 data = adim->data;
780 _adg_dispose_trail(adim);
781 _adg_dispose_markers(adim);
782 data->geometry_arranged = FALSE;
783 _adg_unset_trail(adim);
785 if (data->org1)
786 adg_point_invalidate(data->org1);
787 if (data->org2)
788 adg_point_invalidate(data->org2);
790 if (_ADG_OLD_ENTITY_CLASS->invalidate)
791 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
794 static void
795 _adg_arrange(AdgEntity *entity)
797 AdgADim *adim;
798 AdgDim *dim;
799 AdgADimPrivate *data;
800 AdgAlignment *quote;
801 const cairo_matrix_t *global, *local;
802 CpmlPair ref1, ref2, base1, base12, base2;
803 CpmlPair pair;
804 CpmlExtents extents;
805 AdgEntity *marker_entity;
807 if (_ADG_OLD_ENTITY_CLASS->arrange)
808 _ADG_OLD_ENTITY_CLASS->arrange(entity);
810 adim = (AdgADim *) entity;
812 if (! _adg_update_geometry(adim))
813 return;
815 dim = (AdgDim *) adim;
816 data = adim->data;
817 quote = adg_dim_get_quote(dim);
819 _adg_update_entities(adim);
821 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
822 AdgEntity *quote_entity = (AdgEntity *) quote;
823 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
824 return;
827 global = adg_entity_get_global_matrix(entity);
828 local = adg_entity_get_local_matrix(entity);
829 extents.is_defined = FALSE;
831 cpml_pair_copy(&ref1, (CpmlPair *) adg_dim_get_ref1(dim));
832 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
833 cpml_pair_copy(&base1, &data->point.base1);
834 cpml_pair_copy(&base12, &data->point.base12);
835 cpml_pair_copy(&base2, &data->point.base2);
837 /* Apply the local matrix to the relevant points */
838 cpml_pair_transform(&ref1, local);
839 cpml_pair_transform(&ref2, local);
840 cpml_pair_transform(&base1, local);
841 cpml_pair_transform(&base12, local);
842 cpml_pair_transform(&base2, local);
844 /* Combine points and global shifts to build the path */
845 pair.x = ref1.x + data->shift.from1.x;
846 pair.y = ref1.y + data->shift.from1.y;
847 cpml_pair_to_cairo(&pair, &data->cairo.data[6]);
849 pair.x = base1.x + data->shift.base1.x;
850 pair.y = base1.y + data->shift.base1.y;
851 cpml_pair_to_cairo(&pair, &data->cairo.data[1]);
853 pair.x += data->shift.to1.x;
854 pair.y += data->shift.to1.y;
855 cpml_pair_to_cairo(&pair, &data->cairo.data[8]);
857 pair.x = base12.x + data->shift.base12.x;
858 pair.y = base12.y + data->shift.base12.y;
859 cpml_pair_to_cairo(&pair, &data->cairo.data[3]);
861 pair.x = ref2.x + data->shift.from2.x;
862 pair.y = ref2.y + data->shift.from2.y;
863 cpml_pair_to_cairo(&pair, &data->cairo.data[10]);
865 pair.x = base2.x + data->shift.base2.x;
866 pair.y = base2.y + data->shift.base2.y;
867 cpml_pair_to_cairo(&pair, &data->cairo.data[4]);
869 pair.x += data->shift.to2.x;
870 pair.y += data->shift.to2.y;
871 cpml_pair_to_cairo(&pair, &data->cairo.data[12]);
873 /* Play with header lengths to show or hide the extension lines */
874 if (data->has_extension1) {
875 data->cairo.data[7].header.length = data->has_extension2 ? 2 : 6;
876 } else {
877 data->cairo.data[2].header.length = data->has_extension2 ? 7 : 11;
880 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
882 /* Arrange the quote */
883 if (quote != NULL) {
884 AdgEntity *quote_entity;
885 gdouble angle;
886 cairo_matrix_t map;
888 quote_entity = (AdgEntity *) quote;
889 angle = adg_dim_quote_angle(dim, (data->angle1 + data->angle2) / 2 + G_PI_2);
890 cpml_pair_from_cairo(&pair, &data->cairo.data[3]);
892 adg_alignment_set_factor_explicit(quote, 0.5, 0);
894 cairo_matrix_init_translate(&map, pair.x, pair.y);
895 cairo_matrix_rotate(&map, angle);
896 adg_entity_set_global_map(quote_entity, &map);
897 adg_entity_arrange(quote_entity);
898 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
900 adg_matrix_copy(&data->quote.global_map, &map);
903 /* Arrange the trail */
904 if (data->trail != NULL) {
905 CpmlExtents trail_extents;
906 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
907 cpml_extents_transform(&trail_extents, global);
908 cpml_extents_add(&extents, &trail_extents);
909 } else {
910 _adg_dispose_markers(adim);
913 /* Arrange the markers */
914 if (data->marker1 != NULL) {
915 marker_entity = (AdgEntity *) data->marker1;
916 adg_marker_set_segment(data->marker1, data->trail, 1);
917 adg_entity_local_changed(marker_entity);
918 adg_entity_arrange(marker_entity);
919 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
921 if (data->marker2 != NULL) {
922 marker_entity = (AdgEntity *) data->marker2;
923 adg_marker_set_segment(data->marker2, data->trail, 1);
924 adg_entity_local_changed(marker_entity);
925 adg_entity_arrange(marker_entity);
926 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
929 adg_entity_set_extents(entity, &extents);
932 static void
933 _adg_render(AdgEntity *entity, cairo_t *cr)
935 AdgADim *adim;
936 AdgDim *dim;
937 AdgADimPrivate *data;
938 AdgDimStyle *dim_style;
939 AdgDress dress;
940 const cairo_path_t *cairo_path;
942 adim = (AdgADim *) entity;
943 data = adim->data;
945 if (!data->geometry_arranged) {
946 /* Entity not arranged, probably due to undefined pair found */
947 return;
950 dim = (AdgDim *) entity;
951 dim_style = _ADG_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 adim = (AdgADim *) dim;
978 if (! _adg_update_geometry(adim)) {
979 return g_strdup("undef");
982 data = adim->data;
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_update_geometry(AdgADim *adim)
994 AdgADimPrivate *data;
995 AdgDimStyle *dim_style;
996 gdouble from_offset, to_offset;
997 gdouble spacing, level;
998 CpmlVector vector[3];
999 CpmlPair center;
1000 gdouble distance;
1002 data = adim->data;
1004 /* Check for cached results */
1005 if (data->geometry_arranged)
1006 return TRUE;
1008 if (! _adg_get_info(adim, vector, &center, &distance))
1009 return FALSE;
1011 dim_style = _ADG_GET_DIM_STYLE(adim);
1012 from_offset = adg_dim_style_get_from_offset(dim_style);
1013 to_offset = adg_dim_style_get_to_offset(dim_style);
1014 spacing = adg_dim_style_get_baseline_spacing(dim_style);
1015 level = adg_dim_get_level((AdgDim *) adim);
1017 /* shift.from1 */
1018 cpml_vector_set_length(&vector[0], from_offset);
1019 cpml_pair_copy(&data->shift.from1, &vector[0]);
1021 /* shift.base1 */
1022 cpml_vector_set_length(&vector[0], level * spacing);
1023 cpml_pair_copy(&data->shift.base1, &vector[0]);
1025 /* shift.to1 */
1026 cpml_vector_set_length(&vector[0], to_offset);
1027 cpml_pair_copy(&data->shift.to1, &vector[0]);
1029 /* shift.from2 */
1030 cpml_vector_set_length(&vector[2], from_offset);
1031 cpml_pair_copy(&data->shift.from2, &vector[2]);
1033 /* shift.base2 */
1034 cpml_vector_set_length(&vector[2], level * spacing);
1035 cpml_pair_copy(&data->shift.base2, &vector[2]);
1037 /* shift.to2 */
1038 cpml_vector_set_length(&vector[2], to_offset);
1039 cpml_pair_copy(&data->shift.to2, &vector[2]);
1041 /* shift.base12 */
1042 cpml_vector_set_length(&vector[1], level * spacing);
1043 cpml_pair_copy(&data->shift.base12, &vector[1]);
1045 /* Distance can be 0, so the following will leave the
1046 * vector array in undefined state */
1048 /* point.base1 */
1049 cpml_vector_set_length(&vector[0], distance);
1050 data->point.base1.x = vector[0].x + center.x;
1051 data->point.base1.y = vector[0].y + center.y;
1053 /* point.base2 */
1054 cpml_vector_set_length(&vector[2], distance);
1055 data->point.base2.x = vector[2].x + center.x;
1056 data->point.base2.y = vector[2].y + center.y;
1058 /* point.base12 */
1059 cpml_vector_set_length(&vector[1], distance);
1060 data->point.base12.x = vector[1].x + center.x;
1061 data->point.base12.y = vector[1].y + center.y;
1063 data->geometry_arranged = TRUE;
1065 return TRUE;
1068 static void
1069 _adg_update_entities(AdgADim *adim)
1071 AdgEntity *entity;
1072 AdgADimPrivate *data;
1073 AdgDimStyle *dim_style;
1075 entity = (AdgEntity *) adim;
1076 data = adim->data;
1077 dim_style = _ADG_GET_DIM_STYLE(adim);
1079 if (data->trail == NULL)
1080 data->trail = adg_trail_new(_adg_trail_callback, adim);
1082 if (data->marker1 == NULL) {
1083 data->marker1 = adg_dim_style_marker1_new(dim_style);
1084 adg_entity_set_parent((AdgEntity *) data->marker1, entity);
1087 if (data->marker2 == NULL) {
1088 data->marker2 = adg_dim_style_marker2_new(dim_style);
1089 adg_entity_set_parent((AdgEntity *) data->marker2, entity);
1093 static void
1094 _adg_unset_trail(AdgADim *adim)
1096 AdgADimPrivate *data = adim->data;
1098 if (data->trail != NULL)
1099 adg_model_clear((AdgModel *) data->trail);
1101 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
1104 static void
1105 _adg_dispose_trail(AdgADim *adim)
1107 AdgADimPrivate *data = adim->data;
1109 if (data->trail != NULL) {
1110 g_object_unref(data->trail);
1111 data->trail = NULL;
1115 static void
1116 _adg_dispose_markers(AdgADim *adim)
1118 AdgADimPrivate *data = adim->data;
1120 if (data->marker1 != NULL) {
1121 g_object_unref(data->marker1);
1122 data->marker1 = NULL;
1125 if (data->marker2 != NULL) {
1126 g_object_unref(data->marker2);
1127 data->marker2 = NULL;
1131 static gboolean
1132 _adg_get_info(AdgADim *adim, CpmlVector vector[],
1133 CpmlPair *center, gdouble *distance)
1135 AdgDim *dim;
1136 AdgADimPrivate *data;
1137 AdgPoint *ref1_point, *ref2_point, *pos_point;
1138 const CpmlPair *ref1, *ref2, *pos;
1139 const CpmlPair *org1, *org2;
1140 gdouble factor;
1142 dim = (AdgDim *) adim;
1143 data = adim->data;
1144 ref1_point = adg_dim_get_ref1(dim);
1145 ref2_point = adg_dim_get_ref2(dim);
1146 pos_point = adg_dim_get_pos(dim);
1148 /* Check if the needed points are all properly defined */
1149 if (! adg_point_update(ref1_point) ||
1150 ! adg_point_update(ref2_point) ||
1151 ! adg_point_update(pos_point) ||
1152 ! adg_point_update(data->org1) ||
1153 ! adg_point_update(data->org2))
1155 return FALSE;
1158 ref1 = (CpmlPair *) ref1_point;
1159 ref2 = (CpmlPair *) ref2_point;
1160 pos = (CpmlPair *) pos_point;
1161 org1 = (CpmlPair *) data->org1;
1162 org2 = (CpmlPair *) data->org2;
1164 /* Check if the given points have valid coordinates */
1165 if (cpml_pair_equal(ref1, org1)) {
1166 g_warning(_("%s: ref1 and org1 cannot be coincidents (%lf, %lf)"),
1167 G_STRLOC, ref1->x, ref1->y);
1168 return FALSE;
1171 if (cpml_pair_equal(ref2, org2)) {
1172 g_warning(_("%s: ref2 and org2 cannot be coincidents (%lf, %lf)"),
1173 G_STRLOC, ref2->x, ref2->y);
1174 return FALSE;
1177 vector[0].x = ref1->x - org1->x;
1178 vector[0].y = ref1->y - org1->y;
1179 vector[2].x = ref2->x - org2->x;
1180 vector[2].y = ref2->y - org2->y;
1182 factor = vector[0].x * vector[2].y - vector[0].y * vector[2].x;
1183 if (factor == 0) {
1184 /* Parallel lines: hang with an error message */
1185 g_warning(_("%s: trying to set an angular dimension on parallel lines"),
1186 G_STRLOC);
1187 return FALSE;
1190 factor = ((ref1->y - ref2->y) * vector[2].x -
1191 (ref1->x - ref2->x) * vector[2].y) / factor;
1193 center->x = ref1->x + vector[0].x * factor;
1194 center->y = ref1->y + vector[0].y * factor;
1195 *distance = cpml_pair_distance(center, pos);
1196 data->angle1 = cpml_vector_angle(&vector[0]);
1197 data->angle2 = cpml_vector_angle(&vector[2]);
1198 while (data->angle2 < data->angle1)
1199 data->angle2 += G_PI * 2;
1201 cpml_vector_from_angle(&vector[1], (data->angle1 + data->angle2) / 2);
1203 return TRUE;
1206 static cairo_path_t *
1207 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
1209 AdgADim *adim;
1210 AdgADimPrivate *data;
1212 adim = (AdgADim *) user_data;
1213 data = adim->data;
1215 return &data->cairo.path;