Updated "GNU Library General License" to "GNU Lesser General License
[adg.git] / adg / adg-ldim.c
blobd120aa51ca60f5559096a9795b3ea0e43a0266af
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 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.
20 /**
21 * SECTION:ldim
22 * @title: AdgLDim
23 * @short_description: Linear dimensions
25 * The #AdgLDim entity represents a linear dimension.
28 #include "adg-ldim.h"
29 #include "adg-ldim-private.h"
30 #include "adg-container.h"
31 #include "adg-util.h"
32 #include "adg-intl.h"
33 #include <gcontainer/gcontainer.h>
35 #define PARENT_CLASS ((AdgDimClass *) adg_ldim_parent_class)
36 #define ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
39 enum {
40 PROP_0,
41 PROP_DIRECTION
45 static void get_property (GObject *object,
46 guint param_id,
47 GValue *value,
48 GParamSpec *pspec);
49 static void set_property (GObject *object,
50 guint param_id,
51 const GValue *value,
52 GParamSpec *pspec);
53 static void model_matrix_changed (AdgEntity *entity,
54 AdgMatrix *parent_matrix);
55 static void paper_matrix_changed (AdgEntity *entity,
56 AdgMatrix *parent_matrix);
57 static void invalidate (AdgEntity *entity);
58 static void render (AdgEntity *entity,
59 cairo_t *cr);
60 static gchar * default_quote (AdgDim *dim);
61 static void update (AdgLDim *ldim);
62 static void clear (AdgLDim *ldim);
65 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
68 static void
69 adg_ldim_class_init(AdgLDimClass *klass)
71 GObjectClass *gobject_class;
72 AdgEntityClass *entity_class;
73 AdgDimClass *dim_class;
74 GParamSpec *param;
76 gobject_class = (GObjectClass *) klass;
77 entity_class = (AdgEntityClass *) klass;
78 dim_class = (AdgDimClass *) klass;
80 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
82 gobject_class->get_property = get_property;
83 gobject_class->set_property = set_property;
85 entity_class->model_matrix_changed = model_matrix_changed;
86 entity_class->paper_matrix_changed = paper_matrix_changed;
87 entity_class->invalidate = invalidate;
88 entity_class->render = render;
90 dim_class->default_quote = default_quote;
92 param = g_param_spec_double("direction",
93 P_("Direction"),
94 P_("The inclination angle of the extension lines"),
95 -G_MAXDOUBLE, G_MAXDOUBLE, CPML_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 *priv = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM,
104 AdgLDimPrivate);
106 priv->direction = CPML_DIR_RIGHT;
108 priv->path.status = CAIRO_STATUS_INVALID_PATH_DATA;
109 priv->path.data = priv->path_data;
110 priv->path.num_data = 12;
112 priv->path_data[0].header.type = CAIRO_PATH_MOVE_TO;
113 priv->path_data[0].header.length = 2;
114 priv->path_data[2].header.type = CAIRO_PATH_LINE_TO;
115 priv->path_data[2].header.length = 2;
116 priv->path_data[4].header.type = CAIRO_PATH_MOVE_TO;
117 priv->path_data[4].header.length = 2;
118 priv->path_data[6].header.type = CAIRO_PATH_LINE_TO;
119 priv->path_data[6].header.length = 2;
120 priv->path_data[8].header.type = CAIRO_PATH_MOVE_TO;
121 priv->path_data[8].header.length = 2;
122 priv->path_data[10].header.type = CAIRO_PATH_LINE_TO;
123 priv->path_data[10].header.length = 2;
125 priv->director.cairo_path.status = CAIRO_STATUS_SUCCESS;
126 priv->director.cairo_path.data = priv->director_data;
127 priv->director.cairo_path.num_data = 2;
128 priv->director_data[0].header.type = CAIRO_PATH_LINE_TO;
129 priv->director_data[0].header.length = 2;
131 ldim->priv = priv;
134 static void
135 get_property(GObject *object,
136 guint prop_id, GValue *value, GParamSpec *pspec)
138 AdgLDim *ldim = ADG_LDIM(object);
140 switch (prop_id) {
141 case PROP_DIRECTION:
142 g_value_set_double(value, ldim->priv->direction);
143 break;
144 default:
145 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
146 break;
150 static void
151 set_property(GObject *object,
152 guint prop_id, const GValue *value, GParamSpec *pspec)
154 AdgLDim *ldim = ADG_LDIM(object);
156 switch (prop_id) {
157 case PROP_DIRECTION:
158 ldim->priv->direction = g_value_get_double(value);
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
162 break;
168 * adg_ldim_new:
170 * Creates a new - unreferenced - linear dimension. You must, at least, define
171 * the reference points with adg_dim_set_ref(), the dimension direction with
172 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
173 * or, better, adg_ldim_set_pos().
175 * Return value: the new entity
177 AdgEntity *
178 adg_ldim_new(void)
180 return (AdgEntity *) g_object_new(ADG_TYPE_LDIM, NULL);
184 * adg_ldim_new_full:
185 * @ref1: the first reference point
186 * @ref2: the second reference point
187 * @direction: angle where to extend the dimension
188 * @pos: the position reference
190 * Creates a new linear dimension, specifing all the needed properties in
191 * one shot.
193 * Return value: the new entity
195 AdgEntity *
196 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
197 gdouble direction, const AdgPair *pos)
199 AdgEntity *entity = (AdgEntity *) g_object_new(ADG_TYPE_LDIM,
200 "ref1", ref1,
201 "ref2", ref2,
202 "direction", direction,
203 NULL);
204 adg_ldim_set_pos((AdgLDim *) entity, pos);
205 return entity;
209 * adg_ldim_new_full_explicit:
210 * @ref1_x: the x coordinate of the first reference point
211 * @ref1_y: the y coordinate of the first reference point
212 * @ref2_x: the x coordinate of the second reference point
213 * @ref2_y: the y coordinate of the second reference point
214 * @direction: angle where to extend the dimension
215 * @pos_x: the x coordinate of the position reference
216 * @pos_y: the y coordinate of the position reference
218 * Wrappes adg_ldim_new_full() with explicit quotes.
220 * Return value: the new entity
222 AdgEntity *
223 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
224 gdouble ref2_x, gdouble ref2_y,
225 gdouble direction, gdouble pos_x, gdouble pos_y)
227 AdgPair ref1;
228 AdgPair ref2;
229 AdgPair pos;
231 ref1.x = ref1_x;
232 ref1.y = ref1_y;
233 ref2.x = ref2_x;
234 ref2.y = ref2_y;
235 pos.x = pos_x;
236 pos.y = pos_y;
238 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
242 * adg_ldim_set_pos:
243 * @ldim: an #AdgLDim entity
244 * @pos: an #AdgPair structure
246 * Sets the position references (pos1 and pos2 properties) of @ldim using a
247 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
248 * points and the direction. If these conditions are not met, an error message
249 * is logged and the position references will not be set.
251 void
252 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
254 const AdgPair *ref1, *ref2;
255 AdgPair pos1, pos2;
256 CpmlPair baseline_vector, extension_vector;
257 gdouble d, k;
259 g_return_if_fail(ADG_IS_LDIM(ldim));
261 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
262 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
264 cpml_vector_from_angle(&extension_vector, ldim->priv->direction);
266 baseline_vector.x = -extension_vector.y;
267 baseline_vector.y = extension_vector.x;
269 d = extension_vector.y * baseline_vector.x -
270 extension_vector.x * baseline_vector.y;
271 g_return_if_fail(d != 0.);
273 k = ((pos->y - ref1->y) * baseline_vector.x -
274 (pos->x - ref1->x) * baseline_vector.y) / d;
275 pos1.x = ref1->x + k * extension_vector.x;
276 pos1.y = ref1->y + k * extension_vector.y;
278 k = ((pos->y - ref2->y) * baseline_vector.x -
279 (pos->x - ref2->x) * baseline_vector.y) / d;
280 pos2.x = ref2->x + k * extension_vector.x;
281 pos2.y = ref2->y + k * extension_vector.y;
283 adg_dim_set_pos((AdgDim *) ldim, &pos1, &pos2);
287 * adg_ldim_set_pos_explicit:
288 * @ldim: an #AdgLDim entity
289 * @pos_x: the new x coordinate position reference
290 * @pos_y: the new y coordinate position reference
292 * Wrappers adg_ldim_set_pos() with explicit coordinates.
294 void
295 adg_ldim_set_pos_explicit(AdgLDim *ldim, gdouble pos_x, gdouble pos_y)
297 AdgPair pos;
299 pos.x = pos_x;
300 pos.y = pos_y;
302 adg_ldim_set_pos(ldim, &pos);
306 * adg_ldim_get_direction:
307 * @ldim: an #AdgLDim entity
309 * Gets the direction where @ldim will extend.
311 * Return value: the direction angle in radians
313 gdouble
314 adg_ldim_get_direction(AdgLDim *ldim)
316 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0.);
318 return ldim->priv->direction;
322 * adg_ldim_set_direction:
323 * @ldim: an #AdgLDim entity
324 * @direction: an angle value, in radians
326 * Sets the direction angle where to extend @ldim.
328 void
329 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
331 g_return_if_fail(ADG_IS_LDIM(ldim));
333 ldim->priv->direction = direction;
334 g_object_notify((GObject *) ldim, "direction");
338 static void
339 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
341 clear((AdgLDim *) entity);
342 ENTITY_CLASS->model_matrix_changed(entity, parent_matrix);
345 static void
346 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
348 clear((AdgLDim *) entity);
349 ENTITY_CLASS->paper_matrix_changed(entity, parent_matrix);
352 static void
353 invalidate(AdgEntity *entity)
355 clear((AdgLDim *) entity);
356 ENTITY_CLASS->invalidate(entity);
359 static void
360 render(AdgEntity *entity, cairo_t *cr)
362 AdgLDim *ldim;
363 AdgLDimPrivate *priv;
364 AdgStyle *dim_style;
365 AdgStyle *line_style;
366 AdgStyle *arrow_style;
368 ldim = (AdgLDim *) entity;
369 priv = ldim->priv;
370 dim_style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
371 line_style = adg_dim_style_get_line_style((AdgDimStyle *) dim_style);
372 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
374 update(ldim);
376 cairo_save(cr);
377 adg_entity_apply(entity, ADG_SLOT_DIM_STYLE, cr);
379 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &priv->director);
380 if (!cpml_primitive_reverse(&priv->director)) {
381 g_error("Unable to revert director");
382 return;
384 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &priv->director);
385 cpml_primitive_reverse(&priv->director);
387 adg_style_apply(line_style, cr);
389 cairo_append_path(cr, &priv->path);
390 cairo_stroke(cr);
392 adg_dim_render_quote((AdgDim *) ldim, cr);
394 cairo_restore(cr);
396 ENTITY_CLASS->render(entity, cr);
399 static gchar *
400 default_quote(AdgDim *dim)
402 const AdgPair *pos1, *pos2;
403 AdgStyle *dim_style;
404 gdouble distance;
405 const gchar *format;
407 pos1 = adg_dim_get_pos1(dim);
408 pos2 = adg_dim_get_pos2(dim);
410 if (!cpml_pair_distance(&distance, pos2, pos1))
411 return NULL;
413 dim_style = adg_entity_get_style((AdgEntity *) dim, ADG_SLOT_DIM_STYLE);
414 format = adg_dim_style_get_number_format((AdgDimStyle *) dim_style);
416 return g_strdup_printf(format, distance);
419 static void
420 update(AdgLDim *ldim)
422 AdgLDimPrivate *priv;
423 AdgStyle *dim_style;
424 AdgStyle *arrow_style;
425 const AdgPair *ref1, *ref2, *pos1, *pos2;
426 gdouble level;
427 gdouble baseline_spacing, from_offset;
428 cairo_path_data_t *baseline1, *baseline2;
429 cairo_path_data_t *from1, *to1, *from2, *to2;
430 CpmlPair *arrow1;
431 cairo_path_data_t *arrow2;
432 AdgMatrix paper2model;
433 CpmlPair vector;
434 AdgPair offset;
435 gdouble angle;
437 priv = ldim->priv;
438 if (priv->path.status == CAIRO_STATUS_SUCCESS)
439 return;
441 dim_style = adg_entity_get_style((AdgEntity *) ldim, ADG_SLOT_DIM_STYLE);
442 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
444 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
445 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
446 pos1 = adg_dim_get_pos1((AdgDim *) ldim);
447 pos2 = adg_dim_get_pos2((AdgDim *) ldim);
448 level = adg_dim_get_level((AdgDim *) ldim);
450 baseline_spacing = adg_dim_style_get_baseline_spacing((AdgDimStyle *) dim_style);
451 from_offset = adg_dim_style_get_from_offset((AdgDimStyle *) dim_style);
453 baseline1 = priv->path_data + 1;
454 baseline2 = priv->path_data + 3;
455 from1 = priv->path_data + 5;
456 to1 = priv->path_data + 7;
457 from2 = priv->path_data + 9;
458 to2 = priv->path_data + 11;
459 arrow1 = &priv->director.org;
460 arrow2 = priv->director_data + 1;
462 /* Get the inverted transformation matrix */
463 if (!adg_entity_build_paper2model((AdgEntity *) ldim, &paper2model))
464 return;
466 /* Set vector to the director of the extension lines */
467 cpml_vector_from_angle(&vector, priv->direction);
469 /* Calculate from1 and from2 */
470 offset.x = vector.x * from_offset;
471 offset.y = vector.y * from_offset;
472 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
474 from1->point.x = ref1->x + offset.x;
475 from1->point.y = ref1->y + offset.y;
476 from2->point.x = ref2->x + offset.x;
477 from2->point.y = ref2->y + offset.y;
479 /* Calculate arrow1 and arrow2 */
480 offset.x = vector.x * baseline_spacing * level;
481 offset.y = vector.y * baseline_spacing * level;
482 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
484 arrow1->x = pos1->x + offset.x;
485 arrow1->y = pos1->y + offset.y;
486 arrow2->point.x = pos2->x + offset.x;
487 arrow2->point.y = pos2->y + offset.y;
489 /* Calculate to1 and to2 */
490 offset.x = vector.x * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
491 offset.y = vector.y * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
492 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
494 to1->point.x = arrow1->x + offset.x;
495 to1->point.y = arrow1->y + offset.y;
496 to2->point.x = arrow2->point.x + offset.x;
497 to2->point.y = arrow2->point.y + offset.y;
499 /* Set vector to the director of the baseline */
500 offset.x = arrow2->point.x - arrow1->x;
501 offset.y = arrow2->point.y - arrow1->y;
502 cpml_vector_from_pair(&vector, &offset);
504 /* Update the AdgDim cache contents */
505 adg_dim_set_org_explicit((AdgDim *) ldim,
506 (arrow1->x + arrow2->point.x) / 2.,
507 (arrow1->y + arrow2->point.y) / 2.);
508 cpml_pair_angle(&angle, NULL, &vector);
509 adg_dim_set_angle((AdgDim *) ldim, angle);
511 /* Calculate baseline1 and baseline2 */
512 offset.y = adg_arrow_style_get_margin((AdgArrowStyle *) arrow_style);
513 offset.x = vector.x * offset.y;
514 offset.y *= vector.y;
515 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
517 baseline1->point.x = arrow1->x + offset.x;
518 baseline1->point.y = arrow1->y + offset.y;
519 baseline2->point.x = arrow2->point.x - offset.x;
520 baseline2->point.y = arrow2->point.y - offset.y;
522 priv->path.status = CAIRO_STATUS_SUCCESS;
525 static void
526 clear(AdgLDim *ldim)
528 ldim->priv->path.status = CAIRO_STATUS_INVALID_PATH_DATA;