[AdgLDim] Added color dress for line styles
[adg.git] / adg / adg-ldim.c
blobc05bd71c78e414d68ce879c5cce72f5632f8a608
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 * @short_description: Linear dimensions
25 * The #AdgLDim entity represents a linear dimension.
26 **/
28 /**
29 * AdgLDim:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-ldim.h"
37 #include "adg-ldim-private.h"
38 #include "adg-dim-style.h"
39 #include "adg-line-style.h"
40 #include "adg-color-style.h"
41 #include "adg-arrow.h"
42 #include "adg-intl.h"
44 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_ldim_parent_class)
45 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_ldim_parent_class)
48 enum {
49 PROP_0,
50 PROP_DIRECTION,
51 PROP_HAS_EXTENSION1,
52 PROP_HAS_EXTENSION2
56 static void dispose (GObject *object);
57 static void get_property (GObject *object,
58 guint param_id,
59 GValue *value,
60 GParamSpec *pspec);
61 static void set_property (GObject *object,
62 guint param_id,
63 const GValue *value,
64 GParamSpec *pspec);
65 static gboolean invalidate (AdgEntity *entity);
66 static gboolean render (AdgEntity *entity,
67 cairo_t *cr);
68 static gchar * default_value (AdgDim *dim);
69 static void layout (AdgLDim *ldim);
70 static void update_matrices (AdgLDim *ldim,
71 AdgDimStyle *dim_style);
72 static void update_markers (AdgLDim *ldim);
73 static void dispose_markers (AdgLDim *ldim);
74 static CpmlPath * trail_callback (AdgTrail *trail,
75 gpointer user_data);
78 G_DEFINE_TYPE(AdgLDim, adg_ldim, ADG_TYPE_DIM);
81 static void
82 adg_ldim_class_init(AdgLDimClass *klass)
84 GObjectClass *gobject_class;
85 AdgEntityClass *entity_class;
86 AdgDimClass *dim_class;
87 GParamSpec *param;
89 gobject_class = (GObjectClass *) klass;
90 entity_class = (AdgEntityClass *) klass;
91 dim_class = (AdgDimClass *) klass;
93 g_type_class_add_private(klass, sizeof(AdgLDimPrivate));
95 gobject_class->dispose = dispose;
96 gobject_class->get_property = get_property;
97 gobject_class->set_property = set_property;
99 entity_class->invalidate = invalidate;
100 entity_class->render = render;
102 dim_class->default_value = default_value;
104 param = g_param_spec_double("direction",
105 P_("Direction"),
106 P_("The inclination angle of the extension lines"),
107 -G_MAXDOUBLE, G_MAXDOUBLE, ADG_DIR_RIGHT,
108 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
109 g_object_class_install_property(gobject_class, PROP_DIRECTION, param);
111 param = g_param_spec_boolean("has-extension1",
112 P_("Has First Extension Line flag"),
113 P_("Show (TRUE) or hide (FALSE) the first extension line"),
114 TRUE, G_PARAM_READWRITE);
115 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION1, param);
117 param = g_param_spec_boolean("has-extension2",
118 P_("Has Second Extension Line flag"),
119 P_("Show (TRUE) or hide (FALSE) the second extension line"),
120 TRUE, G_PARAM_READWRITE);
121 g_object_class_install_property(gobject_class, PROP_HAS_EXTENSION2, param);
124 static void
125 adg_ldim_init(AdgLDim *ldim)
127 AdgLDimPrivate *data;
128 cairo_path_data_t move_to, line_to;
130 data = G_TYPE_INSTANCE_GET_PRIVATE(ldim, ADG_TYPE_LDIM, AdgLDimPrivate);
131 move_to.header.type = CAIRO_PATH_MOVE_TO;
132 move_to.header.length = 2;
133 line_to.header.type = CAIRO_PATH_LINE_TO;
134 line_to.header.length = 2;
136 data->direction = 0;
137 data->has_extension1 = TRUE;
138 data->has_extension2 = TRUE;
140 data->cpml.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
141 data->cpml.path.data = data->cpml.data;
142 data->cpml.path.num_data = 12;
143 data->cpml.path.data[0] = move_to;
144 data->cpml.path.data[2] = line_to;
145 data->cpml.path.data[4] = move_to;
146 data->cpml.path.data[6] = line_to;
147 data->cpml.path.data[8] = move_to;
148 data->cpml.path.data[10] = line_to;
150 data->trail = NULL;
151 data->marker1 = NULL;
152 data->marker2 = NULL;
154 ldim->data = data;
157 static void
158 dispose(GObject *object)
160 dispose_markers((AdgLDim *) object);
162 if (PARENT_OBJECT_CLASS->dispose != NULL)
163 PARENT_OBJECT_CLASS->dispose(object);
166 static void
167 get_property(GObject *object,
168 guint prop_id, GValue *value, GParamSpec *pspec)
170 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
172 switch (prop_id) {
173 case PROP_DIRECTION:
174 g_value_set_double(value, data->direction);
175 break;
176 case PROP_HAS_EXTENSION1:
177 g_value_set_boolean(value, data->has_extension1);
178 break;
179 case PROP_HAS_EXTENSION2:
180 g_value_set_boolean(value, data->has_extension2);
181 break;
182 default:
183 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
184 break;
188 static void
189 set_property(GObject *object,
190 guint prop_id, const GValue *value, GParamSpec *pspec)
192 AdgLDimPrivate *data = ((AdgLDim *) object)->data;
194 switch (prop_id) {
195 case PROP_DIRECTION:
196 data->direction = g_value_get_double(value);
197 break;
198 case PROP_HAS_EXTENSION1:
199 data->has_extension1 = g_value_get_boolean(value);
200 break;
201 case PROP_HAS_EXTENSION2:
202 data->has_extension2 = g_value_get_boolean(value);
203 break;
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
206 break;
212 * adg_ldim_new:
214 * Creates a new - unreferenced - linear dimension. You must, at least, define
215 * the reference points with adg_dim_set_ref(), the dimension direction with
216 * adg_ldim_set_direction() and the position reference using adg_dim_set_pos()
217 * or, better, adg_ldim_set_pos().
219 * Returns: the newly created linear dimension entity
221 AdgLDim *
222 adg_ldim_new(void)
224 return g_object_new(ADG_TYPE_LDIM, NULL);
228 * adg_ldim_new_full:
229 * @ref1: the first reference point
230 * @ref2: the second reference point
231 * @direction: angle where to extend the dimension
232 * @pos: the position reference
234 * Creates a new linear dimension, specifing all the needed properties in
235 * one shot.
237 * Returns: the newly created linear dimension entity
239 AdgLDim *
240 adg_ldim_new_full(const AdgPair *ref1, const AdgPair *ref2,
241 gdouble direction, const AdgPair *pos)
243 AdgLDim *ldim = g_object_new(ADG_TYPE_LDIM, "ref1", ref1, "ref2", ref2,
244 "direction", direction, NULL);
245 adg_ldim_set_pos(ldim, pos);
246 return ldim;
250 * adg_ldim_new_full_explicit:
251 * @ref1_x: the x coordinate of the first reference point
252 * @ref1_y: the y coordinate of the first reference point
253 * @ref2_x: the x coordinate of the second reference point
254 * @ref2_y: the y coordinate of the second reference point
255 * @direction: angle where to extend the dimension
256 * @pos_x: the x coordinate of the position reference
257 * @pos_y: the y coordinate of the position reference
259 * Wrappes adg_ldim_new_full() with explicit values.
261 * Returns: the newly created linear dimension entity
263 AdgLDim *
264 adg_ldim_new_full_explicit(gdouble ref1_x, gdouble ref1_y,
265 gdouble ref2_x, gdouble ref2_y,
266 gdouble direction, gdouble pos_x, gdouble pos_y)
268 AdgPair ref1;
269 AdgPair ref2;
270 AdgPair pos;
272 ref1.x = ref1_x;
273 ref1.y = ref1_y;
274 ref2.x = ref2_x;
275 ref2.y = ref2_y;
276 pos.x = pos_x;
277 pos.y = pos_y;
279 return adg_ldim_new_full(&ref1, &ref2, direction, &pos);
283 * adg_ldim_set_pos:
284 * @ldim: an #AdgLDim entity
285 * @pos: an #AdgPair structure
287 * Sets the position references (pos1 and pos2 properties) of @ldim using a
288 * single @pos point. Before this call, @ldim MUST HAVE defined the reference
289 * points and the direction. If these conditions are not met, an error message
290 * is logged and the position references will not be set.
292 void
293 adg_ldim_set_pos(AdgLDim *ldim, const AdgPair *pos)
295 AdgLDimPrivate *data;
296 const AdgPair *ref1, *ref2;
297 AdgPair pos1, pos2;
298 CpmlPair baseline_vector, extension_vector;
299 gdouble d, k;
301 g_return_if_fail(ADG_IS_LDIM(ldim));
303 data = ldim->data;
304 ref1 = adg_dim_get_ref1((AdgDim *) ldim);
305 ref2 = adg_dim_get_ref2((AdgDim *) ldim);
307 cpml_vector_from_angle(&extension_vector, data->direction, 1);
309 baseline_vector.x = -extension_vector.y;
310 baseline_vector.y = extension_vector.x;
312 d = extension_vector.y * baseline_vector.x -
313 extension_vector.x * baseline_vector.y;
314 g_return_if_fail(d != 0);
316 k = ((pos->y - ref1->y) * baseline_vector.x -
317 (pos->x - ref1->x) * baseline_vector.y) / d;
318 pos1.x = ref1->x + k * extension_vector.x;
319 pos1.y = ref1->y + k * extension_vector.y;
321 k = ((pos->y - ref2->y) * baseline_vector.x -
322 (pos->x - ref2->x) * baseline_vector.y) / d;
323 pos2.x = ref2->x + k * extension_vector.x;
324 pos2.y = ref2->y + k * extension_vector.y;
326 adg_dim_set_pos((AdgDim *) ldim, &pos1, &pos2);
330 * adg_ldim_set_pos_explicit:
331 * @ldim: an #AdgLDim entity
332 * @pos_x: the new x coordinate position reference
333 * @pos_y: the new y coordinate position reference
335 * Wrappers adg_ldim_set_pos() with explicit coordinates.
337 void
338 adg_ldim_set_pos_explicit(AdgLDim *ldim, gdouble pos_x, gdouble pos_y)
340 AdgPair pos;
342 pos.x = pos_x;
343 pos.y = pos_y;
345 adg_ldim_set_pos(ldim, &pos);
349 * adg_ldim_get_direction:
350 * @ldim: an #AdgLDim entity
352 * Gets the direction where @ldim will extend.
354 * Returns: the direction angle in radians
356 gdouble
357 adg_ldim_get_direction(AdgLDim *ldim)
359 AdgLDimPrivate *data;
361 g_return_val_if_fail(ADG_IS_LDIM(ldim), 0);
363 data = ldim->data;
365 return data->direction;
369 * adg_ldim_set_direction:
370 * @ldim: an #AdgLDim entity
371 * @direction: an angle value, in radians
373 * Sets the direction angle where to extend @ldim.
375 void
376 adg_ldim_set_direction(AdgLDim *ldim, gdouble direction)
378 AdgLDimPrivate *data;
380 g_return_if_fail(ADG_IS_LDIM(ldim));
382 data = ldim->data;
383 data->direction = direction;
385 g_object_notify((GObject *) ldim, "direction");
389 * adg_ldim_has_extension1:
390 * @ldim: an #AdgLDim entity
392 * Checks if @ldim should render also the first extension line.
394 * Returns: %TRUE on first extension line presents, %FALSE otherwise
396 gboolean
397 adg_ldim_has_extension1(AdgLDim *ldim)
399 AdgLDimPrivate *data;
401 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
403 data = ldim->data;
405 return data->has_extension1;
409 * adg_ldim_switch_extension1:
410 * @ldim: an #AdgLDim entity
411 * @state: the new state
413 * Shows (if @state is %TRUE) or hide (if @state is %FALSE) the first
414 * extension line of @ldim.
416 void
417 adg_ldim_switch_extension1(AdgLDim *ldim, gboolean state)
419 AdgLDimPrivate *data;
421 g_return_if_fail(ADG_IS_LDIM(ldim));
423 data = ldim->data;
425 data->has_extension1 = state;
426 g_object_notify((GObject *) ldim, "has-extension1");
430 * adg_ldim_has_extension2:
431 * @ldim: an #AdgLDim entity
433 * Checks if @ldim should render also the second extension line.
435 * Returns: %TRUE on first extension line presents, %FALSE otherwise
437 gboolean
438 adg_ldim_has_extension2(AdgLDim *ldim)
440 AdgLDimPrivate *data;
442 g_return_val_if_fail(ADG_IS_LDIM(ldim), FALSE);
444 data = ldim->data;
446 return data->has_extension2;
450 * adg_ldim_switch_extension2:
451 * @ldim: an #AdgLDim entity
452 * @state: the new state
454 * Shows (if @state is %TRUE) or hide (if @state is %FALSE) the second
455 * extension line of @ldim.
457 void
458 adg_ldim_switch_extension2(AdgLDim *ldim, gboolean state)
460 AdgLDimPrivate *data;
462 g_return_if_fail(ADG_IS_LDIM(ldim));
464 data = ldim->data;
466 data->has_extension2 = state;
467 g_object_notify((GObject *) ldim, "has-extension2");
471 static gboolean
472 invalidate(AdgEntity *entity)
474 dispose_markers((AdgLDim *) entity);
476 if (PARENT_ENTITY_CLASS->invalidate != NULL)
477 return PARENT_ENTITY_CLASS->invalidate(entity);
479 return TRUE;
482 static gboolean
483 render(AdgEntity *entity, cairo_t *cr)
485 AdgLDim *ldim;
486 AdgDim *dim;
487 AdgLDimPrivate *data;
488 AdgDimStyle *dim_style;
489 AdgLineStyle *line_style;
490 AdgColorStyle *color_style;
492 ldim = (AdgLDim *) entity;
493 dim = (AdgDim *) entity;
494 data = ldim->data;
495 dim_style = (AdgDimStyle *)
496 adg_entity_style(entity, adg_dim_get_dress(dim));
497 line_style = (AdgLineStyle *)
498 adg_entity_style(entity, adg_dim_style_get_line_dress(dim_style));
499 color_style = (AdgColorStyle *)
500 adg_entity_style(entity, adg_line_style_get_color_dress(line_style));
502 if (!adg_entity_get_rendered(entity))
503 update_matrices(ldim, dim_style);
505 layout(ldim);
507 if (!adg_entity_get_rendered(entity))
508 update_markers(ldim);
510 adg_style_apply((AdgStyle *) dim_style, cr);
511 adg_style_apply((AdgStyle *) line_style, cr);
512 adg_style_apply((AdgStyle *) color_style, cr);
514 /* This CpmlPath has no arcs, so it can be feeded directly into cairo */
515 cairo_append_path(cr, &data->cpml.path);
516 cairo_stroke(cr);
518 if (data->marker1 != NULL)
519 adg_entity_render((AdgEntity *) data->marker1, cr);
521 if (data->marker2 != NULL)
522 adg_entity_render((AdgEntity *) data->marker2, cr);
524 adg_dim_render_quote(dim, cr);
526 return TRUE;
529 static gchar *
530 default_value(AdgDim *dim)
532 AdgEntity *entity;
533 const AdgPair *pos1, *pos2;
534 AdgStyle *dim_style;
535 gdouble distance;
536 const gchar *format;
538 entity = (AdgEntity *) dim;
539 pos1 = adg_dim_get_pos1(dim);
540 pos2 = adg_dim_get_pos2(dim);
541 distance = cpml_pair_distance(pos1, pos2);
542 dim_style = adg_entity_style(entity, adg_dim_get_dress(dim));
543 format = adg_dim_style_get_number_format((AdgDimStyle *) dim_style);
545 return g_strdup_printf(format, distance);
548 static void
549 layout(AdgLDim *ldim)
551 AdgEntity *entity;
552 AdgDim *dim;
553 AdgLDimPrivate *data;
554 AdgMatrix local;
555 AdgPair ref1, ref2, pos1, pos2;
556 AdgPair pair;
558 entity = (AdgEntity *) ldim;
559 dim = (AdgDim *) ldim;
560 data = ldim->data;
561 adg_entity_get_local_matrix((AdgEntity *) ldim, &local);
563 cpml_pair_copy(&ref1, adg_dim_get_ref1(dim));
564 cpml_pair_transform(&ref1, &local);
565 cpml_pair_copy(&ref2, adg_dim_get_ref2(dim));
566 cpml_pair_transform(&ref2, &local);
567 cpml_pair_copy(&pos1, adg_dim_get_pos1(dim));
568 cpml_pair_transform(&pos1, &local);
569 cpml_pair_copy(&pos2, adg_dim_get_pos2(dim));
570 cpml_pair_transform(&pos2, &local);
572 cpml_pair_add(cpml_pair_copy(&pair, &ref1), &data->from_shift);
573 cpml_pair_to_cairo(&pair, &data->cpml.data[5]);
575 cpml_pair_add(cpml_pair_copy(&pair, &pos1), &data->arrow_shift);
576 cpml_pair_to_cairo(&pair, &data->cpml.data[1]);
578 cpml_pair_add(&pair, &data->to_shift);
579 cpml_pair_to_cairo(&pair, &data->cpml.data[7]);
581 cpml_pair_add(cpml_pair_copy(&pair, &ref2), &data->from_shift);
582 cpml_pair_to_cairo(&pair, &data->cpml.data[9]);
584 cpml_pair_add(cpml_pair_copy(&pair, &pos2), &data->arrow_shift);
585 cpml_pair_to_cairo(&pair, &data->cpml.data[3]);
587 cpml_pair_add(&pair, &data->to_shift);
588 cpml_pair_to_cairo(&pair, &data->cpml.data[11]);
590 /* Play with header lengths to show or hide the extension lines */
591 if (data->has_extension1) {
592 if (data->has_extension2)
593 data->cpml.data[6].header.length = 2;
594 else
595 data->cpml.data[6].header.length = 6;
596 } else {
597 if (data->has_extension2)
598 data->cpml.data[2].header.length = 6;
599 else
600 data->cpml.data[2].header.length = 10;
603 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
605 /* TODO: temporary workaround. AdgDim should manage text rendering
606 * positioning using global/local matrix of an AdgContainer
607 * (to be implemented) of AdgToyText entities */
608 AdgPair org;
610 org.x = (data->cpml.data[1].point.x + data->cpml.data[3].point.x) / 2;
611 org.y = (data->cpml.data[1].point.y + data->cpml.data[3].point.y) / 2;
613 cairo_matrix_invert(&local);
614 cairo_matrix_transform_point(&local, &org.x, &org.y);
615 adg_dim_set_org(dim, &org);
616 adg_dim_set_angle(dim, data->direction + G_PI_2);
619 static void
620 update_matrices(AdgLDim *ldim, AdgDimStyle *dim_style)
622 AdgLDimPrivate *data;
623 AdgMatrix matrix;
624 gdouble from_offset;
625 gdouble to_offset;
626 gdouble baseline_spacing;
627 gdouble level;
629 data = ldim->data;
630 from_offset = adg_dim_style_get_from_offset(dim_style);
631 to_offset = adg_dim_style_get_to_offset(dim_style);
632 baseline_spacing = adg_dim_style_get_baseline_spacing(dim_style);
633 level = adg_dim_get_level((AdgDim *) ldim);
635 cairo_matrix_init_rotate(&matrix, data->direction);
637 data->from_shift.x = data->from_shift.y = 0;
638 data->arrow_shift.x = data->arrow_shift.y = 0;
639 data->to_shift.x = data->to_shift.y = 0;
641 cairo_matrix_translate(&matrix, from_offset, 0);
642 cpml_pair_transform(&data->from_shift, &matrix);
643 cairo_matrix_translate(&matrix, to_offset-from_offset, 0);
644 cpml_pair_transform(&data->to_shift, &matrix);
645 cairo_matrix_translate(&matrix, level*baseline_spacing-to_offset, 0);
646 cpml_pair_transform(&data->arrow_shift, &matrix);
649 static void
650 update_markers(AdgLDim *ldim)
652 AdgLDimPrivate *data = ldim->data;
654 if (data->trail == NULL)
655 data->trail = adg_trail_new(trail_callback, ldim);
657 if (data->marker1 == NULL)
658 data->marker1 = g_object_new(ADG_TYPE_ARROW,
659 "trail", data->trail, NULL);
661 if (data->marker2 == NULL)
662 data->marker2 = g_object_new(ADG_TYPE_ARROW,
663 "trail", data->trail, "pos", 1., NULL);
666 static void
667 dispose_markers(AdgLDim *ldim)
669 AdgLDimPrivate *data = ldim->data;
671 if (data->trail != NULL) {
672 g_object_unref(data->trail);
673 data->trail = NULL;
676 if (data->marker1 != NULL) {
677 g_object_unref(data->marker1);
678 data->marker1 = NULL;
681 if (data->marker2 != NULL) {
682 g_object_unref(data->marker2);
683 data->marker2 = NULL;
687 static CpmlPath *
688 trail_callback(AdgTrail *trail, gpointer user_data)
690 AdgLDim *ldim;
691 AdgLDimPrivate *data;
693 ldim = (AdgLDim *) user_data;
694 data = ldim->data;
696 adg_trail_clear_cairo_path(trail);
698 return &data->cpml.path;