[AdgStroke] Using adg_entity_apply_local_matrix()
[adg.git] / adg / adg-ldim.c
blob071a051c036c53f8a7cc06c35ad9ac5101628efc
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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-ldim
23 * @short_description: Linear dimensions
25 * The #AdgLDim entity represents a linear dimension.
28 /**
29 * AdgLDim:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-ldim.h"
37 #include "adg-ldim-private.h"
38 #include "adg-dim-style.h"
39 #include "adg-container.h"
40 #include "adg-intl.h"
42 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
45 enum {
46 PROP_0,
47 PROP_DIRECTION
51 static void get_property (GObject *object,
52 guint param_id,
53 GValue *value,
54 GParamSpec *pspec);
55 static void set_property (GObject *object,
56 guint param_id,
57 const GValue *value,
58 GParamSpec *pspec);
59 static gboolean invalidate (AdgEntity *entity);
60 static gboolean render (AdgEntity *entity,
61 cairo_t *cr);
62 static gchar * default_value (AdgDim *dim);
63 static void update (AdgLDim *ldim);
64 static void clear (AdgLDim *ldim);
67 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
70 static void
71 adg_ldim_class_init(AdgLDimClass *klass)
73 GObjectClass *gobject_class;
74 AdgEntityClass *entity_class;
75 AdgDimClass *dim_class;
76 GParamSpec *param;
78 gobject_class = (GObjectClass *) klass;
79 entity_class = (AdgEntityClass *) klass;
80 dim_class = (AdgDimClass *) klass;
82 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
84 gobject_class->get_property = get_property;
85 gobject_class->set_property = set_property;
87 entity_class->invalidate = invalidate;
88 entity_class->render = render;
90 dim_class->default_value = default_value;
92 param = g_param_spec_double("direction",
93 P_("Direction"),
94 P_("The inclination angle of the extension lines"),
95 -G_MAXDOUBLE, G_MAXDOUBLE, ADG_DIR_RIGHT,
96 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
97 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
100 static void
101 adg_ldim_init(AdgLDim *ldim)
103 AdgLDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM,
104 AdgLDimPrivate);
106 data->direction = ADG_DIR_RIGHT;
108 data->path.status = CAIRO_STATUS_INVALID_PATH_DATA;
109 data->path.data = data->path_data;
110 data->path.num_data = 12;
112 data->path_data[0].header.type = CAIRO_PATH_MOVE_TO;
113 data->path_data[0].header.length = 2;
114 data->path_data[2].header.type = CAIRO_PATH_LINE_TO;
115 data->path_data[2].header.length = 2;
116 data->path_data[4].header.type = CAIRO_PATH_MOVE_TO;
117 data->path_data[4].header.length = 2;
118 data->path_data[6].header.type = CAIRO_PATH_LINE_TO;
119 data->path_data[6].header.length = 2;
120 data->path_data[8].header.type = CAIRO_PATH_MOVE_TO;
121 data->path_data[8].header.length = 2;
122 data->path_data[10].header.type = CAIRO_PATH_LINE_TO;
123 data->path_data[10].header.length = 2;
125 data->director.data = data->director_data;
126 data->director.num_data = 4;
127 data->director.path = NULL;
129 data->director_data[0].header.type = CAIRO_PATH_MOVE_TO;
130 data->director_data[0].header.length = 2;
131 data->director_data[2].header.type = CAIRO_PATH_LINE_TO;
132 data->director_data[2].header.length = 2;
134 ldim->data = data;
137 static void
138 get_property(GObject *object,
139 guint prop_id, GValue *value, GParamSpec *pspec)
141 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
143 switch (prop_id) {
144 case PROP_DIRECTION:
145 g_value_set_double(value, data->direction);
146 break;
147 default:
148 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
149 break;
153 static void
154 set_property(GObject *object,
155 guint prop_id, const GValue *value, GParamSpec *pspec)
157 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
159 switch (prop_id) {
160 case PROP_DIRECTION:
161 data->direction = g_value_get_double(value);
162 break;
163 default:
164 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
165 break;
171 * adg_ldim_new:
173 * Creates a new - unreferenced - linear dimension. You must, at least, define
174 * the reference points with adg_dim_set_ref(), the dimension direction with
175 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
176 * or, better, adg_ldim_set_pos().
178 * Returns: the new entity
180 AdgEntity *
181 adg_ldim_new(void)
183 return (AdgEntity *) g_object_new(ADG_TYPE_LDIM, NULL);
187 * adg_ldim_new_full:
188 * @ref1: the first reference point
189 * @ref2: the second reference point
190 * @direction: angle where to extend the dimension
191 * @pos: the position reference
193 * Creates a new linear dimension, specifing all the needed properties in
194 * one shot.
196 * Returns: the new entity
198 AdgEntity *
199 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
200 gdouble direction, const AdgPair *pos)
202 AdgEntity *entity = (AdgEntity *) g_object_new(ADG_TYPE_LDIM,
203 "ref1", ref1,
204 "ref2", ref2,
205 "direction", direction,
206 NULL);
207 adg_ldim_set_pos((AdgLDim *) entity, pos);
208 return entity;
212 * adg_ldim_new_full_explicit:
213 * @ref1_x: the x coordinate of the first reference point
214 * @ref1_y: the y coordinate of the first reference point
215 * @ref2_x: the x coordinate of the second reference point
216 * @ref2_y: the y coordinate of the second reference point
217 * @direction: angle where to extend the dimension
218 * @pos_x: the x coordinate of the position reference
219 * @pos_y: the y coordinate of the position reference
221 * Wrappes adg_ldim_new_full() with explicit values.
223 * Returns: the new entity
225 AdgEntity *
226 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
227 gdouble ref2_x, gdouble ref2_y,
228 gdouble direction, gdouble pos_x, gdouble pos_y)
230 AdgPair ref1;
231 AdgPair ref2;
232 AdgPair pos;
234 ref1.x = ref1_x;
235 ref1.y = ref1_y;
236 ref2.x = ref2_x;
237 ref2.y = ref2_y;
238 pos.x = pos_x;
239 pos.y = pos_y;
241 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
245 * adg_ldim_set_pos:
246 * @ldim: an #AdgLDim entity
247 * @pos: an #AdgPair structure
249 * Sets the position references (pos1 and pos2 properties) of @ldim using a
250 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
251 * points and the direction. If these conditions are not met, an error message
252 * is logged and the position references will not be set.
254 void
255 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
257 AdgLDimPrivate *data;
258 const AdgPair *ref1, *ref2;
259 AdgPair pos1, pos2;
260 CpmlPair baseline_vector, extension_vector;
261 gdouble d, k;
263 g_return_if_fail(ADG_IS_LDIM(ldim));
265 data = ldim->data;
266 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
267 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
269 cpml_vector_from_angle(&extension_vector, data->direction, 1);
271 baseline_vector.x = -extension_vector.y;
272 baseline_vector.y = extension_vector.x;
274 d = extension_vector.y * baseline_vector.x -
275 extension_vector.x * baseline_vector.y;
276 g_return_if_fail(d != 0);
278 k = ((pos->y - ref1->y) * baseline_vector.x -
279 (pos->x - ref1->x) * baseline_vector.y) / d;
280 pos1.x = ref1->x + k * extension_vector.x;
281 pos1.y = ref1->y + k * extension_vector.y;
283 k = ((pos->y - ref2->y) * baseline_vector.x -
284 (pos->x - ref2->x) * baseline_vector.y) / d;
285 pos2.x = ref2->x + k * extension_vector.x;
286 pos2.y = ref2->y + k * extension_vector.y;
288 adg_dim_set_pos((AdgDim *) ldim, &pos1, &pos2);
292 * adg_ldim_set_pos_explicit:
293 * @ldim: an #AdgLDim entity
294 * @pos_x: the new x coordinate position reference
295 * @pos_y: the new y coordinate position reference
297 * Wrappers adg_ldim_set_pos() with explicit coordinates.
299 void
300 adg_ldim_set_pos_explicit(AdgLDim *ldim, gdouble pos_x, gdouble pos_y)
302 AdgPair pos;
304 pos.x = pos_x;
305 pos.y = pos_y;
307 adg_ldim_set_pos(ldim, &pos);
311 * adg_ldim_get_direction:
312 * @ldim: an #AdgLDim entity
314 * Gets the direction where @ldim will extend.
316 * Returns: the direction angle in radians
318 gdouble
319 adg_ldim_get_direction(AdgLDim *ldim)
321 AdgLDimPrivate *data;
323 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
325 data = ldim->data;
327 return data->direction;
331 * adg_ldim_set_direction:
332 * @ldim: an #AdgLDim entity
333 * @direction: an angle value, in radians
335 * Sets the direction angle where to extend @ldim.
337 void
338 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
340 AdgLDimPrivate *data;
342 g_return_if_fail(ADG_IS_LDIM(ldim));
344 data = ldim->data;
345 data->direction = direction;
347 g_object_notify((GObject *) ldim, "direction");
351 static gboolean
352 invalidate(AdgEntity *entity)
354 clear((AdgLDim *) entity);
355 return PARENT_ENTITY_CLASS->invalidate(entity);
358 static gboolean
359 render(AdgEntity *entity, cairo_t *cr)
361 AdgLDim *ldim;
362 AdgLDimPrivate *data;
363 AdgStyle *dim_style;
364 AdgStyle *arrow_style;
365 AdgStyle *line_style;
366 AdgMatrix local;
368 ldim = (AdgLDim *) entity;
369 data = ldim->data;
370 dim_style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
371 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
372 line_style = adg_dim_style_get_line_style((AdgDimStyle *) dim_style);
373 adg_entity_get_local_matrix(entity, &local);
375 update(ldim);
376 adg_entity_apply(entity, ADG_SLOT_DIM_STYLE, cr);
378 cairo_save(cr);
379 cairo_transform(cr, &local);
380 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &data->director);
381 cpml_segment_reverse(&data->director);
382 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &data->director);
383 cpml_segment_reverse(&data->director);
384 cairo_append_path(cr, &data->path);
385 cairo_restore(cr);
387 adg_style_apply(line_style, cr);
388 cairo_stroke(cr);
390 adg_dim_render_quote((AdgDim *) ldim, cr);
392 return TRUE;
395 static gchar *
396 default_value(AdgDim *dim)
398 const AdgPair *pos1, *pos2;
399 AdgStyle *dim_style;
400 gdouble distance;
401 const gchar *format;
403 pos1 = adg_dim_get_pos1(dim);
404 pos2 = adg_dim_get_pos2(dim);
405 distance = cpml_pair_distance(pos1, pos2);
406 dim_style = adg_entity_get_style((AdgEntity *) dim, ADG_SLOT_DIM_STYLE);
407 format = adg_dim_style_get_number_format((AdgDimStyle *) dim_style);
409 return g_strdup_printf(format, distance);
412 static void
413 update(AdgLDim *ldim)
415 AdgLDimPrivate *data;
416 AdgStyle *dim_style;
417 AdgStyle *arrow_style;
418 const AdgPair *ref1, *ref2, *pos1, *pos2;
419 gdouble level;
420 gdouble baseline_spacing, from_offset;
421 cairo_path_data_t *baseline1, *baseline2;
422 cairo_path_data_t *from1, *to1, *from2, *to2;
423 cairo_path_data_t *arrow1, *arrow2;
424 AdgMatrix unlocal;
425 CpmlPair vector;
426 AdgPair offset;
427 AdgPair quote_org;
428 gdouble angle;
430 data = ldim->data;
431 dim_style = adg_entity_get_style((AdgEntity *) ldim, ADG_SLOT_DIM_STYLE);
432 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
434 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
435 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
436 pos1 = adg_dim_get_pos1((AdgDim *) ldim);
437 pos2 = adg_dim_get_pos2((AdgDim *) ldim);
438 level = adg_dim_get_level((AdgDim *) ldim);
440 baseline_spacing = adg_dim_style_get_baseline_spacing((AdgDimStyle *) dim_style);
441 from_offset = adg_dim_style_get_from_offset((AdgDimStyle *) dim_style);
443 baseline1 = data->path_data + 1;
444 baseline2 = data->path_data + 3;
445 from1 = data->path_data + 5;
446 to1 = data->path_data + 7;
447 from2 = data->path_data + 9;
448 to2 = data->path_data + 11;
449 arrow1 = data->director_data + 1;
450 arrow2 = data->director_data + 3;
452 /* Get the inverted local matrix */
453 adg_entity_get_local_matrix((AdgEntity *) ldim, &unlocal);
454 if (cairo_matrix_invert(&unlocal) != CAIRO_STATUS_SUCCESS)
455 return;
457 /* Set vector to the director of the extension lines */
458 cpml_vector_from_angle(&vector, data->direction, 1);
460 /* Calculate from1 and from2 */
461 offset.x = vector.x * from_offset;
462 offset.y = vector.y * from_offset;
463 cairo_matrix_transform_distance(&unlocal, &offset.x, &offset.y);
465 from1->point.x = ref1->x + offset.x;
466 from1->point.y = ref1->y + offset.y;
467 from2->point.x = ref2->x + offset.x;
468 from2->point.y = ref2->y + offset.y;
470 /* Calculate arrow1 and arrow2 */
471 offset.x = vector.x * baseline_spacing * level;
472 offset.y = vector.y * baseline_spacing * level;
473 cairo_matrix_transform_distance(&unlocal, &offset.x, &offset.y);
475 arrow1->point.x = pos1->x + offset.x;
476 arrow1->point.y = pos1->y + offset.y;
477 arrow2->point.x = pos2->x + offset.x;
478 arrow2->point.y = pos2->y + offset.y;
480 /* Calculate to1 and to2 */
481 offset.x = vector.x * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
482 offset.y = vector.y * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
483 cairo_matrix_transform_distance(&unlocal, &offset.x, &offset.y);
485 to1->point.x = arrow1->point.x + offset.x;
486 to1->point.y = arrow1->point.y + offset.y;
487 to2->point.x = arrow2->point.x + offset.x;
488 to2->point.y = arrow2->point.y + offset.y;
490 /* Set vector to the director of the baseline */
491 offset.x = arrow2->point.x - arrow1->point.x;
492 offset.y = arrow2->point.y - arrow1->point.y;
493 cpml_vector_set_length(cpml_pair_copy(&vector, &offset), 1);
495 /* Update the AdgDim cache contents */
497 quote_org.x = (arrow1->point.x + arrow2->point.x) / 2;
498 quote_org.y = (arrow1->point.y + arrow2->point.y) / 2;
499 adg_dim_set_org((AdgDim *) ldim, &quote_org);
500 angle = cpml_vector_angle(&vector);
501 adg_dim_set_angle((AdgDim *) ldim, angle);
503 /* Calculate baseline1 and baseline2 */
504 offset.y = adg_arrow_style_get_margin((AdgArrowStyle *) arrow_style);
505 offset.x = vector.x * offset.y;
506 offset.y *= vector.y;
507 cairo_matrix_transform_distance(&unlocal, &offset.x, &offset.y);
509 baseline1->point.x = arrow1->point.x + offset.x;
510 baseline1->point.y = arrow1->point.y + offset.y;
511 baseline2->point.x = arrow2->point.x - offset.x;
512 baseline2->point.y = arrow2->point.y - offset.y;
514 data->path.status = CAIRO_STATUS_SUCCESS;
517 static void
518 clear(AdgLDim *ldim)
520 AdgLDimPrivate *data = ldim->data;
522 data->path.status = CAIRO_STATUS_INVALID_PATH_DATA;