Tab expansion in all the source files
[adg.git] / adg / adg-ldim.c
blobc29ac4a739854388da89a1690527c21c5364475f
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.
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-dim-private.h"
31 #include "adg-container.h"
32 #include "adg-util.h"
33 #include "adg-intl.h"
34 #include <gcontainer/gcontainer.h>
36 #define PARENT_CLASS ((AdgDimClass *) adg_ldim_parent_class)
39 enum {
40 PROP_0,
41 PROP_DIRECTION
45 static void finalize (GObject *object);
46 static void get_property (GObject *object,
47 guint param_id,
48 GValue *value,
49 GParamSpec *pspec);
50 static void set_property (GObject *object,
51 guint param_id,
52 const GValue *value,
53 GParamSpec *pspec);
54 static void update (AdgEntity *entity);
55 static void render (AdgEntity *entity,
56 cairo_t *cr);
57 static gchar * default_quote (AdgDim *dim);
60 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
63 static void
64 adg_ldim_class_init(AdgLDimClass *klass)
66 GObjectClass *gobject_class;
67 AdgEntityClass *entity_class;
68 AdgDimClass *dim_class;
69 GParamSpec *param;
71 gobject_class = (GObjectClass *) klass;
72 entity_class = (AdgEntityClass *) klass;
73 dim_class = (AdgDimClass *) klass;
75 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
77 gobject_class->finalize = finalize;
78 gobject_class->get_property = get_property;
79 gobject_class->set_property = set_property;
81 entity_class->render = render;
83 dim_class->default_quote = default_quote;
85 param = g_param_spec_double("direction",
86 P_("Direction"),
87 P_("The inclination angle of the extension lines"),
88 -G_MAXDOUBLE, G_MAXDOUBLE, CPML_DIR_RIGHT,
89 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
90 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
93 static void
94 adg_ldim_init(AdgLDim *ldim)
96 AdgLDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM,
97 AdgLDimPrivate);
99 priv->direction = CPML_DIR_RIGHT;
101 priv->extension1.status = CAIRO_STATUS_SUCCESS;
102 priv->extension1.data = NULL;
103 priv->extension1.num_data = 4;
105 priv->extension2.status = CAIRO_STATUS_SUCCESS;
106 priv->extension2.data = NULL;
107 priv->extension2.num_data = 4;
109 priv->arrow_path.status = CAIRO_STATUS_SUCCESS;
110 priv->arrow_path.data = NULL;
111 priv->arrow_path.num_data = 4;
113 priv->baseline.status = CAIRO_STATUS_SUCCESS;
114 priv->baseline.data = NULL;
115 priv->baseline.num_data = 4;
117 ldim->priv = priv;
120 static void
121 finalize(GObject *object)
123 AdgLDimPrivate *priv = ((AdgLDim *) object)->priv;
125 g_free(priv->extension1.data);
126 g_free(priv->extension2.data);
127 g_free(priv->arrow_path.data);
128 g_free(priv->baseline.data);
131 static void
132 get_property(GObject *object,
133 guint prop_id, GValue *value, GParamSpec *pspec)
135 AdgLDim *ldim = ADG_LDIM(object);
137 switch (prop_id) {
138 case PROP_DIRECTION:
139 g_value_set_double(value, ldim->priv->direction);
140 break;
141 default:
142 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
143 break;
147 static void
148 set_property(GObject *object,
149 guint prop_id, const GValue *value, GParamSpec *pspec)
151 AdgLDim *ldim = ADG_LDIM(object);
153 switch (prop_id) {
154 case PROP_DIRECTION:
155 ldim->priv->direction = g_value_get_double(value);
156 break;
157 default:
158 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
159 break;
164 static void
165 update(AdgEntity *entity)
167 AdgDim *dim;
168 AdgLDim *ldim;
169 AdgDimStyle *dim_style;
170 AdgArrowStyle *arrow_style;
171 AdgMatrix device2user;
172 CpmlPair vector;
173 AdgPair offset;
174 AdgPair from1, to1;
175 AdgPair from2, to2;
176 AdgPair arrow1, arrow2;
177 AdgPair baseline1, baseline2;
178 cairo_path_data_t *path_data;
180 dim = (AdgDim *) entity;
181 ldim = (AdgLDim *) entity;
182 dim_style =
183 (AdgDimStyle *) adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
184 arrow_style =
185 (AdgArrowStyle *) adg_dim_style_get_arrow_style(dim_style);
187 /* Get the inverted transformation matrix */
188 adg_matrix_set(&device2user, adg_entity_get_model_matrix(entity));
189 g_return_if_fail(cairo_matrix_invert(&device2user) ==
190 CAIRO_STATUS_SUCCESS);
192 /* Set vector to the director of the extension lines */
193 cpml_vector_from_angle(&vector, ldim->priv->direction);
195 /* Calculate from1 and from2 */
196 offset.x = vector.x * adg_dim_style_get_from_offset(dim_style);
197 offset.y = vector.y * adg_dim_style_get_from_offset(dim_style);
198 cairo_matrix_transform_distance(&device2user, &offset.x, &offset.y);
200 from1.x = dim->priv->ref1.x + offset.x;
201 from1.y = dim->priv->ref1.y + offset.y;
202 from2.x = dim->priv->ref2.x + offset.x;
203 from2.y = dim->priv->ref2.y + offset.y;
205 /* Calculate arrow1 and arrow2 */
206 offset.x =
207 vector.x * adg_dim_style_get_baseline_spacing(dim_style) *
208 dim->priv->level;
209 offset.y =
210 vector.y * adg_dim_style_get_baseline_spacing(dim_style) *
211 dim->priv->level;
212 cairo_matrix_transform_distance(&device2user, &offset.x, &offset.y);
214 arrow1.x = dim->priv->pos1.x + offset.x;
215 arrow1.y = dim->priv->pos1.y + offset.y;
216 arrow2.x = dim->priv->pos2.x + offset.x;
217 arrow2.y = dim->priv->pos2.y + offset.y;
219 /* Calculate to1 and to2 */
220 offset.x = vector.x * adg_dim_style_get_to_offset(dim_style);
221 offset.y = vector.y * adg_dim_style_get_to_offset(dim_style);
222 cairo_matrix_transform_distance(&device2user, &offset.x, &offset.y);
224 to1.x = arrow1.x + offset.x;
225 to1.y = arrow1.y + offset.y;
226 to2.x = arrow2.x + offset.x;
227 to2.y = arrow2.y + offset.y;
229 /* Set vector to the director of the baseline */
230 offset.x = arrow2.x - arrow1.x;
231 offset.y = arrow2.y - arrow1.y;
232 cpml_vector_from_pair(&vector, &offset);
234 /* Update the AdgDim cache contents */
235 dim->priv->quote_org.x = (arrow1.x + arrow2.x) / 2.;
236 dim->priv->quote_org.y = (arrow1.y + arrow2.y) / 2.;
237 cpml_pair_angle(NULL, &vector, &dim->priv->quote_angle);
239 /* Calculate baseline1 and baseline2 */
240 offset.y = adg_arrow_style_get_margin(arrow_style);
241 offset.x = vector.x * offset.y;
242 offset.y *= vector.y;
243 cairo_matrix_transform_distance(&device2user, &offset.x, &offset.y);
245 baseline1.x = arrow1.x + offset.x;
246 baseline1.y = arrow1.y + offset.y;
247 baseline2.x = arrow2.x - offset.x;
248 baseline2.y = arrow2.y - offset.y;
250 /* Set extension1 */
251 if (ldim->priv->extension1.data == NULL)
252 ldim->priv->extension1.data = g_new(cairo_path_data_t, 4);
254 path_data = ldim->priv->extension1.data;
255 path_data[0].header.type = CAIRO_PATH_MOVE_TO;
256 path_data[0].header.length = 2;
257 path_data[1].point.x = from1.x;
258 path_data[1].point.y = from1.y;
259 path_data[2].header.type = CAIRO_PATH_LINE_TO;
260 path_data[2].header.length = 2;
261 path_data[3].point.x = to1.x;
262 path_data[3].point.y = to1.y;
264 /* Set extension2 */
265 if (ldim->priv->extension2.data == NULL)
266 ldim->priv->extension2.data = g_new(cairo_path_data_t, 4);
268 path_data = ldim->priv->extension2.data;
269 path_data[0].header.type = CAIRO_PATH_MOVE_TO;
270 path_data[0].header.length = 2;
271 path_data[1].point.x = from2.x;
272 path_data[1].point.y = from2.y;
273 path_data[2].header.type = CAIRO_PATH_LINE_TO;
274 path_data[2].header.length = 2;
275 path_data[3].point.x = to2.x;
276 path_data[3].point.y = to2.y;
278 /* Set arrow_path */
279 if (ldim->priv->arrow_path.data == NULL)
280 ldim->priv->arrow_path.data = g_new(cairo_path_data_t, 4);
282 path_data = ldim->priv->arrow_path.data;
283 path_data[0].header.type = CAIRO_PATH_MOVE_TO;
284 path_data[0].header.length = 2;
285 path_data[1].point.x = arrow1.x;
286 path_data[1].point.y = arrow1.y;
287 path_data[2].header.type = CAIRO_PATH_LINE_TO;
288 path_data[2].header.length = 2;
289 path_data[3].point.x = arrow2.x;
290 path_data[3].point.y = arrow2.y;
292 /* Set baseline */
293 if (ldim->priv->baseline.data == NULL)
294 ldim->priv->baseline.data = g_new(cairo_path_data_t, 4);
296 path_data = ldim->priv->baseline.data;
297 path_data[0].header.type = CAIRO_PATH_MOVE_TO;
298 path_data[0].header.length = 2;
299 path_data[1].point.x = baseline1.x;
300 path_data[1].point.y = baseline1.y;
301 path_data[2].header.type = CAIRO_PATH_LINE_TO;
302 path_data[2].header.length = 2;
303 path_data[3].point.x = baseline2.x;
304 path_data[3].point.y = baseline2.y;
307 static void
308 render(AdgEntity *entity, cairo_t *cr)
310 AdgDim *dim;
311 AdgLDim *ldim;
312 AdgDimStyle *dim_style;
313 AdgStyle *line_style;
314 AdgArrowStyle *arrow_style;
315 CpmlPath primitive;
317 dim = (AdgDim *) entity;
318 ldim = (AdgLDim *) entity;
319 dim_style =
320 (AdgDimStyle *) adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
321 line_style = adg_dim_style_get_line_style(dim_style);
322 arrow_style =
323 (AdgArrowStyle *) adg_dim_style_get_arrow_style(dim_style);
325 /* TODO: caching
326 if (!adg_entity_model_applied (entity)) */
327 update(entity);
329 cairo_save(cr);
330 adg_entity_apply(entity, ADG_SLOT_DIM_STYLE, cr);
332 /* Arrows */
333 if (cpml_path_from_cairo(&primitive, &ldim->priv->arrow_path, NULL)) {
334 adg_arrow_style_render(arrow_style, cr, &primitive);
335 if (cpml_primitive_reverse(&primitive))
336 adg_arrow_style_render(arrow_style, cr, &primitive);
339 /* Lines */
340 adg_style_apply(line_style, cr);
342 cairo_append_path(cr, &ldim->priv->extension1);
343 cairo_append_path(cr, &ldim->priv->extension2);
344 cairo_append_path(cr, &ldim->priv->baseline);
346 cairo_stroke(cr);
348 adg_dim_render_quote(dim, cr);
350 cairo_restore(cr);
352 ((AdgEntityClass *) PARENT_CLASS)->render(entity, cr);
355 static gchar *
356 default_quote(AdgDim *dim)
358 AdgDimStyle *dim_style;
359 gdouble number;
361 if (!cpml_pair_distance(&dim->priv->pos2, &dim->priv->pos1, &number))
362 return NULL;
364 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
365 ADG_SLOT_DIM_STYLE);
366 return g_strdup_printf(adg_dim_style_get_number_format(dim_style),
367 number);
372 * adg_ldim_new:
374 * Creates a new - unreferenced - linear dimension. You must, at least, define
375 * the reference points with adg_dim_set_ref(), the dimension direction with
376 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
377 * or, better, adg_ldim_set_pos().
379 * Return value: the new entity
381 AdgEntity *
382 adg_ldim_new(void)
384 return (AdgEntity *) g_object_new(ADG_TYPE_LDIM, NULL);
388 * adg_ldim_new_full:
389 * @ref1: the first reference point
390 * @ref2: the second reference point
391 * @direction: angle where to extend the dimension
392 * @pos: the position reference
394 * Creates a new linear dimension, specifing all the needed properties in
395 * one shot.
397 * Return value: the new entity
399 AdgEntity *
400 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
401 double direction, const AdgPair *pos)
403 AdgEntity *entity = (AdgEntity *) g_object_new(ADG_TYPE_LDIM,
404 "ref1", ref1,
405 "ref2", ref2,
406 "direction", direction,
407 NULL);
408 adg_ldim_set_pos((AdgLDim *) entity, pos);
409 return entity;
413 * adg_ldim_new_full_explicit:
414 * @ref1_x: the x coordinate of the first reference point
415 * @ref1_y: the y coordinate of the first reference point
416 * @ref2_x: the x coordinate of the second reference point
417 * @ref2_y: the y coordinate of the second reference point
418 * @direction: angle where to extend the dimension
419 * @pos_x: the x coordinate of the position reference
420 * @pos_y: the y coordinate of the position reference
422 * Wrappes adg_ldim_new_full() with explicit quotes.
424 * Return value: the new entity
426 AdgEntity *
427 adg_ldim_new_full_explicit(double ref1_x,
428 double ref1_y,
429 double ref2_x,
430 double ref2_y,
431 double direction, double pos_x, double pos_y)
433 AdgPair ref1;
434 AdgPair ref2;
435 AdgPair pos;
437 ref1.x = ref1_x;
438 ref1.y = ref1_y;
439 ref2.x = ref2_x;
440 ref2.y = ref2_y;
441 pos.x = pos_x;
442 pos.y = pos_y;
444 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
450 * adg_ldim_set_pos:
451 * @ldim: an #AdgLDim entity
452 * @pos: an #AdgPair structure
454 * Sets the position references (pos1 and pos2 properties) of @ldim using a
455 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
456 * points and the direction. If these conditions are not met, an error message
457 * is logged and the position references will not be set.
459 void
460 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
462 AdgDim *dim;
463 GObject *object;
464 CpmlPair extension_vector;
465 CpmlPair baseline_vector;
466 gdouble d, k;
468 g_return_if_fail(ADG_IS_LDIM(ldim));
470 dim = (AdgDim *) ldim;
471 object = (GObject *) ldim;
473 cpml_vector_from_angle(&extension_vector, ldim->priv->direction);
475 baseline_vector.x = -extension_vector.y;
476 baseline_vector.y = extension_vector.x;
478 d = extension_vector.y * baseline_vector.x -
479 extension_vector.x * baseline_vector.y;
480 g_return_if_fail(d != 0.);
482 k = ((pos->y - dim->priv->ref1.y) * baseline_vector.x -
483 (pos->x - dim->priv->ref1.x) * baseline_vector.y) / d;
484 dim->priv->pos1.x = dim->priv->ref1.x + k * extension_vector.x;
485 dim->priv->pos1.y = dim->priv->ref1.y + k * extension_vector.y;
487 k = ((pos->y - dim->priv->ref2.y) * baseline_vector.x -
488 (pos->x - dim->priv->ref2.x) * baseline_vector.y) / d;
489 dim->priv->pos2.x = dim->priv->ref2.x + k * extension_vector.x;
490 dim->priv->pos2.y = dim->priv->ref2.y + k * extension_vector.y;
492 g_object_freeze_notify(object);
493 g_object_notify(object, "pos1");
494 g_object_notify(object, "pos2");
495 g_object_thaw_notify(object);
499 * adg_ldim_set_pos_explicit:
500 * @ldim: an #AdgLDim entity
501 * @pos_x: the new x coordinate position reference
502 * @pos_y: the new y coordinate position reference
504 * Wrappers adg_ldim_set_pos() with explicit coordinates.
506 void
507 adg_ldim_set_pos_explicit(AdgLDim *ldim, double pos_x, double pos_y)
509 AdgPair pos;
511 pos.x = pos_x;
512 pos.y = pos_y;
514 adg_ldim_set_pos(ldim, &pos);
518 * adg_ldim_get_direction:
519 * @ldim: an #AdgLDim entity
521 * Gets the direction where @ldim will extend.
523 * Return value: the direction angle in radians
525 double
526 adg_ldim_get_direction(AdgLDim *ldim)
528 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0.);
530 return ldim->priv->direction;
534 * adg_ldim_set_direction:
535 * @ldim: an #AdgLDim entity
536 * @direction: an angle value, in radians
538 * Sets the direction angle where to extend @ldim.
540 void
541 adg_ldim_set_direction(AdgLDim *ldim, double direction)
543 g_return_if_fail(ADG_IS_LDIM(ldim));
545 ldim->priv->direction = direction;
546 g_object_notify((GObject *) ldim, "direction");