[AdgPath] Implemented adg_path_chamfer()
[adg.git] / adg / adg-ldim.c
blobc1265a223a696ce18345a272941918bfe036e5f0
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.
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"
35 enum {
36 PROP_0,
37 PROP_DIRECTION
41 static void get_property (GObject *object,
42 guint param_id,
43 GValue *value,
44 GParamSpec *pspec);
45 static void set_property (GObject *object,
46 guint param_id,
47 const GValue *value,
48 GParamSpec *pspec);
49 static void model_matrix_changed (AdgEntity *entity,
50 AdgMatrix *parent_matrix);
51 static void paper_matrix_changed (AdgEntity *entity,
52 AdgMatrix *parent_matrix);
53 static void invalidate (AdgEntity *entity);
54 static void render (AdgEntity *entity,
55 cairo_t *cr);
56 static gchar * default_quote (AdgDim *dim);
57 static void update (AdgLDim *ldim);
58 static void clear (AdgLDim *ldim);
61 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
64 static void
65 adg_ldim_class_init(AdgLDimClass *klass)
67 GObjectClass *gobject_class;
68 AdgEntityClass *entity_class;
69 AdgDimClass *dim_class;
70 GParamSpec *param;
72 gobject_class = (GObjectClass *) klass;
73 entity_class = (AdgEntityClass *) klass;
74 dim_class = (AdgDimClass *) klass;
76 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
78 gobject_class->get_property = get_property;
79 gobject_class->set_property = set_property;
81 entity_class->model_matrix_changed = model_matrix_changed;
82 entity_class->paper_matrix_changed = paper_matrix_changed;
83 entity_class->invalidate = invalidate;
84 entity_class->render = render;
86 dim_class->default_quote = default_quote;
88 param = g_param_spec_double("direction",
89 P_("Direction"),
90 P_("The inclination angle of the extension lines"),
91 -G_MAXDOUBLE, G_MAXDOUBLE, ADG_DIR_RIGHT,
92 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
93 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
96 static void
97 adg_ldim_init(AdgLDim *ldim)
99 AdgLDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM,
100 AdgLDimPrivate);
102 priv->direction = ADG_DIR_RIGHT;
104 priv->path.status = CAIRO_STATUS_INVALID_PATH_DATA;
105 priv->path.data = priv->path_data;
106 priv->path.num_data = 12;
108 priv->path_data[0].header.type = CAIRO_PATH_MOVE_TO;
109 priv->path_data[0].header.length = 2;
110 priv->path_data[2].header.type = CAIRO_PATH_LINE_TO;
111 priv->path_data[2].header.length = 2;
112 priv->path_data[4].header.type = CAIRO_PATH_MOVE_TO;
113 priv->path_data[4].header.length = 2;
114 priv->path_data[6].header.type = CAIRO_PATH_LINE_TO;
115 priv->path_data[6].header.length = 2;
116 priv->path_data[8].header.type = CAIRO_PATH_MOVE_TO;
117 priv->path_data[8].header.length = 2;
118 priv->path_data[10].header.type = CAIRO_PATH_LINE_TO;
119 priv->path_data[10].header.length = 2;
121 priv->director.data = priv->director_data;
122 priv->director.num_data = 4;
123 priv->director.cairo_path = NULL;
125 priv->director_data[0].header.type = CAIRO_PATH_MOVE_TO;
126 priv->director_data[0].header.length = 2;
127 priv->director_data[2].header.type = CAIRO_PATH_LINE_TO;
128 priv->director_data[2].header.length = 2;
130 ldim->priv = priv;
133 static void
134 get_property(GObject *object,
135 guint prop_id, GValue *value, GParamSpec *pspec)
137 AdgLDim *ldim = ADG_LDIM(object);
139 switch (prop_id) {
140 case PROP_DIRECTION:
141 g_value_set_double(value, ldim->priv->direction);
142 break;
143 default:
144 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
145 break;
149 static void
150 set_property(GObject *object,
151 guint prop_id, const GValue *value, GParamSpec *pspec)
153 AdgLDim *ldim = ADG_LDIM(object);
155 switch (prop_id) {
156 case PROP_DIRECTION:
157 ldim->priv->direction = g_value_get_double(value);
158 break;
159 default:
160 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
161 break;
167 * adg_ldim_new:
169 * Creates a new - unreferenced - linear dimension. You must, at least, define
170 * the reference points with adg_dim_set_ref(), the dimension direction with
171 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
172 * or, better, adg_ldim_set_pos().
174 * Return value: the new entity
176 AdgEntity *
177 adg_ldim_new(void)
179 return (AdgEntity *) g_object_new(ADG_TYPE_LDIM, NULL);
183 * adg_ldim_new_full:
184 * @ref1: the first reference point
185 * @ref2: the second reference point
186 * @direction: angle where to extend the dimension
187 * @pos: the position reference
189 * Creates a new linear dimension, specifing all the needed properties in
190 * one shot.
192 * Return value: the new entity
194 AdgEntity *
195 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
196 gdouble direction, const AdgPair *pos)
198 AdgEntity *entity = (AdgEntity *) g_object_new(ADG_TYPE_LDIM,
199 "ref1", ref1,
200 "ref2", ref2,
201 "direction", direction,
202 NULL);
203 adg_ldim_set_pos((AdgLDim *) entity, pos);
204 return entity;
208 * adg_ldim_new_full_explicit:
209 * @ref1_x: the x coordinate of the first reference point
210 * @ref1_y: the y coordinate of the first reference point
211 * @ref2_x: the x coordinate of the second reference point
212 * @ref2_y: the y coordinate of the second reference point
213 * @direction: angle where to extend the dimension
214 * @pos_x: the x coordinate of the position reference
215 * @pos_y: the y coordinate of the position reference
217 * Wrappes adg_ldim_new_full() with explicit quotes.
219 * Return value: the new entity
221 AdgEntity *
222 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
223 gdouble ref2_x, gdouble ref2_y,
224 gdouble direction, gdouble pos_x, gdouble pos_y)
226 AdgPair ref1;
227 AdgPair ref2;
228 AdgPair pos;
230 ref1.x = ref1_x;
231 ref1.y = ref1_y;
232 ref2.x = ref2_x;
233 ref2.y = ref2_y;
234 pos.x = pos_x;
235 pos.y = pos_y;
237 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
241 * adg_ldim_set_pos:
242 * @ldim: an #AdgLDim entity
243 * @pos: an #AdgPair structure
245 * Sets the position references (pos1 and pos2 properties) of @ldim using a
246 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
247 * points and the direction. If these conditions are not met, an error message
248 * is logged and the position references will not be set.
250 void
251 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
253 const AdgPair *ref1, *ref2;
254 AdgPair pos1, pos2;
255 CpmlPair baseline_vector, extension_vector;
256 gdouble d, k;
258 g_return_if_fail(ADG_IS_LDIM(ldim));
260 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
261 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
263 cpml_vector_from_angle(&extension_vector, ldim->priv->direction, 1);
265 baseline_vector.x = -extension_vector.y;
266 baseline_vector.y = extension_vector.x;
268 d = extension_vector.y * baseline_vector.x -
269 extension_vector.x * baseline_vector.y;
270 g_return_if_fail(d != 0);
272 k = ((pos->y - ref1->y) * baseline_vector.x -
273 (pos->x - ref1->x) * baseline_vector.y) / d;
274 pos1.x = ref1->x + k * extension_vector.x;
275 pos1.y = ref1->y + k * extension_vector.y;
277 k = ((pos->y - ref2->y) * baseline_vector.x -
278 (pos->x - ref2->x) * baseline_vector.y) / d;
279 pos2.x = ref2->x + k * extension_vector.x;
280 pos2.y = ref2->y + k * extension_vector.y;
282 adg_dim_set_pos((AdgDim *) ldim, &pos1, &pos2);
286 * adg_ldim_set_pos_explicit:
287 * @ldim: an #AdgLDim entity
288 * @pos_x: the new x coordinate position reference
289 * @pos_y: the new y coordinate position reference
291 * Wrappers adg_ldim_set_pos() with explicit coordinates.
293 void
294 adg_ldim_set_pos_explicit(AdgLDim *ldim, gdouble pos_x, gdouble pos_y)
296 AdgPair pos;
298 pos.x = pos_x;
299 pos.y = pos_y;
301 adg_ldim_set_pos(ldim, &pos);
305 * adg_ldim_get_direction:
306 * @ldim: an #AdgLDim entity
308 * Gets the direction where @ldim will extend.
310 * Return value: the direction angle in radians
312 gdouble
313 adg_ldim_get_direction(AdgLDim *ldim)
315 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
317 return ldim->priv->direction;
321 * adg_ldim_set_direction:
322 * @ldim: an #AdgLDim entity
323 * @direction: an angle value, in radians
325 * Sets the direction angle where to extend @ldim.
327 void
328 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
330 g_return_if_fail(ADG_IS_LDIM(ldim));
332 ldim->priv->direction = direction;
333 g_object_notify((GObject *) ldim, "direction");
337 static void
338 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
340 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
342 clear((AdgLDim *) entity);
344 if (entity_class->model_matrix_changed != NULL)
345 entity_class->model_matrix_changed(entity, parent_matrix);
348 static void
349 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
351 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
353 clear((AdgLDim *) entity);
355 if (entity_class->paper_matrix_changed != NULL)
356 entity_class->paper_matrix_changed(entity, parent_matrix);
359 static void
360 invalidate(AdgEntity *entity)
362 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
364 clear((AdgLDim *) entity);
366 if (entity_class->invalidate != NULL)
367 entity_class->invalidate(entity);
370 static void
371 render(AdgEntity *entity, cairo_t *cr)
373 AdgLDim *ldim;
374 AdgLDimPrivate *priv;
375 AdgEntityClass *entity_class;
376 AdgStyle *dim_style;
377 AdgStyle *line_style;
378 AdgStyle *arrow_style;
380 ldim = (AdgLDim *) entity;
381 priv = ldim->priv;
382 entity_class = (AdgEntityClass *) adg_ldim_parent_class;
383 dim_style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
384 line_style = adg_dim_style_get_line_style((AdgDimStyle *) dim_style);
385 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
387 update(ldim);
389 cairo_save(cr);
390 adg_entity_apply(entity, ADG_SLOT_DIM_STYLE, cr);
392 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &priv->director);
393 cpml_segment_reverse(&priv->director);
394 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &priv->director);
395 cpml_segment_reverse(&priv->director);
397 adg_style_apply(line_style, cr);
399 cairo_append_path(cr, &priv->path);
400 cairo_stroke(cr);
402 adg_dim_render_quote((AdgDim *) ldim, cr);
404 cairo_restore(cr);
406 if (entity_class->render != NULL)
407 entity_class->render(entity, cr);
410 static gchar *
411 default_quote(AdgDim *dim)
413 const AdgPair *pos1, *pos2;
414 AdgStyle *dim_style;
415 gdouble distance;
416 const gchar *format;
418 pos1 = adg_dim_get_pos1(dim);
419 pos2 = adg_dim_get_pos2(dim);
420 distance = cpml_pair_distance(pos1, pos2);
421 dim_style = adg_entity_get_style((AdgEntity *) dim, ADG_SLOT_DIM_STYLE);
422 format = adg_dim_style_get_number_format((AdgDimStyle *) dim_style);
424 return g_strdup_printf(format, distance);
427 static void
428 update(AdgLDim *ldim)
430 AdgLDimPrivate *priv;
431 AdgStyle *dim_style;
432 AdgStyle *arrow_style;
433 const AdgPair *ref1, *ref2, *pos1, *pos2;
434 gdouble level;
435 gdouble baseline_spacing, from_offset;
436 cairo_path_data_t *baseline1, *baseline2;
437 cairo_path_data_t *from1, *to1, *from2, *to2;
438 cairo_path_data_t *arrow1, *arrow2;
439 AdgMatrix paper2model;
440 CpmlPair vector;
441 AdgPair offset;
442 gdouble angle;
444 priv = ldim->priv;
445 if (priv->path.status == CAIRO_STATUS_SUCCESS)
446 return;
448 dim_style = adg_entity_get_style((AdgEntity *) ldim, ADG_SLOT_DIM_STYLE);
449 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
451 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
452 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
453 pos1 = adg_dim_get_pos1((AdgDim *) ldim);
454 pos2 = adg_dim_get_pos2((AdgDim *) ldim);
455 level = adg_dim_get_level((AdgDim *) ldim);
457 baseline_spacing = adg_dim_style_get_baseline_spacing((AdgDimStyle *) dim_style);
458 from_offset = adg_dim_style_get_from_offset((AdgDimStyle *) dim_style);
460 baseline1 = priv->path_data + 1;
461 baseline2 = priv->path_data + 3;
462 from1 = priv->path_data + 5;
463 to1 = priv->path_data + 7;
464 from2 = priv->path_data + 9;
465 to2 = priv->path_data + 11;
466 arrow1 = priv->director_data + 1;
467 arrow2 = priv->director_data + 3;
469 /* Get the inverted transformation matrix */
470 if (!adg_entity_build_paper2model((AdgEntity *) ldim, &paper2model))
471 return;
473 /* Set vector to the director of the extension lines */
474 cpml_vector_from_angle(&vector, priv->direction, 1);
476 /* Calculate from1 and from2 */
477 offset.x = vector.x * from_offset;
478 offset.y = vector.y * from_offset;
479 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
481 from1->point.x = ref1->x + offset.x;
482 from1->point.y = ref1->y + offset.y;
483 from2->point.x = ref2->x + offset.x;
484 from2->point.y = ref2->y + offset.y;
486 /* Calculate arrow1 and arrow2 */
487 offset.x = vector.x * baseline_spacing * level;
488 offset.y = vector.y * baseline_spacing * level;
489 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
491 arrow1->point.x = pos1->x + offset.x;
492 arrow1->point.y = pos1->y + offset.y;
493 arrow2->point.x = pos2->x + offset.x;
494 arrow2->point.y = pos2->y + offset.y;
496 /* Calculate to1 and to2 */
497 offset.x = vector.x * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
498 offset.y = vector.y * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
499 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
501 to1->point.x = arrow1->point.x + offset.x;
502 to1->point.y = arrow1->point.y + offset.y;
503 to2->point.x = arrow2->point.x + offset.x;
504 to2->point.y = arrow2->point.y + offset.y;
506 /* Set vector to the director of the baseline */
507 offset.x = arrow2->point.x - arrow1->point.x;
508 offset.y = arrow2->point.y - arrow1->point.y;
509 cpml_vector_set_length(cpml_pair_copy(&vector, &offset), 1);
511 /* Update the AdgDim cache contents */
512 adg_dim_set_org_explicit((AdgDim *) ldim,
513 (arrow1->point.x + arrow2->point.x) / 2.,
514 (arrow1->point.y + arrow2->point.y) / 2.);
515 angle = cpml_vector_angle(&vector);
516 adg_dim_set_angle((AdgDim *) ldim, angle);
518 /* Calculate baseline1 and baseline2 */
519 offset.y = adg_arrow_style_get_margin((AdgArrowStyle *) arrow_style);
520 offset.x = vector.x * offset.y;
521 offset.y *= vector.y;
522 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
524 baseline1->point.x = arrow1->point.x + offset.x;
525 baseline1->point.y = arrow1->point.y + offset.y;
526 baseline2->point.x = arrow2->point.x - offset.x;
527 baseline2->point.y = arrow2->point.y - offset.y;
529 priv->path.status = CAIRO_STATUS_SUCCESS;
532 static void
533 clear(AdgLDim *ldim)
535 ldim->priv->path.status = CAIRO_STATUS_INVALID_PATH_DATA;