1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, 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 Library 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 * Library 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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 * @short_description: Linear dimensions
26 * The #AdgLDim entity represents a linear dimension.
30 #include "adg-ldim-private.h"
31 #include "adg-dim-private.h"
32 #include "adg-dim-style.h"
33 #include "adg-dim-style-private.h"
34 #include "adg-container.h"
35 #include "adg-arrow-style.h"
39 #include <gcontainer/gcontainer.h>
41 #define PARENT_CLASS ((AdgDimClass *) adg_ldim_parent_class)
51 static void finalize (GObject
*object
);
52 static void get_property (GObject
*object
,
56 static void set_property (GObject
*object
,
60 static void update (AdgEntity
*entity
);
61 static void render (AdgEntity
*entity
,
63 static gchar
* default_quote (AdgDim
*dim
);
66 G_DEFINE_TYPE (AdgLDim
, adg_ldim
, ADG_TYPE_DIM
);
70 adg_ldim_class_init (AdgLDimClass
*klass
)
72 GObjectClass
*gobject_class
;
73 AdgEntityClass
*entity_class
;
74 AdgDimClass
*dim_class
;
77 gobject_class
= (GObjectClass
*) klass
;
78 entity_class
= (AdgEntityClass
*) klass
;
79 dim_class
= (AdgDimClass
*) klass
;
81 g_type_class_add_private (klass
, sizeof (AdgLDimPrivate
));
83 gobject_class
->finalize
= finalize
;
84 gobject_class
->get_property
= get_property
;
85 gobject_class
->set_property
= set_property
;
87 entity_class
->render
= render
;
89 dim_class
->default_quote
= default_quote
;
91 param
= g_param_spec_double ("direction",
93 P_("The inclination angle of the extension lines"),
94 -G_MAXDOUBLE
, G_MAXDOUBLE
, CPML_DIR_RIGHT
,
95 G_PARAM_READWRITE
|G_PARAM_CONSTRUCT
);
96 g_object_class_install_property (gobject_class
, PROP_DIRECTION
, param
);
100 adg_ldim_init (AdgLDim
*ldim
)
102 AdgLDimPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (ldim
, ADG_TYPE_LDIM
,
105 priv
->direction
= CPML_DIR_RIGHT
;
107 priv
->extension1
.status
= CAIRO_STATUS_SUCCESS
;
108 priv
->extension1
.data
= NULL
;
109 priv
->extension1
.num_data
= 4;
111 priv
->extension2
.status
= CAIRO_STATUS_SUCCESS
;
112 priv
->extension2
.data
= NULL
;
113 priv
->extension2
.num_data
= 4;
115 priv
->arrow_path
.status
= CAIRO_STATUS_SUCCESS
;
116 priv
->arrow_path
.data
= NULL
;
117 priv
->arrow_path
.num_data
= 4;
119 priv
->baseline
.status
= CAIRO_STATUS_SUCCESS
;
120 priv
->baseline
.data
= NULL
;
121 priv
->baseline
.num_data
= 4;
127 finalize (GObject
*object
)
129 AdgLDimPrivate
*priv
= ((AdgLDim
*) object
)->priv
;
131 g_free (priv
->extension1
.data
);
132 g_free (priv
->extension2
.data
);
133 g_free (priv
->arrow_path
.data
);
134 g_free (priv
->baseline
.data
);
138 get_property (GObject
*object
,
143 AdgLDim
*ldim
= ADG_LDIM (object
);
148 g_value_set_double (value
, ldim
->priv
->direction
);
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
157 set_property (GObject
*object
,
162 AdgLDim
*ldim
= ADG_LDIM (object
);
167 ldim
->priv
->direction
= g_value_get_double (value
);
170 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
177 update (AdgEntity
*entity
)
181 AdgDimStylePrivate
*style_data
;
182 AdgMatrix device2user
;
187 AdgPair arrow1
, arrow2
;
188 AdgPair baseline1
, baseline2
;
189 cairo_path_data_t
*path_data
;
191 g_return_if_fail (((AdgDim
*) entity
)->priv
->dim_style
!= NULL
);
193 dim
= (AdgDim
*) entity
;
194 ldim
= (AdgLDim
*) entity
;
195 style_data
= dim
->priv
->dim_style
->priv
;
197 /* Get the inverted transformation matrix */
198 adg_matrix_set (&device2user
, adg_entity_get_model_matrix (entity
));
199 g_return_if_fail (cairo_matrix_invert (&device2user
) == CAIRO_STATUS_SUCCESS
);
201 /* Set vector to the director of the extension lines */
202 cpml_vector_from_angle (&vector
, ldim
->priv
->direction
);
204 /* Calculate from1 and from2 */
205 offset
.x
= vector
.x
* style_data
->from_offset
;
206 offset
.y
= vector
.y
* style_data
->from_offset
;
207 cairo_matrix_transform_distance (&device2user
, &offset
.x
, &offset
.y
);
209 from1
.x
= dim
->priv
->ref1
.x
+ offset
.x
;
210 from1
.y
= dim
->priv
->ref1
.y
+ offset
.y
;
211 from2
.x
= dim
->priv
->ref2
.x
+ offset
.x
;
212 from2
.y
= dim
->priv
->ref2
.y
+ offset
.y
;
214 /* Calculate arrow1 and arrow2 */
215 offset
.x
= vector
.x
* style_data
->baseline_spacing
* dim
->priv
->level
;
216 offset
.y
= vector
.y
* style_data
->baseline_spacing
* dim
->priv
->level
;
217 cairo_matrix_transform_distance (&device2user
, &offset
.x
, &offset
.y
);
219 arrow1
.x
= dim
->priv
->pos1
.x
+ offset
.x
;
220 arrow1
.y
= dim
->priv
->pos1
.y
+ offset
.y
;
221 arrow2
.x
= dim
->priv
->pos2
.x
+ offset
.x
;
222 arrow2
.y
= dim
->priv
->pos2
.y
+ offset
.y
;
224 /* Calculate to1 and to2 */
225 offset
.x
= vector
.x
* style_data
->to_offset
;
226 offset
.y
= vector
.y
* style_data
->to_offset
;
227 cairo_matrix_transform_distance (&device2user
, &offset
.x
, &offset
.y
);
229 to1
.x
= arrow1
.x
+ offset
.x
;
230 to1
.y
= arrow1
.y
+ offset
.y
;
231 to2
.x
= arrow2
.x
+ offset
.x
;
232 to2
.y
= arrow2
.y
+ offset
.y
;
234 /* Set vector to the director of the baseline */
235 offset
.x
= arrow2
.x
- arrow1
.x
;
236 offset
.y
= arrow2
.y
- arrow1
.y
;
237 cpml_vector_from_pair (&vector
, &offset
);
239 /* Update the AdgDim cache contents */
240 dim
->priv
->quote_org
.x
= (arrow1
.x
+ arrow2
.x
) / 2.;
241 dim
->priv
->quote_org
.y
= (arrow1
.y
+ arrow2
.y
) / 2.;
242 cpml_pair_angle (NULL
, &vector
, &dim
->priv
->quote_angle
);
244 /* Calculate baseline1 and baseline2 */
245 g_object_get (style_data
->arrow_style
, "margin", &offset
.y
, NULL
);
246 offset
.x
= vector
.x
* offset
.y
;
247 offset
.y
*= vector
.y
;
248 cairo_matrix_transform_distance (&device2user
, &offset
.x
, &offset
.y
);
250 baseline1
.x
= arrow1
.x
+ offset
.x
;
251 baseline1
.y
= arrow1
.y
+ offset
.y
;
252 baseline2
.x
= arrow2
.x
- offset
.x
;
253 baseline2
.y
= arrow2
.y
- offset
.y
;
256 if (ldim
->priv
->extension1
.data
== NULL
)
257 ldim
->priv
->extension1
.data
= g_new (cairo_path_data_t
, 4);
259 path_data
= ldim
->priv
->extension1
.data
;
260 path_data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
261 path_data
[0].header
.length
= 2;
262 path_data
[1].point
.x
= from1
.x
;
263 path_data
[1].point
.y
= from1
.y
;
264 path_data
[2].header
.type
= CAIRO_PATH_LINE_TO
;
265 path_data
[2].header
.length
= 2;
266 path_data
[3].point
.x
= to1
.x
;
267 path_data
[3].point
.y
= to1
.y
;
270 if (ldim
->priv
->extension2
.data
== NULL
)
271 ldim
->priv
->extension2
.data
= g_new (cairo_path_data_t
, 4);
273 path_data
= ldim
->priv
->extension2
.data
;
274 path_data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
275 path_data
[0].header
.length
= 2;
276 path_data
[1].point
.x
= from2
.x
;
277 path_data
[1].point
.y
= from2
.y
;
278 path_data
[2].header
.type
= CAIRO_PATH_LINE_TO
;
279 path_data
[2].header
.length
= 2;
280 path_data
[3].point
.x
= to2
.x
;
281 path_data
[3].point
.y
= to2
.y
;
284 if (ldim
->priv
->arrow_path
.data
== NULL
)
285 ldim
->priv
->arrow_path
.data
= g_new (cairo_path_data_t
, 4);
287 path_data
= ldim
->priv
->arrow_path
.data
;
288 path_data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
289 path_data
[0].header
.length
= 2;
290 path_data
[1].point
.x
= arrow1
.x
;
291 path_data
[1].point
.y
= arrow1
.y
;
292 path_data
[2].header
.type
= CAIRO_PATH_LINE_TO
;
293 path_data
[2].header
.length
= 2;
294 path_data
[3].point
.x
= arrow2
.x
;
295 path_data
[3].point
.y
= arrow2
.y
;
298 if (ldim
->priv
->baseline
.data
== NULL
)
299 ldim
->priv
->baseline
.data
= g_new (cairo_path_data_t
, 4);
301 path_data
= ldim
->priv
->baseline
.data
;
302 path_data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
303 path_data
[0].header
.length
= 2;
304 path_data
[1].point
.x
= baseline1
.x
;
305 path_data
[1].point
.y
= baseline1
.y
;
306 path_data
[2].header
.type
= CAIRO_PATH_LINE_TO
;
307 path_data
[2].header
.length
= 2;
308 path_data
[3].point
.x
= baseline2
.x
;
309 path_data
[3].point
.y
= baseline2
.y
;
313 render (AdgEntity
*entity
,
318 AdgDimStylePrivate
*style_data
;
319 AdgArrowStyle
*arrow_style
;
322 g_return_if_fail (((AdgDim
*) entity
)->priv
->dim_style
!= NULL
);
324 dim
= (AdgDim
*) entity
;
325 ldim
= (AdgLDim
*) entity
;
326 style_data
= dim
->priv
->dim_style
->priv
;
328 arrow_style
= (AdgArrowStyle
*) style_data
->arrow_style
;
331 if (!adg_entity_model_applied (entity)) */
337 if (cpml_path_from_cairo (&primitive
, &ldim
->priv
->arrow_path
, NULL
))
339 adg_arrow_style_render (arrow_style
, cr
, &primitive
);
340 if (cpml_primitive_reverse (&primitive
))
341 adg_arrow_style_render (arrow_style
, cr
, &primitive
);
345 adg_style_apply (style_data
->line_style
, cr
);
347 cairo_append_path (cr
, &ldim
->priv
->extension1
);
348 cairo_append_path (cr
, &ldim
->priv
->extension2
);
349 cairo_append_path (cr
, &ldim
->priv
->baseline
);
353 _adg_dim_render_quote (dim
, cr
);
357 ((AdgEntityClass
*) PARENT_CLASS
)->render (entity
, cr
);
361 default_quote (AdgDim
*dim
)
365 if (!cpml_pair_distance(&dim
->priv
->pos2
, &dim
->priv
->pos1
, &number
))
368 return g_strdup_printf (dim
->priv
->dim_style
->priv
->number_format
, number
);
375 * Creates a new - unreferenced - linear dimension. You must, at least, define
376 * the reference points with adg_dim_set_ref(), the dimension direction with
377 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
378 * or, better, adg_ldim_set_pos().
380 * Return value: the new entity
385 return (AdgEntity
*) g_object_new (ADG_TYPE_LDIM
, NULL
);
390 * @ref1: the first reference point
391 * @ref2: the second reference point
392 * @direction: angle where to extend the dimension
393 * @pos: the position reference
395 * Creates a new linear dimension, specifing all the needed properties in
398 * Return value: the new entity
401 adg_ldim_new_full (const AdgPair
*ref1
,
406 AdgEntity
*entity
= (AdgEntity
*) g_object_new (ADG_TYPE_LDIM
,
409 "direction", direction
,
411 adg_ldim_set_pos ((AdgLDim
*) entity
, pos
);
416 * adg_ldim_new_full_explicit:
417 * @ref1_x: the x coordinate of the first reference point
418 * @ref1_y: the y coordinate of the first reference point
419 * @ref2_x: the x coordinate of the second reference point
420 * @ref2_y: the y coordinate of the second reference point
421 * @direction: angle where to extend the dimension
422 * @pos_x: the x coordinate of the position reference
423 * @pos_y: the y coordinate of the position reference
425 * Wrappes adg_ldim_new_full() with explicit quotes.
427 * Return value: the new entity
430 adg_ldim_new_full_explicit (double ref1_x
,
449 return adg_ldim_new_full (&ref1
, &ref2
, direction
, &pos
);
456 * @ldim: an #AdgLDim entity
457 * @pos: an #AdgPair structure
459 * Sets the position references (pos1 and pos2 properties) of @ldim using a
460 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
461 * points and the direction. If these conditions are not met, an error message
462 * is logged and the position references will not be set.
465 adg_ldim_set_pos (AdgLDim
*ldim
,
470 CpmlPair extension_vector
;
471 CpmlPair baseline_vector
;
474 g_return_if_fail (ADG_IS_LDIM (ldim
));
476 dim
= (AdgDim
*) ldim
;
477 object
= (GObject
*) ldim
;
479 cpml_vector_from_angle (&extension_vector
, ldim
->priv
->direction
);
481 baseline_vector
.x
= -extension_vector
.y
;
482 baseline_vector
.y
= extension_vector
.x
;
484 d
= extension_vector
.y
* baseline_vector
.x
-
485 extension_vector
.x
* baseline_vector
.y
;
486 g_return_if_fail (d
!= 0.);
488 k
= ((pos
->y
- dim
->priv
->ref1
.y
) * baseline_vector
.x
-
489 (pos
->x
- dim
->priv
->ref1
.x
) * baseline_vector
.y
) / d
;
490 dim
->priv
->pos1
.x
= dim
->priv
->ref1
.x
+ k
*extension_vector
.x
;
491 dim
->priv
->pos1
.y
= dim
->priv
->ref1
.y
+ k
*extension_vector
.y
;
493 k
= ((pos
->y
- dim
->priv
->ref2
.y
) * baseline_vector
.x
-
494 (pos
->x
- dim
->priv
->ref2
.x
) * baseline_vector
.y
) / d
;
495 dim
->priv
->pos2
.x
= dim
->priv
->ref2
.x
+ k
*extension_vector
.x
;
496 dim
->priv
->pos2
.y
= dim
->priv
->ref2
.y
+ k
*extension_vector
.y
;
498 g_object_freeze_notify (object
);
499 g_object_notify (object
, "pos1");
500 g_object_notify (object
, "pos2");
501 g_object_thaw_notify (object
);
505 * adg_ldim_set_pos_explicit:
506 * @ldim: an #AdgLDim entity
507 * @pos_x: the new x coordinate position reference
508 * @pos_y: the new y coordinate position reference
510 * Wrappers adg_ldim_set_pos() with explicit coordinates.
513 adg_ldim_set_pos_explicit (AdgLDim
*ldim
,
522 adg_ldim_set_pos (ldim
, &pos
);
526 * adg_ldim_get_direction:
527 * @ldim: an #AdgLDim entity
529 * Gets the direction where @ldim will extend.
531 * Return value: the direction angle in radians
534 adg_ldim_get_direction (AdgLDim
*ldim
)
536 g_return_val_if_fail (ADG_IS_LDIM (ldim
), 0.);
538 return ldim
->priv
->direction
;
542 * adg_ldim_set_direction:
543 * @ldim: an #AdgLDim entity
544 * @direction: an angle value, in radians
546 * Sets the direction angle where to extend @ldim.
549 adg_ldim_set_direction (AdgLDim
*ldim
,
552 g_return_if_fail (ADG_IS_LDIM (ldim
));
554 ldim
->priv
->direction
= direction
;
555 g_object_notify ((GObject
*) ldim
, "direction");