[AdgPoint] Removed from ADG
[adg.git] / adg / adg-ldim.c
blob0ff3a89151b2f4774fdb82c7983b560a7d95025f
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 * @title: AdgLDim
24 * @short_description: Linear dimensions
26 * The #AdgLDim entity represents a linear dimension.
29 /**
30 * AdgLDim:
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
34 **/
37 #include "adg-ldim.h"
38 #include "adg-ldim-private.h"
39 #include "adg-container.h"
40 #include "adg-util.h"
41 #include "adg-intl.h"
44 enum {
45 PROP_0,
46 PROP_DIRECTION
50 static void get_property (GObject *object,
51 guint param_id,
52 GValue *value,
53 GParamSpec *pspec);
54 static void set_property (GObject *object,
55 guint param_id,
56 const GValue *value,
57 GParamSpec *pspec);
58 static void model_matrix_changed (AdgEntity *entity,
59 AdgMatrix *parent_matrix);
60 static void paper_matrix_changed (AdgEntity *entity,
61 AdgMatrix *parent_matrix);
62 static void invalidate (AdgEntity *entity);
63 static void render (AdgEntity *entity,
64 cairo_t *cr);
65 static gchar * default_quote (AdgDim *dim);
66 static void update (AdgLDim *ldim);
67 static void clear (AdgLDim *ldim);
70 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
73 static void
74 adg_ldim_class_init(AdgLDimClass *klass)
76 GObjectClass *gobject_class;
77 AdgEntityClass *entity_class;
78 AdgDimClass *dim_class;
79 GParamSpec *param;
81 gobject_class = (GObjectClass *) klass;
82 entity_class = (AdgEntityClass *) klass;
83 dim_class = (AdgDimClass *) klass;
85 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
87 gobject_class->get_property = get_property;
88 gobject_class->set_property = set_property;
90 entity_class->model_matrix_changed = model_matrix_changed;
91 entity_class->paper_matrix_changed = paper_matrix_changed;
92 entity_class->invalidate = invalidate;
93 entity_class->render = render;
95 dim_class->default_quote = default_quote;
97 param = g_param_spec_double("direction",
98 P_("Direction"),
99 P_("The inclination angle of the extension lines"),
100 -G_MAXDOUBLE, G_MAXDOUBLE, ADG_DIR_RIGHT,
101 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
102 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
105 static void
106 adg_ldim_init(AdgLDim *ldim)
108 AdgLDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM,
109 AdgLDimPrivate);
111 data->direction = ADG_DIR_RIGHT;
113 data->path.status = CAIRO_STATUS_INVALID_PATH_DATA;
114 data->path.data = data->path_data;
115 data->path.num_data = 12;
117 data->path_data[0].header.type = CAIRO_PATH_MOVE_TO;
118 data->path_data[0].header.length = 2;
119 data->path_data[2].header.type = CAIRO_PATH_LINE_TO;
120 data->path_data[2].header.length = 2;
121 data->path_data[4].header.type = CAIRO_PATH_MOVE_TO;
122 data->path_data[4].header.length = 2;
123 data->path_data[6].header.type = CAIRO_PATH_LINE_TO;
124 data->path_data[6].header.length = 2;
125 data->path_data[8].header.type = CAIRO_PATH_MOVE_TO;
126 data->path_data[8].header.length = 2;
127 data->path_data[10].header.type = CAIRO_PATH_LINE_TO;
128 data->path_data[10].header.length = 2;
130 data->director.data = data->director_data;
131 data->director.num_data = 4;
132 data->director.path = NULL;
134 data->director_data[0].header.type = CAIRO_PATH_MOVE_TO;
135 data->director_data[0].header.length = 2;
136 data->director_data[2].header.type = CAIRO_PATH_LINE_TO;
137 data->director_data[2].header.length = 2;
139 ldim->data = data;
142 static void
143 get_property(GObject *object,
144 guint prop_id, GValue *value, GParamSpec *pspec)
146 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
148 switch (prop_id) {
149 case PROP_DIRECTION:
150 g_value_set_double(value, data->direction);
151 break;
152 default:
153 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
154 break;
158 static void
159 set_property(GObject *object,
160 guint prop_id, const GValue *value, GParamSpec *pspec)
162 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
164 switch (prop_id) {
165 case PROP_DIRECTION:
166 data->direction = g_value_get_double(value);
167 break;
168 default:
169 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
170 break;
176 * adg_ldim_new:
178 * Creates a new - unreferenced - linear dimension. You must, at least, define
179 * the reference points with adg_dim_set_ref(), the dimension direction with
180 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
181 * or, better, adg_ldim_set_pos().
183 * Return value: the new entity
185 AdgEntity *
186 adg_ldim_new(void)
188 return (AdgEntity *) g_object_new(ADG_TYPE_LDIM, NULL);
192 * adg_ldim_new_full:
193 * @ref1: the first reference point
194 * @ref2: the second reference point
195 * @direction: angle where to extend the dimension
196 * @pos: the position reference
198 * Creates a new linear dimension, specifing all the needed properties in
199 * one shot.
201 * Return value: the new entity
203 AdgEntity *
204 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
205 gdouble direction, const AdgPair *pos)
207 AdgEntity *entity = (AdgEntity *) g_object_new(ADG_TYPE_LDIM,
208 "ref1", ref1,
209 "ref2", ref2,
210 "direction", direction,
211 NULL);
212 adg_ldim_set_pos((AdgLDim *) entity, pos);
213 return entity;
217 * adg_ldim_new_full_explicit:
218 * @ref1_x: the x coordinate of the first reference point
219 * @ref1_y: the y coordinate of the first reference point
220 * @ref2_x: the x coordinate of the second reference point
221 * @ref2_y: the y coordinate of the second reference point
222 * @direction: angle where to extend the dimension
223 * @pos_x: the x coordinate of the position reference
224 * @pos_y: the y coordinate of the position reference
226 * Wrappes adg_ldim_new_full() with explicit quotes.
228 * Return value: the new entity
230 AdgEntity *
231 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
232 gdouble ref2_x, gdouble ref2_y,
233 gdouble direction, gdouble pos_x, gdouble pos_y)
235 AdgPair ref1;
236 AdgPair ref2;
237 AdgPair pos;
239 ref1.x = ref1_x;
240 ref1.y = ref1_y;
241 ref2.x = ref2_x;
242 ref2.y = ref2_y;
243 pos.x = pos_x;
244 pos.y = pos_y;
246 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
250 * adg_ldim_set_pos:
251 * @ldim: an #AdgLDim entity
252 * @pos: an #AdgPair structure
254 * Sets the position references (pos1 and pos2 properties) of @ldim using a
255 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
256 * points and the direction. If these conditions are not met, an error message
257 * is logged and the position references will not be set.
259 void
260 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
262 AdgLDimPrivate *data;
263 const AdgPair *ref1, *ref2;
264 AdgPair pos1, pos2;
265 CpmlPair baseline_vector, extension_vector;
266 gdouble d, k;
268 g_return_if_fail(ADG_IS_LDIM(ldim));
270 data = ldim->data;
271 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
272 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
274 cpml_vector_from_angle(&extension_vector, data->direction, 1);
276 baseline_vector.x = -extension_vector.y;
277 baseline_vector.y = extension_vector.x;
279 d = extension_vector.y * baseline_vector.x -
280 extension_vector.x * baseline_vector.y;
281 g_return_if_fail(d != 0);
283 k = ((pos->y - ref1->y) * baseline_vector.x -
284 (pos->x - ref1->x) * baseline_vector.y) / d;
285 pos1.x = ref1->x + k * extension_vector.x;
286 pos1.y = ref1->y + k * extension_vector.y;
288 k = ((pos->y - ref2->y) * baseline_vector.x -
289 (pos->x - ref2->x) * baseline_vector.y) / d;
290 pos2.x = ref2->x + k * extension_vector.x;
291 pos2.y = ref2->y + k * extension_vector.y;
293 adg_dim_set_pos((AdgDim *) ldim, &pos1, &pos2);
297 * adg_ldim_set_pos_explicit:
298 * @ldim: an #AdgLDim entity
299 * @pos_x: the new x coordinate position reference
300 * @pos_y: the new y coordinate position reference
302 * Wrappers adg_ldim_set_pos() with explicit coordinates.
304 void
305 adg_ldim_set_pos_explicit(AdgLDim *ldim, gdouble pos_x, gdouble pos_y)
307 AdgPair pos;
309 pos.x = pos_x;
310 pos.y = pos_y;
312 adg_ldim_set_pos(ldim, &pos);
316 * adg_ldim_get_direction:
317 * @ldim: an #AdgLDim entity
319 * Gets the direction where @ldim will extend.
321 * Return value: the direction angle in radians
323 gdouble
324 adg_ldim_get_direction(AdgLDim *ldim)
326 AdgLDimPrivate *data;
328 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
330 data = ldim->data;
332 return data->direction;
336 * adg_ldim_set_direction:
337 * @ldim: an #AdgLDim entity
338 * @direction: an angle value, in radians
340 * Sets the direction angle where to extend @ldim.
342 void
343 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
345 AdgLDimPrivate *data;
347 g_return_if_fail(ADG_IS_LDIM(ldim));
349 data = ldim->data;
350 data->direction = direction;
352 g_object_notify((GObject *) ldim, "direction");
356 static void
357 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
359 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
361 clear((AdgLDim *) entity);
363 if (entity_class->model_matrix_changed != NULL)
364 entity_class->model_matrix_changed(entity, parent_matrix);
367 static void
368 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
370 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
372 clear((AdgLDim *) entity);
374 if (entity_class->paper_matrix_changed != NULL)
375 entity_class->paper_matrix_changed(entity, parent_matrix);
378 static void
379 invalidate(AdgEntity *entity)
381 AdgEntityClass *entity_class = (AdgEntityClass *) adg_ldim_parent_class;
383 clear((AdgLDim *) entity);
385 if (entity_class->invalidate != NULL)
386 entity_class->invalidate(entity);
389 static void
390 render(AdgEntity *entity, cairo_t *cr)
392 AdgLDim *ldim;
393 AdgLDimPrivate *data;
394 AdgEntityClass *entity_class;
395 AdgStyle *dim_style;
396 AdgStyle *line_style;
397 AdgStyle *arrow_style;
399 ldim = (AdgLDim *) entity;
400 data = ldim->data;
401 entity_class = (AdgEntityClass *) adg_ldim_parent_class;
402 dim_style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
403 line_style = adg_dim_style_get_line_style((AdgDimStyle *) dim_style);
404 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
406 update(ldim);
408 cairo_save(cr);
409 adg_entity_apply(entity, ADG_SLOT_DIM_STYLE, cr);
411 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &data->director);
412 cpml_segment_reverse(&data->director);
413 adg_arrow_style_render((AdgArrowStyle *) arrow_style, cr, &data->director);
414 cpml_segment_reverse(&data->director);
416 adg_style_apply(line_style, cr);
418 cairo_append_path(cr, &data->path);
419 cairo_stroke(cr);
421 adg_dim_render_quote((AdgDim *) ldim, cr);
423 cairo_restore(cr);
425 if (entity_class->render != NULL)
426 entity_class->render(entity, cr);
429 static gchar *
430 default_quote(AdgDim *dim)
432 const AdgPair *pos1, *pos2;
433 AdgStyle *dim_style;
434 gdouble distance;
435 const gchar *format;
437 pos1 = adg_dim_get_pos1(dim);
438 pos2 = adg_dim_get_pos2(dim);
439 distance = cpml_pair_distance(pos1, pos2);
440 dim_style = adg_entity_get_style((AdgEntity *) dim, ADG_SLOT_DIM_STYLE);
441 format = adg_dim_style_get_number_format((AdgDimStyle *) dim_style);
443 return g_strdup_printf(format, distance);
446 static void
447 update(AdgLDim *ldim)
449 AdgLDimPrivate *data;
450 AdgStyle *dim_style;
451 AdgStyle *arrow_style;
452 const AdgPair *ref1, *ref2, *pos1, *pos2;
453 gdouble level;
454 gdouble baseline_spacing, from_offset;
455 cairo_path_data_t *baseline1, *baseline2;
456 cairo_path_data_t *from1, *to1, *from2, *to2;
457 cairo_path_data_t *arrow1, *arrow2;
458 AdgMatrix paper2model;
459 CpmlPair vector;
460 AdgPair offset;
461 gdouble angle;
463 data = ldim->data;
464 if (data->path.status == CAIRO_STATUS_SUCCESS)
465 return;
467 dim_style = adg_entity_get_style((AdgEntity *) ldim, ADG_SLOT_DIM_STYLE);
468 arrow_style = adg_dim_style_get_arrow_style((AdgDimStyle *) dim_style);
470 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
471 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
472 pos1 = adg_dim_get_pos1((AdgDim *) ldim);
473 pos2 = adg_dim_get_pos2((AdgDim *) ldim);
474 level = adg_dim_get_level((AdgDim *) ldim);
476 baseline_spacing = adg_dim_style_get_baseline_spacing((AdgDimStyle *) dim_style);
477 from_offset = adg_dim_style_get_from_offset((AdgDimStyle *) dim_style);
479 baseline1 = data->path_data + 1;
480 baseline2 = data->path_data + 3;
481 from1 = data->path_data + 5;
482 to1 = data->path_data + 7;
483 from2 = data->path_data + 9;
484 to2 = data->path_data + 11;
485 arrow1 = data->director_data + 1;
486 arrow2 = data->director_data + 3;
488 /* Get the inverted transformation matrix */
489 if (!adg_entity_build_paper2model((AdgEntity *) ldim, &paper2model))
490 return;
492 /* Set vector to the director of the extension lines */
493 cpml_vector_from_angle(&vector, data->direction, 1);
495 /* Calculate from1 and from2 */
496 offset.x = vector.x * from_offset;
497 offset.y = vector.y * from_offset;
498 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
500 from1->point.x = ref1->x + offset.x;
501 from1->point.y = ref1->y + offset.y;
502 from2->point.x = ref2->x + offset.x;
503 from2->point.y = ref2->y + offset.y;
505 /* Calculate arrow1 and arrow2 */
506 offset.x = vector.x * baseline_spacing * level;
507 offset.y = vector.y * baseline_spacing * level;
508 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
510 arrow1->point.x = pos1->x + offset.x;
511 arrow1->point.y = pos1->y + offset.y;
512 arrow2->point.x = pos2->x + offset.x;
513 arrow2->point.y = pos2->y + offset.y;
515 /* Calculate to1 and to2 */
516 offset.x = vector.x * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
517 offset.y = vector.y * adg_dim_style_get_to_offset((AdgDimStyle *) dim_style);
518 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
520 to1->point.x = arrow1->point.x + offset.x;
521 to1->point.y = arrow1->point.y + offset.y;
522 to2->point.x = arrow2->point.x + offset.x;
523 to2->point.y = arrow2->point.y + offset.y;
525 /* Set vector to the director of the baseline */
526 offset.x = arrow2->point.x - arrow1->point.x;
527 offset.y = arrow2->point.y - arrow1->point.y;
528 cpml_vector_set_length(cpml_pair_copy(&vector, &offset), 1);
530 /* Update the AdgDim cache contents */
531 adg_dim_set_org_explicit((AdgDim *) ldim,
532 (arrow1->point.x + arrow2->point.x) / 2.,
533 (arrow1->point.y + arrow2->point.y) / 2.);
534 angle = cpml_vector_angle(&vector);
535 adg_dim_set_angle((AdgDim *) ldim, angle);
537 /* Calculate baseline1 and baseline2 */
538 offset.y = adg_arrow_style_get_margin((AdgArrowStyle *) arrow_style);
539 offset.x = vector.x * offset.y;
540 offset.y *= vector.y;
541 cairo_matrix_transform_distance(&paper2model, &offset.x, &offset.y);
543 baseline1->point.x = arrow1->point.x + offset.x;
544 baseline1->point.y = arrow1->point.y + offset.y;
545 baseline2->point.x = arrow2->point.x - offset.x;
546 baseline2->point.y = arrow2->point.y - offset.y;
548 data->path.status = CAIRO_STATUS_SUCCESS;
551 static void
552 clear(AdgLDim *ldim)
554 AdgLDimPrivate *data = ldim->data;
556 data->path.status = CAIRO_STATUS_INVALID_PATH_DATA;