doc: update copyright line for 2021
[adg.git] / src / adg / adg-rdim.c
blobd7500eefa0e7dc68f71582f630e180049b215711
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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-rdim
23 * @short_description: Radial dimensions
25 * The #AdgRDim entity represents a radial dimension.
27 * Since: 1.0
28 **/
30 /**
31 * AdgRDim:
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
36 * Since: 1.0
37 **/
40 #include "adg-internal.h"
41 #include "adg-container.h"
42 #include "adg-alignment.h"
43 #include "adg-model.h"
44 #include "adg-point.h"
45 #include "adg-trail.h"
46 #include "adg-marker.h"
47 #include "adg-style.h"
48 #include "adg-dim-style.h"
49 #include "adg-dress.h"
50 #include "adg-textual.h"
51 #include "adg-toy-text.h"
52 #include "adg-dim.h"
53 #include "adg-dim-private.h"
54 #include <math.h>
56 #include "adg-rdim.h"
57 #include "adg-rdim-private.h"
60 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_rdim_parent_class)
61 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_rdim_parent_class)
64 G_DEFINE_TYPE_WITH_PRIVATE(AdgRDim, adg_rdim, ADG_TYPE_DIM)
66 enum {
67 PROP_0,
71 static void _adg_dispose (GObject *object);
72 static void _adg_global_changed (AdgEntity *entity);
73 static void _adg_local_changed (AdgEntity *entity);
74 static void _adg_invalidate (AdgEntity *entity);
75 static void _adg_arrange (AdgEntity *entity);
76 static void _adg_render (AdgEntity *entity,
77 cairo_t *cr);
78 static gchar * _adg_default_value (AdgDim *dim);
79 static gboolean _adg_compute_geometry (AdgDim *dim);
80 static void _adg_update_entities (AdgRDim *rdim);
81 static void _adg_clear_trail (AdgRDim *rdim);
82 static void _adg_dispose_trail (AdgRDim *rdim);
83 static void _adg_dispose_marker (AdgRDim *rdim);
84 static cairo_path_t * _adg_trail_callback (AdgTrail *trail,
85 gpointer user_data);
88 static void
89 adg_rdim_class_init(AdgRDimClass *klass)
91 GObjectClass *gobject_class;
92 AdgEntityClass *entity_class;
93 AdgDimClass *dim_class;
95 gobject_class = (GObjectClass *) klass;
96 entity_class = (AdgEntityClass *) klass;
97 dim_class = (AdgDimClass *) klass;
99 gobject_class->dispose = _adg_dispose;
101 entity_class->global_changed = _adg_global_changed;
102 entity_class->local_changed = _adg_local_changed;
103 entity_class->invalidate = _adg_invalidate;
104 entity_class->arrange = _adg_arrange;
105 entity_class->render = _adg_render;
107 dim_class->default_value = _adg_default_value;
108 dim_class->compute_geometry = _adg_compute_geometry;
111 static void
112 adg_rdim_init(AdgRDim *rdim)
114 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
115 AdgStyle *style;
116 AdgDimStyle *dim_style;
117 cairo_path_data_t move_to, line_to;
119 move_to.header.type = CPML_MOVE;
120 move_to.header.length = 2;
121 line_to.header.type = CPML_LINE;
122 line_to.header.length = 2;
124 data->trail = NULL;
125 data->marker = NULL;
126 data->radius = -1.;
127 data->angle = 0.;
128 data->shift.base.x = data->shift.base.y = 0;
129 cairo_matrix_init_identity(&data->quote.global_map);
131 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
132 data->cairo.path.data = data->cairo.data;
133 data->cairo.path.num_data = G_N_ELEMENTS(data->cairo.data);
134 data->cairo.path.data[0] = move_to;
135 data->cairo.path.data[2] = line_to;
136 data->cairo.path.data[4] = move_to;
137 data->cairo.path.data[6] = line_to;
139 /* Override the default dimension style to prefix the quote with an R */
140 style = adg_dress_get_fallback(ADG_DRESS_DIMENSION);
141 dim_style = (AdgDimStyle *) adg_style_clone(style);
143 adg_dim_style_set_number_format(dim_style, "R%g");
145 adg_entity_set_style((AdgEntity *) rdim,
146 ADG_DRESS_DIMENSION,
147 (AdgStyle *) dim_style);
150 static void
151 _adg_dispose(GObject *object)
153 AdgRDim *rdim = (AdgRDim *) object;
155 _adg_dispose_trail(rdim);
156 _adg_dispose_marker(rdim);
158 if (_ADG_OLD_OBJECT_CLASS->dispose)
159 _ADG_OLD_OBJECT_CLASS->dispose(object);
164 * adg_rdim_new:
166 * Creates a new uninitialized radial dimension. To be useful, you
167 * need at least define the center of the arc to quote in #AdgDim:ref1,
168 * a point on the arc in #AdgDim:ref2 and the position of the quote
169 * in #AdgDim:pos using any valid #AdgDim method.
171 * Returns: a newly created quote
173 * Since: 1.0
175 AdgRDim *
176 adg_rdim_new(void)
178 return g_object_new(ADG_TYPE_RDIM, NULL);
182 * adg_rdim_new_full:
183 * @center: (allow-none): center of the arc to quote
184 * @radius: (allow-none): where the quote must be applied on the arc
185 * @pos: (allow-none): position of the quote text
187 * Creates a new quote by specifying explicitely all the needed
188 * data to get a valid quote.
190 * Returns: the newly created quote.
192 * Since: 1.0
194 AdgRDim *
195 adg_rdim_new_full(const CpmlPair *center, const CpmlPair *radius,
196 const CpmlPair *pos)
198 AdgRDim *rdim;
199 AdgDim *dim;
201 rdim = adg_rdim_new();
202 dim = (AdgDim *) rdim;
204 if (center != NULL)
205 adg_dim_set_ref1_from_pair(dim, center);
207 if (radius != NULL)
208 adg_dim_set_ref2_from_pair(dim, radius);
210 if (pos != NULL)
211 adg_dim_set_pos_from_pair(dim, pos);
213 return rdim;
217 * adg_rdim_new_full_explicit:
218 * @center_x: x coordinate of the center of the arc to quote
219 * @center_y: y coordinate of the center of the arc to quote
220 * @radius_x: x coordiante where the quote must be applied on the arc
221 * @radius_y: y coordiante where the quote must be applied on the arc
222 * @pos_x: x coordinate of the quote text
223 * @pos_y: y coordinate of the quote text
225 * Does the same job of adg_rdim_new_full() but using specific
226 * coordinates instead of #CpmlPair structures.
228 * Returns: the newly created quote.
230 * Since: 1.0
232 AdgRDim *
233 adg_rdim_new_full_explicit(gdouble center_x, gdouble center_y,
234 gdouble radius_x, gdouble radius_y,
235 gdouble pos_x, gdouble pos_y)
237 CpmlPair center, radius, pos;
239 center.x = center_x;
240 center.y = center_y;
241 radius.x = radius_x;
242 radius.y = radius_y;
243 pos.x = pos_x;
244 pos.y = pos_y;
246 return adg_rdim_new_full(&center, &radius, &pos);
250 * adg_rdim_new_full_from_model:
251 * @model: (transfer none): the model from which the named pairs are taken
252 * @center: (allow-none): the center point of the arc to quote
253 * @radius: (allow-none): an arbitrary point on the arc
254 * @pos: (allow-none): the position reference
256 * Creates a new radial dimension, specifing all the needed properties in
257 * one shot and using named pairs from @model.
259 * Returns: the newly created radial dimension entity
261 * Since: 1.0
263 AdgRDim *
264 adg_rdim_new_full_from_model(AdgModel *model, const gchar *center,
265 const gchar *radius, const gchar *pos)
267 AdgRDim *rdim;
268 AdgDim *dim;
270 g_return_val_if_fail(model != NULL, NULL);
272 rdim = adg_rdim_new();
273 dim = (AdgDim *) rdim;
275 if (center != NULL)
276 adg_dim_set_ref1_from_model(dim, model, center);
278 if (radius != NULL)
279 adg_dim_set_ref2_from_model(dim, model, radius);
281 if (pos != NULL)
282 adg_dim_set_pos_from_model(dim, model, pos);
284 return rdim;
288 static void
289 _adg_global_changed(AdgEntity *entity)
291 AdgRDim *rdim = (AdgRDim *) entity;
292 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
294 _adg_clear_trail(rdim);
296 if (_ADG_OLD_ENTITY_CLASS->global_changed)
297 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
299 if (data->marker != NULL)
300 adg_entity_global_changed((AdgEntity *) data->marker);
303 static void
304 _adg_local_changed(AdgEntity *entity)
306 _adg_clear_trail((AdgRDim *) entity);
308 if (_ADG_OLD_ENTITY_CLASS->local_changed)
309 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
312 static void
313 _adg_invalidate(AdgEntity *entity)
315 AdgRDim *rdim = (AdgRDim *) entity;
317 _adg_dispose_trail(rdim);
318 _adg_dispose_marker(rdim);
319 _adg_clear_trail(rdim);
321 if (_ADG_OLD_ENTITY_CLASS->invalidate)
322 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
325 static void
326 _adg_arrange(AdgEntity *entity)
328 AdgRDim *rdim;
329 AdgDim *dim;
330 AdgRDimPrivate *data;
331 AdgAlignment *quote;
332 AdgEntity *quote_entity;
333 gboolean outside;
334 const cairo_matrix_t *global, *local;
335 CpmlPair ref2, base;
336 CpmlPair pair;
337 CpmlExtents extents;
339 if (_ADG_OLD_ENTITY_CLASS->arrange != NULL)
340 _ADG_OLD_ENTITY_CLASS->arrange(entity);
342 dim = (AdgDim *) entity;
344 if (! adg_dim_compute_geometry(dim))
345 return;
347 rdim = (AdgRDim *) entity;
348 data = adg_rdim_get_instance_private(rdim);
349 quote = adg_dim_get_quote(dim);
350 quote_entity = (AdgEntity *) quote;
352 _adg_update_entities(rdim);
354 /* Check for cached result */
355 if (data->cairo.path.status == CAIRO_STATUS_SUCCESS) {
356 adg_entity_set_global_map(quote_entity, &data->quote.global_map);
357 return;
360 outside = adg_dim_get_outside(dim);
361 if (outside == ADG_THREE_STATE_UNKNOWN)
362 outside = ADG_THREE_STATE_OFF;
364 global = adg_entity_get_global_matrix(entity);
365 local = adg_entity_get_local_matrix(entity);
366 extents.is_defined = FALSE;
368 cpml_pair_copy(&ref2, (CpmlPair *) adg_dim_get_ref2(dim));
369 cpml_pair_copy(&base, &data->point.base);
371 cpml_pair_transform(&ref2, local);
372 cpml_pair_transform(&base, local);
373 base.x += data->shift.base.x;
374 base.y += data->shift.base.y;
376 /* baseline start */
377 cpml_pair_to_cairo(&base, &data->cairo.data[1]);
379 /* baseline end */
380 cpml_pair_to_cairo(&ref2, &data->cairo.data[3]);
382 if (outside) {
383 AdgDimStyle *dim_style;
384 gdouble beyond;
385 CpmlVector vector;
387 dim_style = adg_dim_get_dim_style(dim);
388 beyond = adg_dim_style_get_beyond(dim_style);
389 vector.x = ref2.x - base.x;
390 vector.y = ref2.y - base.y;
391 cpml_vector_set_length(&vector, beyond);
392 pair.x = ref2.x + vector.x;
393 pair.y = ref2.y + vector.y;
395 data->cairo.data[2].header.length = 2;
397 /* Outside segment start */
398 cpml_pair_to_cairo(&pair, &data->cairo.data[5]);
400 /* Outside segment end */
401 cpml_pair_to_cairo(&ref2, &data->cairo.data[7]);
402 } else {
403 data->cairo.data[2].header.length = 6;
406 /* Arrange the quote */
407 if (quote != NULL) {
408 cairo_matrix_t map;
409 gdouble x_align;
410 gdouble quote_angle = adg_dim_quote_angle(dim, data->angle);
412 x_align = cpml_angle_distance(quote_angle, data->angle) > G_PI_2 ? 0 : 1;
413 adg_alignment_set_factor_explicit(quote, x_align, 0);
415 cpml_pair_from_cairo(&pair, &data->cairo.data[1]);
416 cairo_matrix_init_translate(&map, pair.x, pair.y);
417 cairo_matrix_rotate(&map, quote_angle);
418 adg_entity_set_global_map(quote_entity, &map);
419 adg_entity_arrange(quote_entity);
420 cpml_extents_add(&extents, adg_entity_get_extents(quote_entity));
422 adg_matrix_copy(&data->quote.global_map, &map);
425 data->cairo.path.status = CAIRO_STATUS_SUCCESS;
427 /* Arrange the trail */
428 if (data->trail != NULL) {
429 CpmlExtents trail_extents;
430 cpml_extents_copy(&trail_extents, adg_trail_get_extents(data->trail));
431 cpml_extents_transform(&trail_extents, global);
432 cpml_extents_add(&extents, &trail_extents);
433 } else {
434 _adg_dispose_marker(rdim);
437 /* Arrange the marker */
438 if (data->marker != NULL) {
439 AdgEntity *marker_entity = (AdgEntity *) data->marker;
440 adg_marker_set_segment(data->marker, data->trail, outside ? 2 : 1);
441 adg_entity_local_changed(marker_entity);
442 adg_entity_arrange(marker_entity);
443 cpml_extents_add(&extents, adg_entity_get_extents(marker_entity));
446 adg_entity_set_extents(entity, &extents);
449 static void
450 _adg_render(AdgEntity *entity, cairo_t *cr)
452 AdgDim *dim;
453 AdgRDimPrivate *data;
454 AdgDimStyle *dim_style;
455 AdgDress dress;
456 const cairo_path_t *cairo_path;
458 dim = (AdgDim *) entity;
460 if (! adg_dim_compute_geometry(dim)) {
461 /* Entity not arranged, probably due to undefined pair found */
462 return;
465 data = adg_rdim_get_instance_private((AdgRDim *) dim);
466 dim_style = adg_dim_get_dim_style(dim);
468 adg_style_apply((AdgStyle *) dim_style, entity, cr);
469 adg_entity_render((AdgEntity *) adg_dim_get_quote(dim), cr);
471 if (data->marker != NULL)
472 adg_entity_render((AdgEntity *) data->marker, cr);
474 cairo_transform(cr, adg_entity_get_global_matrix(entity));
475 dress = adg_dim_style_get_line_dress(dim_style);
476 adg_entity_apply_dress(entity, dress, cr);
478 cairo_path = adg_trail_get_cairo_path(data->trail);
479 cairo_append_path(cr, cairo_path);
480 cairo_stroke(cr);
483 static gchar *
484 _adg_default_value(AdgDim *dim)
486 AdgRDimPrivate *data;
488 if (! adg_dim_compute_geometry(dim))
489 return g_strdup("undef");
491 data = adg_rdim_get_instance_private((AdgRDim *) dim);
492 return adg_dim_get_text(dim, data->radius);
495 static gboolean
496 _adg_compute_geometry(AdgDim *dim)
498 AdgRDimPrivate *data;
499 AdgDimStyle *dim_style;
500 AdgPoint *ref1_point, *ref2_point, *pos_point;
501 const CpmlPair *ref1, *ref2, *pos;
502 gdouble spacing, level, pos_distance;
503 CpmlVector vector;
505 ref1_point = adg_dim_get_ref1(dim);
506 if (! adg_point_update(ref1_point)) {
507 adg_dim_geometry_missing(dim, "ref1");
508 return FALSE;
511 ref2_point = adg_dim_get_ref2(dim);
512 if (! adg_point_update(ref2_point)) {
513 adg_dim_geometry_missing(dim, "ref2");
514 return FALSE;
517 pos_point = adg_dim_get_pos(dim);
518 if (! adg_point_update(pos_point)) {
519 adg_dim_geometry_missing(dim, "pos");
520 return FALSE;
523 ref1 = (CpmlPair *) ref1_point;
524 ref2 = (CpmlPair *) ref2_point;
525 if (cpml_pair_equal(ref1, ref2)) {
526 adg_dim_geometry_coincident(dim, "ref1", "ref2", ref1);
527 return FALSE;
530 data = adg_rdim_get_instance_private((AdgRDim *) dim);
531 pos = (CpmlPair *) pos_point;
532 dim_style = adg_dim_get_dim_style(dim);
533 spacing = adg_dim_style_get_baseline_spacing(dim_style);
534 level = adg_dim_get_level(dim);
535 pos_distance = cpml_pair_distance(pos, ref1);
536 vector.x = ref2->x - ref1->x;
537 vector.y = ref2->y - ref1->y;
539 if (cpml_pair_squared_distance(pos, ref1) <
540 cpml_pair_squared_distance(pos, ref2)) {
541 vector.x = -vector.x;
542 vector.y = -vector.y;
545 /* radius */
546 data->radius = cpml_pair_distance(&vector, NULL);
548 /* angle */
549 data->angle = cpml_vector_angle(&vector);
551 /* point.base */
552 cpml_pair_copy(&data->point.base, &vector);
553 cpml_vector_set_length(&data->point.base, pos_distance);
554 data->point.base.x += ref1->x;
555 data->point.base.y += ref1->y;
557 /* shift.base */
558 cpml_pair_copy(&data->shift.base, &vector);
559 cpml_vector_set_length(&data->shift.base, spacing * level);
561 return TRUE;
564 static void
565 _adg_update_entities(AdgRDim *rdim)
567 AdgEntity *entity = (AdgEntity *) rdim;
568 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
569 AdgDimStyle *dim_style = adg_dim_get_dim_style((AdgDim *) rdim);
571 if (data->trail == NULL)
572 data->trail = adg_trail_new(_adg_trail_callback, rdim);
574 if (data->marker == NULL) {
575 data->marker = adg_dim_style_marker2_new(dim_style);
576 adg_entity_set_parent((AdgEntity *) data->marker, entity);
580 static void
581 _adg_clear_trail(AdgRDim *rdim)
583 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
585 if (data->trail != NULL)
586 adg_model_clear((AdgModel *) data->trail);
588 data->cairo.path.status = CAIRO_STATUS_INVALID_PATH_DATA;
591 static void
592 _adg_dispose_trail(AdgRDim *rdim)
594 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
595 if (data->trail != NULL) {
596 g_object_unref(data->trail);
597 data->trail = NULL;
601 static void
602 _adg_dispose_marker(AdgRDim *rdim)
604 AdgRDimPrivate *data = adg_rdim_get_instance_private(rdim);
605 if (data->marker != NULL) {
606 g_object_unref(data->marker);
607 data->marker = NULL;
611 static cairo_path_t *
612 _adg_trail_callback(AdgTrail *trail, gpointer user_data)
614 AdgRDimPrivate *data = adg_rdim_get_instance_private((AdgRDim *) user_data);
615 return &data->cairo.path;