1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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.
23 * @short_description: Linear dimensions
25 * The #AdgRDim entity represents a linear dimension.
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
36 #include "adg-internal.h"
37 #include "adg-container.h"
38 #include "adg-alignment.h"
39 #include "adg-model.h"
40 #include "adg-point.h"
41 #include "adg-trail.h"
42 #include "adg-marker.h"
43 #include "adg-style.h"
44 #include "adg-dim-style.h"
45 #include "adg-textual.h"
46 #include "adg-toy-text.h"
48 #include "adg-dim-private.h"
52 #include "adg-rdim-private.h"
55 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_rdim_parent_class)
56 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_rdim_parent_class)
59 G_DEFINE_TYPE(AdgRDim
, adg_rdim
, ADG_TYPE_DIM
)
67 static void _adg_dispose (GObject
*object
);
68 static void _adg_get_property (GObject
*object
,
72 static void _adg_set_property (GObject
*object
,
76 static void _adg_global_changed (AdgEntity
*entity
);
77 static void _adg_local_changed (AdgEntity
*entity
);
78 static void _adg_invalidate (AdgEntity
*entity
);
79 static void _adg_arrange (AdgEntity
*entity
);
80 static void _adg_render (AdgEntity
*entity
,
82 static gchar
* _adg_default_value (AdgDim
*dim
);
83 static gboolean
_adg_update_geometry (AdgRDim
*rdim
);
84 static void _adg_update_entities (AdgRDim
*rdim
);
85 static void _adg_clear_trail (AdgRDim
*rdim
);
86 static void _adg_dispose_marker (AdgRDim
*rdim
);
87 static CpmlPath
* _adg_trail_callback (AdgTrail
*trail
,
92 adg_rdim_class_init(AdgRDimClass
*klass
)
94 GObjectClass
*gobject_class
;
95 AdgEntityClass
*entity_class
;
96 AdgDimClass
*dim_class
;
97 GParamSpec
*param
, *old_param
;
99 gobject_class
= (GObjectClass
*) klass
;
100 entity_class
= (AdgEntityClass
*) klass
;
101 dim_class
= (AdgDimClass
*) klass
;
103 g_type_class_add_private(klass
, sizeof(AdgRDimPrivate
));
105 gobject_class
->dispose
= _adg_dispose
;
106 gobject_class
->get_property
= _adg_get_property
;
107 gobject_class
->set_property
= _adg_set_property
;
109 entity_class
->global_changed
= _adg_global_changed
;
110 entity_class
->local_changed
= _adg_local_changed
;
111 entity_class
->invalidate
= _adg_invalidate
;
112 entity_class
->arrange
= _adg_arrange
;
113 entity_class
->render
= _adg_render
;
115 dim_class
->default_value
= _adg_default_value
;
117 /* Override #AdgDim:value to prepend "R "
118 * to the default set value template string */
119 old_param
= g_object_class_find_property(gobject_class
, "value");
120 param
= g_param_spec_string(old_param
->name
,
121 g_param_spec_get_nick(old_param
),
122 g_param_spec_get_blurb(old_param
),
125 g_object_class_install_property(gobject_class
, PROP_VALUE
, param
);
129 adg_rdim_init(AdgRDim
*rdim
)
131 AdgRDimPrivate
*data
;
132 cairo_path_data_t move_to
, line_to
;
134 data
= G_TYPE_INSTANCE_GET_PRIVATE(rdim
, ADG_TYPE_RDIM
, AdgRDimPrivate
);
135 move_to
.header
.type
= CPML_MOVE
;
136 move_to
.header
.length
= 2;
137 line_to
.header
.type
= CPML_LINE
;
138 line_to
.header
.length
= 2;
142 data
->geometry_arranged
= FALSE
;
144 data
->shift
.base
.x
= data
->shift
.base
.y
= 0;
145 cairo_matrix_init_identity(&data
->quote
.global_map
);
147 data
->cpml
.path
.status
= CAIRO_STATUS_INVALID_PATH_DATA
;
148 data
->cpml
.path
.data
= data
->cpml
.data
;
149 data
->cpml
.path
.num_data
= G_N_ELEMENTS(data
->cpml
.data
);
150 data
->cpml
.path
.data
[0] = move_to
;
151 data
->cpml
.path
.data
[2] = line_to
;
152 data
->cpml
.path
.data
[4] = move_to
;
153 data
->cpml
.path
.data
[6] = line_to
;
159 _adg_dispose(GObject
*object
)
161 _adg_dispose_marker((AdgRDim
*) object
);
163 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
164 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
168 _adg_get_property(GObject
*object
, guint prop_id
,
169 GValue
*value
, GParamSpec
*pspec
)
173 g_value_set_string(value
, adg_dim_get_value((AdgDim
*) object
));
176 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
182 _adg_set_property(GObject
*object
, guint prop_id
,
183 const GValue
*value
, GParamSpec
*pspec
)
187 adg_dim_set_value((AdgDim
*) object
, g_value_get_string(value
));
190 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
199 * Creates a new uninitialized radial dimension. To be useful, you
200 * need at least define the center of the arc to quote in #AdgDim:ref1,
201 * a point on the arc in #AdgDim:ref2 and the position of the quote
202 * in #AdgDim:pos using any valid #AdgDim method.
204 * Returns: a newly created quote
209 return g_object_new(ADG_TYPE_RDIM
, NULL
);
214 * @center: center of the arc to quote
215 * @radius: where the quote must be applied on the arc
216 * @pos: position of the quote text
218 * Creates a new quote by specifying explicitely all the needed
219 * data to get a valid quote.
221 * Returns: a newly created quote
224 adg_rdim_new_full(const AdgPair
*center
, const AdgPair
*radius
,
230 rdim
= adg_rdim_new();
231 dim
= (AdgDim
*) rdim
;
233 adg_dim_set_ref1_from_pair(dim
, center
);
234 adg_dim_set_ref2_from_pair(dim
, radius
);
235 adg_dim_set_pos_from_pair(dim
, pos
);
241 * adg_rdim_new_full_explicit:
242 * @center_x: x coordinate of the center of the arc to quote
243 * @center_y: y coordinate of the center of the arc to quote
244 * @radius_x: x coordiante where the quote must be applied on the arc
245 * @radius_y: y coordiante where the quote must be applied on the arc
246 * @pos_x: x coordinate of the quote text
247 * @pos_y: y coordinate of the quote text
249 * Does the same job of adg_rdim_full() but using specific coordinates
250 * instead of AdgPair structures.
251 * data to get a valid quote.
253 * Returns: a newly created quote
256 adg_rdim_new_full_explicit(gdouble center_x
, gdouble center_y
,
257 gdouble radius_x
, gdouble radius_y
,
258 gdouble pos_x
, gdouble pos_y
)
260 AdgPair center
, radius
, pos
;
269 return adg_rdim_new_full(¢er
, &radius
, &pos
);
273 * adg_rdim_new_full_from_model:
274 * @model: the model from which the named pairs are taken
275 * @center: the center point of the arc to quote
276 * @radius: an arbitrary point on the arc
277 * @pos: the position reference
279 * Creates a new radial dimension, specifing all the needed properties in
280 * one shot and using named pairs from @model.
282 * Returns: the newly created radial dimension entity
285 adg_rdim_new_full_from_model(AdgModel
*model
, const gchar
*center
,
286 const gchar
*radius
, const gchar
*pos
)
288 AdgDim
*dim
= g_object_new(ADG_TYPE_RDIM
, NULL
);
290 adg_dim_set_ref1_from_model(dim
, model
, center
);
291 adg_dim_set_ref2_from_model(dim
, model
, radius
);
292 adg_dim_set_pos_from_model(dim
, model
, pos
);
294 return (AdgRDim
*) dim
;
299 _adg_global_changed(AdgEntity
*entity
)
301 AdgRDimPrivate
*data
= ((AdgRDim
*) entity
)->data
;
303 _adg_clear_trail((AdgRDim
*) entity
);
305 if (_ADG_OLD_ENTITY_CLASS
->global_changed
)
306 _ADG_OLD_ENTITY_CLASS
->global_changed(entity
);
308 if (data
->marker
!= NULL
)
309 adg_entity_global_changed((AdgEntity
*) data
->marker
);
313 _adg_local_changed(AdgEntity
*entity
)
315 _adg_clear_trail((AdgRDim
*) entity
);
317 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
318 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
322 _adg_invalidate(AdgEntity
*entity
)
325 AdgRDimPrivate
*data
;
327 rdim
= (AdgRDim
*) entity
;
330 _adg_dispose_marker(rdim
);
331 data
->geometry_arranged
= FALSE
;
332 _adg_clear_trail(rdim
);
334 if (_ADG_OLD_ENTITY_CLASS
->invalidate
)
335 _ADG_OLD_ENTITY_CLASS
->invalidate(entity
);
339 _adg_arrange(AdgEntity
*entity
)
343 AdgRDimPrivate
*data
;
345 AdgDimStyle
*dim_style
;
347 const AdgMatrix
*local
;
348 AdgPair ref1
, ref2
, base
;
351 if (_ADG_OLD_ENTITY_CLASS
->arrange
)
352 _ADG_OLD_ENTITY_CLASS
->arrange(entity
);
354 rdim
= (AdgRDim
*) entity
;
355 dim
= (AdgDim
*) rdim
;
357 quote
= adg_dim_get_quote(dim
);
359 if (!_adg_update_geometry(rdim
))
362 _adg_update_entities(rdim
);
364 if (data
->cpml
.path
.status
== CAIRO_STATUS_SUCCESS
) {
365 AdgEntity
*quote_entity
= (AdgEntity
*) quote
;
366 adg_entity_set_global_map(quote_entity
, &data
->quote
.global_map
);
370 dim_style
= _ADG_GET_DIM_STYLE(dim
);
371 outside
= adg_dim_get_outside(dim
);
372 if (outside
== ADG_THREE_STATE_UNKNOWN
)
373 outside
= ADG_THREE_STATE_OFF
;
375 local
= adg_entity_get_local_matrix(entity
);
376 cpml_pair_copy(&ref1
, adg_point_get_pair(adg_dim_get_ref1(dim
)));
377 cpml_pair_copy(&ref2
, adg_point_get_pair(adg_dim_get_ref2(dim
)));
378 cpml_pair_copy(&base
, &data
->point
.base
);
380 cpml_pair_transform(&ref1
, local
);
381 cpml_pair_transform(&ref2
, local
);
382 cpml_pair_transform(&base
, local
);
383 base
.x
+= data
->shift
.base
.x
;
384 base
.y
+= data
->shift
.base
.y
;
387 cpml_pair_to_cairo(&base
, &data
->cpml
.data
[1]);
390 cpml_pair_to_cairo(&ref2
, &data
->cpml
.data
[3]);
393 data
->cpml
.data
[2].header
.length
= 2;
394 /* TODO: outside radius dimensions */
396 data
->cpml
.data
[2].header
.length
= 6;
399 data
->cpml
.path
.status
= CAIRO_STATUS_SUCCESS
;
402 AdgEntity
*quote_entity
;
405 quote_entity
= (AdgEntity
*) quote
;
406 cpml_pair_from_cairo(&pair
, &data
->cpml
.data
[1]);
408 adg_alignment_set_factor_explicit(quote
, 1, 0);
410 cairo_matrix_init_translate(&map
, pair
.x
, pair
.y
);
411 cairo_matrix_rotate(&map
, data
->angle
);
412 adg_entity_set_global_map(quote_entity
, &map
);
414 adg_matrix_copy(&data
->quote
.global_map
,
415 adg_entity_get_global_map(quote_entity
));
418 if (data
->marker
!= NULL
) {
419 adg_marker_set_segment(data
->marker
, data
->trail
, outside
? 2 : 1);
420 adg_entity_local_changed((AdgEntity
*) data
->marker
);
423 /* TODO: compute the extents */
427 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
431 AdgRDimPrivate
*data
;
432 AdgDimStyle
*dim_style
;
434 const cairo_path_t
*cairo_path
;
436 rdim
= (AdgRDim
*) entity
;
439 if (!data
->geometry_arranged
) {
440 /* Entity not arranged, probably due to undefined pair found */
444 dim
= (AdgDim
*) entity
;
445 dim_style
= _ADG_GET_DIM_STYLE(dim
);
447 adg_style_apply((AdgStyle
*) dim_style
, entity
, cr
);
448 adg_entity_render((AdgEntity
*) adg_dim_get_quote(dim
), cr
);
450 if (data
->marker
!= NULL
)
451 adg_entity_render((AdgEntity
*) data
->marker
, cr
);
453 cairo_transform(cr
, adg_entity_get_global_matrix(entity
));
454 dress
= adg_dim_style_get_line_dress(dim_style
);
455 adg_entity_apply_dress(entity
, dress
, cr
);
457 cairo_path
= adg_trail_get_cairo_path(data
->trail
);
458 cairo_append_path(cr
, cairo_path
);
463 _adg_default_value(AdgDim
*dim
)
466 AdgRDimPrivate
*data
;
467 AdgDimStyle
*dim_style
;
470 rdim
= (AdgRDim
*) dim
;
472 dim_style
= _ADG_GET_DIM_STYLE(dim
);
473 format
= adg_dim_style_get_number_format(dim_style
);
475 if (!_adg_update_geometry(rdim
))
476 return g_strdup("undef");
478 return g_strdup_printf(format
, data
->radius
);
482 _adg_update_geometry(AdgRDim
*rdim
)
484 AdgRDimPrivate
*data
;
486 AdgDimStyle
*dim_style
;
487 const AdgPair
*ref1
, *ref2
;
489 gdouble spacing
, level
, pos_distance
;
494 if (data
->geometry_arranged
)
497 dim
= (AdgDim
*) rdim
;
498 ref1
= adg_point_get_pair(adg_dim_get_ref1(dim
));
499 ref2
= adg_point_get_pair(adg_dim_get_ref2(dim
));
500 pos
= adg_point_get_pair(adg_dim_get_pos(dim
));
502 if (ref1
== NULL
|| ref2
== NULL
|| pos
== NULL
)
505 dim_style
= _ADG_GET_DIM_STYLE(rdim
);
506 spacing
= adg_dim_style_get_baseline_spacing(dim_style
);
507 level
= adg_dim_get_level(dim
);
508 pos_distance
= cpml_pair_distance(pos
, ref1
);
509 vector
.x
= ref2
->x
- ref1
->x
;
510 vector
.y
= ref2
->y
- ref1
->y
;
512 if (cpml_pair_squared_distance(pos
, ref1
) <
513 cpml_pair_squared_distance(pos
, ref2
)) {
514 vector
.x
= -vector
.x
;
515 vector
.y
= -vector
.y
;
519 data
->radius
= cpml_pair_distance(&vector
, NULL
);
522 data
->angle
= adg_dim_quote_angle(dim
, cpml_vector_angle(&vector
));
525 cpml_pair_copy(&data
->point
.base
, &vector
);
526 cpml_vector_set_length(&data
->point
.base
, pos_distance
);
527 data
->point
.base
.x
+= ref1
->x
;
528 data
->point
.base
.y
+= ref1
->y
;
531 cpml_pair_copy(&data
->shift
.base
, &vector
);
532 cpml_vector_set_length(&data
->shift
.base
, spacing
* level
);
534 data
->geometry_arranged
= TRUE
;
540 _adg_update_entities(AdgRDim
*rdim
)
543 AdgRDimPrivate
*data
;
544 AdgDimStyle
*dim_style
;
546 entity
= (AdgEntity
*) rdim
;
548 dim_style
= _ADG_GET_DIM_STYLE(rdim
);
550 if (data
->trail
== NULL
)
551 data
->trail
= adg_trail_new(_adg_trail_callback
, rdim
);
553 if (data
->marker
== NULL
) {
554 data
->marker
= adg_dim_style_marker2_new(dim_style
);
555 adg_entity_set_parent((AdgEntity
*) data
->marker
, entity
);
560 _adg_clear_trail(AdgRDim
*rdim
)
562 AdgRDimPrivate
*data
= rdim
->data
;
564 if (data
->trail
!= NULL
)
565 adg_model_clear((AdgModel
*) data
->trail
);
567 data
->cpml
.path
.status
= CAIRO_STATUS_INVALID_PATH_DATA
;
571 _adg_dispose_marker(AdgRDim
*rdim
)
573 AdgRDimPrivate
*data
= rdim
->data
;
575 if (data
->trail
!= NULL
) {
576 g_object_unref(data
->trail
);
580 if (data
->marker
!= NULL
) {
581 g_object_unref(data
->marker
);
587 _adg_trail_callback(AdgTrail
*trail
, gpointer user_data
)
590 AdgRDimPrivate
*data
;
592 rdim
= (AdgRDim
*) user_data
;
595 return &data
->cpml
.path
;