Implemented adg_line_style_apply() as virtual method adg_style_apply()
[adg.git] / adg / adg-ldim.c
blobb08d22435b7e8de43b8f1e709d0be4ecf07c0ffd
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.
21 /**
22 * SECTION:ldim
23 * @title: AdgLDim
24 * @short_description: Linear dimensions
26 * The #AdgLDim entity represents a linear dimension.
29 #include "adg-ldim.h"
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"
36 #include "adg-util.h"
37 #include "adg-intl.h"
39 #include <gcontainer/gcontainer.h>
41 #define PARENT_CLASS ((AdgDimClass *) adg_ldim_parent_class)
44 enum
46 PROP_0,
47 PROP_DIRECTION
51 static void finalize (GObject *object);
52 static void get_property (GObject *object,
53 guint param_id,
54 GValue *value,
55 GParamSpec *pspec);
56 static void set_property (GObject *object,
57 guint param_id,
58 const GValue *value,
59 GParamSpec *pspec);
60 static void update (AdgEntity *entity);
61 static void render (AdgEntity *entity,
62 cairo_t *cr);
63 static gchar * default_quote (AdgDim *dim);
66 G_DEFINE_TYPE (AdgLDim, adg_ldim, ADG_TYPE_DIM);
69 static void
70 adg_ldim_class_init (AdgLDimClass *klass)
72 GObjectClass *gobject_class;
73 AdgEntityClass *entity_class;
74 AdgDimClass *dim_class;
75 GParamSpec *param;
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",
92 P_("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);
99 static void
100 adg_ldim_init (AdgLDim *ldim)
102 AdgLDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (ldim, ADG_TYPE_LDIM,
103 AdgLDimPrivate);
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;
123 ldim->priv = priv;
126 static void
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);
137 static void
138 get_property (GObject *object,
139 guint prop_id,
140 GValue *value,
141 GParamSpec *pspec)
143 AdgLDim *ldim = ADG_LDIM (object);
145 switch (prop_id)
147 case PROP_DIRECTION:
148 g_value_set_double (value, ldim->priv->direction);
149 break;
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
156 static void
157 set_property (GObject *object,
158 guint prop_id,
159 const GValue *value,
160 GParamSpec *pspec)
162 AdgLDim *ldim = ADG_LDIM (object);
164 switch (prop_id)
166 case PROP_DIRECTION:
167 ldim->priv->direction = g_value_get_double (value);
168 break;
169 default:
170 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171 break;
176 static void
177 update (AdgEntity *entity)
179 AdgDim *dim;
180 AdgLDim *ldim;
181 AdgDimStylePrivate *style_data;
182 AdgMatrix device2user;
183 CpmlPair vector;
184 AdgPair offset;
185 AdgPair from1, to1;
186 AdgPair from2, to2;
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;
255 /* Set extension1 */
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;
269 /* Set extension2 */
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;
283 /* Set arrow_path */
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;
297 /* Set baseline */
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;
312 static void
313 render (AdgEntity *entity,
314 cairo_t *cr)
316 AdgDim *dim;
317 AdgLDim *ldim;
318 AdgDimStylePrivate *style_data;
319 AdgArrowStyle *arrow_style;
320 CpmlPath primitive;
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;
330 /* TODO: caching
331 if (!adg_entity_model_applied (entity)) */
332 update (entity);
334 cairo_save (cr);
336 /* Arrows */
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);
344 /* Lines */
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);
351 cairo_stroke (cr);
353 _adg_dim_render_quote (dim, cr);
355 cairo_restore (cr);
357 ((AdgEntityClass *) PARENT_CLASS)->render (entity, cr);
360 static gchar *
361 default_quote (AdgDim *dim)
363 double number;
365 if (!cpml_pair_distance(&dim->priv->pos2, &dim->priv->pos1, &number))
366 return NULL;
368 return g_strdup_printf (dim->priv->dim_style->priv->number_format, number);
373 * adg_ldim_new:
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
382 AdgEntity *
383 adg_ldim_new (void)
385 return (AdgEntity *) g_object_new (ADG_TYPE_LDIM, NULL);
389 * adg_ldim_new_full:
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
396 * one shot.
398 * Return value: the new entity
400 AdgEntity *
401 adg_ldim_new_full (const AdgPair *ref1,
402 const AdgPair *ref2,
403 double direction,
404 const AdgPair *pos)
406 AdgEntity *entity = (AdgEntity *) g_object_new (ADG_TYPE_LDIM,
407 "ref1", ref1,
408 "ref2", ref2,
409 "direction", direction,
410 NULL);
411 adg_ldim_set_pos ((AdgLDim *) entity, pos);
412 return entity;
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
429 AdgEntity *
430 adg_ldim_new_full_explicit (double ref1_x,
431 double ref1_y,
432 double ref2_x,
433 double ref2_y,
434 double direction,
435 double pos_x,
436 double pos_y)
438 AdgPair ref1;
439 AdgPair ref2;
440 AdgPair pos;
442 ref1.x = ref1_x;
443 ref1.y = ref1_y;
444 ref2.x = ref2_x;
445 ref2.y = ref2_y;
446 pos.x = pos_x;
447 pos.y = pos_y;
449 return adg_ldim_new_full (&ref1, &ref2, direction, &pos);
455 * adg_ldim_set_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.
464 void
465 adg_ldim_set_pos (AdgLDim *ldim,
466 const AdgPair *pos)
468 AdgDim *dim;
469 GObject *object;
470 CpmlPair extension_vector;
471 CpmlPair baseline_vector;
472 gdouble d, k;
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.
512 void
513 adg_ldim_set_pos_explicit (AdgLDim *ldim,
514 double pos_x,
515 double pos_y)
517 AdgPair pos;
519 pos.x = pos_x;
520 pos.y = pos_y;
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
533 double
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.
548 void
549 adg_ldim_set_direction (AdgLDim *ldim,
550 double direction)
552 g_return_if_fail (ADG_IS_LDIM (ldim));
554 ldim->priv->direction = direction;
555 g_object_notify ((GObject *) ldim, "direction");