1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2017 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.
23 * @short_description: Base entity for markers
25 * A marker is an entity to be applied at the start or end of a segment.
26 * Typical examples include arrows, ticks, dots and so on.
28 * The #AdgMarker:trail and #AdgMarker:n-segment properties specify the
29 * segment where the marker should be applied. Similarly to the
30 * #AdgStroke type, if the associated trail is destroyed the above
31 * properties are unset.
33 * The local map is used internally to align the marker to the trail end,
34 * so adg_entity_set_local_map() and friends is reserved. Therefore, if
35 * the trail is modified and the marker had no way to know it, you should
36 * call adg_entity_local_changed() to update the marker position.
38 * Use adg_marker_set_pos() to select the position where the marker
39 * should be put: 0 means the start point of the segment while 1
40 * means the end point.
42 * The #AdgMarker:model property and APIs are intended only for marker
43 * implementation purposes.
51 * All fields are privates and should not be used directly.
52 * Use its public methods instead.
59 * @create_model: abstract virtual method that creates a model template for
60 * all the markers used by this class.
62 * The @create_model method must be implemented by any #AdgMarker derived
63 * classes. The derived classes are expected to apply a single model
64 * (the one returned by this method) to every path endings by using
65 * different transformations.
71 #include "adg-internal.h"
72 #include "adg-model.h"
73 #include "adg-trail.h"
76 #include "adg-marker.h"
77 #include "adg-marker-private.h"
80 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
81 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_marker_parent_class)
84 G_DEFINE_ABSTRACT_TYPE(AdgMarker
, adg_marker
, ADG_TYPE_ENTITY
)
96 static void _adg_dispose (GObject
*object
);
97 static void _adg_get_property (GObject
*object
,
101 static void _adg_set_property (GObject
*object
,
105 static void _adg_local_changed (AdgEntity
*entity
);
106 static void _adg_invalidate (AdgEntity
*entity
);
107 static void _adg_clear_trail (AdgMarker
*marker
);
108 static gboolean
_adg_set_segment (AdgMarker
*marker
,
111 static AdgModel
* _adg_create_model (AdgMarker
*marker
);
115 adg_marker_class_init(AdgMarkerClass
*klass
)
117 GObjectClass
*gobject_class
;
118 AdgEntityClass
*entity_class
;
121 gobject_class
= (GObjectClass
*) klass
;
122 entity_class
= (AdgEntityClass
*) klass
;
124 g_type_class_add_private(klass
, sizeof(AdgMarkerPrivate
));
126 gobject_class
->dispose
= _adg_dispose
;
127 gobject_class
->set_property
= _adg_set_property
;
128 gobject_class
->get_property
= _adg_get_property
;
130 entity_class
->local_changed
= _adg_local_changed
;
131 entity_class
->invalidate
= _adg_invalidate
;
133 klass
->create_model
= _adg_create_model
;
135 param
= g_param_spec_object("trail",
137 P_("The subject AdgTrail for this marker"),
139 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
140 g_object_class_install_property(gobject_class
, PROP_TRAIL
, param
);
142 param
= g_param_spec_uint("n-segment",
144 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
147 g_object_class_install_property(gobject_class
, PROP_N_SEGMENT
, param
);
149 param
= g_param_spec_double("pos",
151 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
153 G_PARAM_READWRITE
|G_PARAM_CONSTRUCT
);
154 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
156 param
= g_param_spec_double("size",
158 P_("The size (in global space) of the marker"),
161 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
163 param
= g_param_spec_object("model",
165 P_("A general purpose model usable by the marker implementations"),
168 g_object_class_install_property(gobject_class
, PROP_MODEL
, param
);
172 adg_marker_init(AdgMarker
*marker
)
174 AdgMarkerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(marker
,
179 data
->backup_segment
= NULL
;
180 memset(&data
->segment
, 0, sizeof(data
->segment
));
189 _adg_dispose(GObject
*object
)
191 AdgMarker
*marker
= (AdgMarker
*) object
;
193 adg_marker_set_model(marker
, NULL
);
194 adg_marker_set_segment(marker
, NULL
, 0);
196 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
197 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
202 _adg_get_property(GObject
*object
, guint prop_id
,
203 GValue
*value
, GParamSpec
*pspec
)
205 AdgMarkerPrivate
*data
= ((AdgMarker
*) object
)->data
;
209 g_value_set_object(value
, data
->trail
);
212 g_value_set_uint(value
, data
->n_segment
);
215 g_value_set_double(value
, data
->pos
);
218 g_value_set_double(value
, data
->size
);
221 g_value_set_object(value
, data
->model
);
224 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
230 _adg_set_property(GObject
*object
, guint prop_id
,
231 const GValue
*value
, GParamSpec
*pspec
)
233 AdgMarker
*marker
= (AdgMarker
*) object
;
234 AdgMarkerPrivate
*data
= ((AdgMarker
*) object
)->data
;
239 _adg_set_segment(marker
, g_value_get_object(value
), data
->n_segment
);
242 _adg_set_segment(marker
, data
->trail
, g_value_get_uint(value
));
245 data
->pos
= g_value_get_double(value
);
248 data
->size
= g_value_get_double(value
);
251 old_model
= data
->model
;
252 data
->model
= g_value_get_object(value
);
255 g_object_ref((GObject
*) data
->model
);
256 adg_entity_local_changed((AdgEntity
*) object
);
259 g_object_unref(old_model
);
262 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
269 * adg_marker_set_trail:
270 * @marker: an #AdgMarker
271 * @trail: the new trail to use
273 * Sets the #AdgMarker:trail property to @trail. It is allowed to
274 * pass <constant>NULL</constant> to clear the current trail.
276 * This method could fail unexpectedly if the segment index specified
277 * by the #AdgMarker:n-segment property is not present inside the new
278 * segment: if you want to set a new segment it is more convenient to
279 * change both properties (#AdgMarker:trail and #AdgMarker:n-segment)
280 * at once with adg_marker_set_segment().
285 adg_marker_set_trail(AdgMarker
*marker
, AdgTrail
*trail
)
287 g_return_if_fail(ADG_IS_MARKER(marker
));
288 g_object_set(marker
, "trail", trail
, NULL
);
292 * adg_marker_get_trail:
293 * @marker: an #AdgMarker
295 * Gets the trail where this marker should be applied.
296 * The returned object is owned by @marker and should not be
299 * Returns: (transfer none): the requested trail or <constant>NULL</constant> on errors.
304 adg_marker_get_trail(AdgMarker
*marker
)
306 AdgMarkerPrivate
*data
;
308 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
316 * adg_marker_set_n_segment:
317 * @marker: an #AdgMarker
318 * @n_segment: the new segment index
320 * Sets the #AdgMarker:n-segment property to @n_segment. The trail
321 * is unchanged. If you want to set both properties at once (as
322 * usually requested to refer to a specific segment),
323 * adg_marker_set_segment() should be more convenient.
328 adg_marker_set_n_segment(AdgMarker
*marker
, guint n_segment
)
330 g_return_if_fail(ADG_IS_MARKER(marker
));
331 g_object_set(marker
, "n-segment", n_segment
, NULL
);
335 * adg_marker_get_n_segment:
336 * @marker: an #AdgMarker
338 * Returns the segment of the associated trail where this marker
339 * will be applied, where 1 is the first segment.
341 * Returns: an index greather than 0 on success or 0 on errors.
346 adg_marker_get_n_segment(AdgMarker
*marker
)
348 AdgMarkerPrivate
*data
;
350 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
354 return data
->n_segment
;
358 * adg_marker_set_segment:
359 * @marker: an #AdgMarker
360 * @trail: the #AdgTrail
361 * @n_segment: a segment index
363 * Sets a new segment where the marker should be applied at once.
364 * A dependency between @trail and @marker is added, so when @trail
365 * changes @marker is invalidated.
367 * A callback is added to #AdgModel::remove-dependency so manually
368 * removing the dependency (such as when @trail is destroyed) will
369 * unlink @marker from it.
374 adg_marker_set_segment(AdgMarker
*marker
, AdgTrail
*trail
, guint n_segment
)
376 g_return_if_fail(ADG_IS_MARKER(marker
));
377 /* To avoid referring to an inexistent trail/n-segment couple, the
378 * "n-segment" property is reset before to avoid segment validation */
379 g_object_set(marker
, "n-segment", 0,
380 "trail", trail
, "n-segment", n_segment
, NULL
);
384 * adg_marker_get_segment:
385 * @marker: an #AdgMarker
388 * This function is only useful in marker implementations.
391 * Gets the segment where the marker will be applied. This segment
392 * is eventually a modified version of the backup segment, after
393 * having applied the marker.
395 * Returns: the segment or <constant>NULL</constant> on errors.
400 adg_marker_get_segment(AdgMarker
*marker
)
402 AdgMarkerPrivate
*data
;
404 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
408 return &data
->segment
;
412 * adg_marker_backup_segment:
413 * @marker: an #AdgMarker
416 * This function is only useful in marker implementations.
419 * Duplicates the current subject segment for backup purpose: this
420 * segment can be accessed by adg_marker_get_backup_segment().
422 * A current segment should exist (i.e. both #AdgMarker:trail and
423 * #AdgMarker:n-segment properties must be properly set) or this
424 * method will fail without further processing.
426 * When the subject segment is changed (either by changing
427 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
428 * is automatically restored.
433 adg_marker_backup_segment(AdgMarker
*marker
)
435 AdgMarkerPrivate
*data
;
437 g_return_if_fail(ADG_IS_MARKER(marker
));
441 if (data
->n_segment
> 0) {
442 g_return_if_fail(data
->trail
!= NULL
);
444 g_free(data
->backup_segment
);
446 /* Backup the segment, if a segment to backup exists */
447 if (adg_trail_put_segment(data
->trail
, data
->n_segment
,
449 data
->backup_segment
= cpml_segment_deep_dup(&data
->segment
);
451 data
->backup_segment
= NULL
;
456 * adg_marker_get_backup_segment:
457 * @marker: an #AdgMarker
460 * This function is only useful in marker implementations.
463 * Gets the original segment where the marker has been applied.
464 * Applying a marker could modify the underlying trail, usually
465 * by trimming the original segment of a #AdgMarker:size dependent
466 * length from the ends. The marker instance holds a copy of the
467 * original segment, generated by adg_marker_backup_segment(),
468 * to be used in recomputation, for example when the marker
471 * When the subject segment is changed (either by changing
472 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
473 * is automatically restored.
475 * Returns: the original segment or <constant>NULL</constant> on errors.
480 adg_marker_get_backup_segment(AdgMarker
*marker
)
482 AdgMarkerPrivate
*data
;
484 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
488 return data
->backup_segment
;
492 * adg_marker_set_pos:
493 * @marker: an #AdgMarker
496 * Sets a new position on @marker. Check out adg_marker_get_pos() for
497 * details on what @pos represents.
502 adg_marker_set_pos(AdgMarker
*marker
, gdouble pos
)
504 g_return_if_fail(ADG_IS_MARKER(marker
));
505 g_object_set(marker
, "pos", pos
, NULL
);
509 * adg_marker_get_pos:
510 * @marker: an #AdgMarker
512 * Gets the current position of @marker. The returned value is a ratio
513 * position referred to the segment associated to @marker: 0 means the
514 * start point and 1 means the end point of the segment.
516 * Returns: the marker position.
521 adg_marker_get_pos(AdgMarker
*marker
)
523 AdgMarkerPrivate
*data
;
525 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
533 * adg_marker_set_size:
534 * @marker: an #AdgMarker
535 * @size: the new size
537 * Sets a new size on @marker. The @size is an implementation-dependent
538 * property: it has meaning only when used by an #AdgMarker derived type.
543 adg_marker_set_size(AdgMarker
*marker
, gdouble size
)
545 g_return_if_fail(ADG_IS_MARKER(marker
));
546 g_object_set(marker
, "size", size
, NULL
);
550 * adg_marker_get_size:
551 * @marker: an #AdgMarker
553 * Gets the current size of @marker.
555 * Returns: the marker size, in global space
560 adg_marker_get_size(AdgMarker
*marker
)
562 AdgMarkerPrivate
*data
;
564 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
572 * adg_marker_set_model:
573 * @marker: an #AdgMarker
574 * @model: a new #AdgModel
577 * This function is only useful in marker implementations.
580 * Sets a new model for @marker. The reference to the old model (if an
581 * old model was present) is dropped while a new reference is added to
587 adg_marker_set_model(AdgMarker
*marker
, AdgModel
*model
)
589 g_return_if_fail(ADG_IS_MARKER(marker
));
590 g_object_set(marker
, "model", model
, NULL
);
594 * adg_marker_get_model:
595 * @marker: an #AdgMarker
598 * This function is only useful in marker implementations.
601 * Gets the current model of @marker. This is an accessor method:
602 * if you need to get the model for rendering, use adg_marker_model()
603 * instead. The returned object is owned by @marker and should not be
606 * Returns: (transfer none): the cached model or <constant>NULL</constant> on errors.
611 adg_marker_get_model(AdgMarker
*marker
)
613 AdgMarkerPrivate
*data
;
615 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
624 * @marker: an #AdgMarker
627 * This function is only useful in marker implementations.
630 * Gets the model of @marker. If the model is not found, it is
631 * automatically created by calling the <function>create_model</function>
632 * virtual method. The returned object is owned by @marker and
633 * should not be freed or modified.
635 * Returns: (transfer none): the current model or <constant>NULL</constant> on errors.
640 adg_marker_model(AdgMarker
*marker
)
642 AdgMarkerPrivate
*data
;
644 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
648 if (data
->model
== NULL
) {
649 /* Model not found: regenerate it */
650 AdgMarkerClass
*marker_class
= ADG_MARKER_GET_CLASS(marker
);
652 if (marker_class
->create_model
)
653 adg_marker_set_model(marker
, marker_class
->create_model(marker
));
661 _adg_local_changed(AdgEntity
*entity
)
663 AdgMarkerPrivate
*data
= ((AdgMarker
*) entity
)->data
;
665 /* On invalid segments, segment.data is not set: do not crash */
666 if (data
->segment
.data
!= NULL
) {
671 cpml_segment_put_pair_at(&data
->segment
, data
->pos
, &pair
);
672 cpml_segment_put_vector_at(&data
->segment
, data
->pos
, &vector
);
673 cpml_vector_set_length(&vector
, data
->size
);
675 if (data
->pos
> 0.5) {
676 vector
.x
= -vector
.x
;
677 vector
.y
= -vector
.y
;
680 cairo_matrix_init(&map
, vector
.x
, vector
.y
,
681 -vector
.y
, vector
.x
, pair
.x
, pair
.y
);
683 adg_entity_set_local_map(entity
, &map
);
687 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
688 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
692 _adg_invalidate(AdgEntity
*entity
)
694 adg_marker_set_model((AdgMarker
*) entity
, NULL
);
699 _adg_clear_trail(AdgMarker
*marker
)
701 AdgMarkerPrivate
*data
= marker
->data
;
703 if (data
->trail
&& data
->backup_segment
) {
704 /* Restore the original segment in the old trail */
705 cpml_segment_copy_data(&data
->segment
, data
->backup_segment
);
706 g_free(data
->backup_segment
);
707 data
->backup_segment
= NULL
;
715 _adg_set_segment(AdgMarker
*marker
, AdgTrail
*trail
, guint n_segment
)
717 AdgMarkerPrivate
*data
;
718 CpmlSegment segment
= { 0 };
720 g_return_val_if_fail(trail
== NULL
|| ADG_IS_TRAIL(trail
), FALSE
);
722 /* Segment validation, but only if trail and n_segment are specified */
723 if (trail
&& n_segment
> 0 && !adg_trail_put_segment(trail
, n_segment
, &segment
))
728 /* Do not try to cache results! Although @trail and @n_segment
729 * could be the same, the internal CpmlSegment could change.
730 * This is the case when AdgLDim arranges the layout and changes
731 * the path data (to force outside arrows for example) reusing
732 * the same CpmlPath. In other words, do not do this:
734 * if (trail == data->trail && n_segment == data->n_segment)
737 * Incidentally, on a 64 bit platform this issue has been never
738 * exposed. Avoiding the cache will solve the issue 33:
740 * http://dev.entidi.com/p/adg/issues/33/
743 if (trail
!= data
->trail
) {
744 AdgEntity
*entity
= (AdgEntity
*) marker
;
747 adg_model_remove_dependency((AdgModel
*) data
->trail
, entity
);
752 adg_model_add_dependency((AdgModel
*) trail
, entity
);
753 g_signal_connect_swapped(trail
, "remove-dependency",
754 G_CALLBACK(_adg_clear_trail
), marker
);
758 cpml_segment_copy(&data
->segment
, &segment
);
759 data
->n_segment
= n_segment
;
765 _adg_create_model(AdgMarker
*marker
)
767 g_warning(_("%s: 'create_model' method not implemented for type '%s'"),
768 G_STRLOC
, g_type_name(G_OBJECT_TYPE(marker
)));