[AdgEntity] Changed "local-mode" to "normalized"
[adg.git] / adg / adg-marker.c
blobc1e2e15b7c4b1c581b6a597b3d5c0993aca5fc53
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-marker
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.
44 **/
46 /**
47 * AdgMarker:
49 * All fields are privates and should not be used directly.
50 * Use its public methods instead.
51 **/
54 #include "adg-marker.h"
55 #include "adg-marker-private.h"
56 #include "adg-intl.h"
58 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
59 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_marker_parent_class)
62 enum {
63 PROP_0,
64 PROP_TRAIL,
65 PROP_N_SEGMENT,
66 PROP_POS,
67 PROP_SIZE,
68 PROP_MODEL
72 static void dispose (GObject *object);
73 static void get_property (GObject *object,
74 guint prop_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void set_property (GObject *object,
78 guint prop_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void local_changed (AdgEntity *entity);
82 static void invalidate (AdgEntity *entity);
83 static gboolean set_segment (AdgMarker *marker,
84 AdgTrail *trail,
85 guint n_segment);
86 static void unset_trail (AdgMarker *marker);
87 static gboolean set_n_segment (AdgMarker *marker,
88 guint n_segment);
89 static gboolean set_pos (AdgMarker *marker,
90 gdouble pos);
91 static gboolean set_size (AdgMarker *marker,
92 gdouble size);
93 static gboolean set_model (AdgMarker *marker,
94 AdgModel *model);
95 static AdgModel * create_model (AdgMarker *marker);
98 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
101 static void
102 adg_marker_class_init(AdgMarkerClass *klass)
104 GObjectClass *gobject_class;
105 AdgEntityClass *entity_class;
106 GParamSpec *param;
108 gobject_class = (GObjectClass *) klass;
109 entity_class = (AdgEntityClass *) klass;
111 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
113 gobject_class->dispose = dispose;
114 gobject_class->set_property = set_property;
115 gobject_class->get_property = get_property;
117 entity_class->local_changed = local_changed;
118 entity_class->invalidate = invalidate;
120 klass->create_model = create_model;
122 param = g_param_spec_object("trail",
123 P_("Trail"),
124 P_("The subject AdgTrail for this marker"),
125 ADG_TYPE_TRAIL,
126 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
127 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
129 param = g_param_spec_uint("n-segment",
130 P_("Segment Index"),
131 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
132 0, G_MAXUINT, 0,
133 G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
136 param = g_param_spec_double("pos",
137 P_("Position"),
138 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
139 0, 1, 0,
140 G_PARAM_READWRITE);
141 g_object_class_install_property(gobject_class, PROP_POS, param);
143 param = g_param_spec_double("size",
144 P_("Marker Size"),
145 P_("The size (in global space) of the marker"),
146 0, G_MAXDOUBLE, 10,
147 G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_SIZE, param);
150 param = g_param_spec_object("model",
151 P_("Model"),
152 P_("A general purpose model usable by the marker implementations"),
153 ADG_TYPE_MODEL,
154 G_PARAM_READWRITE);
155 g_object_class_install_property(gobject_class, PROP_MODEL, param);
158 static void
159 adg_marker_init(AdgMarker *marker)
161 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
162 ADG_TYPE_MARKER,
163 AdgMarkerPrivate);
164 data->trail = NULL;
165 data->n_segment = 0;
166 data->backup_segment = NULL;
167 memset(&data->segment, 0, sizeof(data->segment));
168 data->pos = 0;
169 data->size = 10;
170 data->model = NULL;
172 marker->data = data;
175 static void
176 dispose(GObject *object)
178 AdgMarker *marker = (AdgMarker *) object;
180 adg_marker_set_model(marker, NULL);
181 adg_marker_set_segment(marker, NULL, 0);
183 if (PARENT_OBJECT_CLASS->dispose != NULL)
184 PARENT_OBJECT_CLASS->dispose(object);
188 static void
189 get_property(GObject *object,
190 guint prop_id, GValue *value, GParamSpec *pspec)
192 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
194 switch (prop_id) {
195 case PROP_TRAIL:
196 g_value_set_object(value, data->trail);
197 break;
198 case PROP_N_SEGMENT:
199 g_value_set_uint(value, data->n_segment);
200 break;
201 case PROP_POS:
202 g_value_set_double(value, data->pos);
203 break;
204 case PROP_SIZE:
205 g_value_set_double(value, data->size);
206 break;
207 case PROP_MODEL:
208 g_value_set_object(value, data->model);
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
212 break;
216 static void
217 set_property(GObject *object,
218 guint prop_id, const GValue *value, GParamSpec *pspec)
220 AdgMarker *marker = (AdgMarker *) object;
222 switch (prop_id) {
223 case PROP_TRAIL:
224 set_segment(marker, g_value_get_object(value), 1);
225 break;
226 case PROP_N_SEGMENT:
227 set_n_segment(marker, g_value_get_uint(value));
228 break;
229 case PROP_POS:
230 set_pos(marker, g_value_get_double(value));
231 break;
232 case PROP_SIZE:
233 set_size(marker, g_value_get_double(value));
234 break;
235 case PROP_MODEL:
236 set_model(marker, g_value_get_object(value));
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
240 break;
246 * adg_marker_get_trail:
247 * @marker: an #AdgMarker
249 * Gets the trail where this marker should be applied.
251 * Returns: the trail owned by @marker or %NULL on errors
253 AdgTrail *
254 adg_marker_get_trail(AdgMarker *marker)
256 AdgMarkerPrivate *data;
258 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
260 data = marker->data;
262 return data->trail;
266 * adg_marker_get_n_segment:
267 * @marker: an #AdgMarker
269 * Returns the segment of the associated trail where this marker
270 * will be applied, where %1 is the first segment.
272 * Returns: an index greather than %0 on success or %0 on errors
274 guint
275 adg_marker_get_n_segment(AdgMarker *marker)
277 AdgMarkerPrivate *data;
279 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
281 data = marker->data;
283 return data->n_segment;
287 * adg_marker_get_segment:
288 * @marker: an #AdgMarker
290 * <note><para>
291 * This function is only useful in marker implementations.
292 * </para></note>
294 * Gets the segment where the marker will be applied. This segment
295 * is eventually a modified version of the backup segment, after
296 * having applied the marker.
298 * Returns: the segment or %NULL on errors
300 AdgSegment *
301 adg_marker_get_segment(AdgMarker *marker)
303 AdgMarkerPrivate *data;
305 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
307 data = marker->data;
309 return &data->segment;
313 * adg_marker_set_segment:
314 * @marker: an #AdgMarker
315 * @trail: the #AdgTrail
316 * @n_segment: a segment index
318 * Sets the new segment where the marker should be applied. The weak
319 * reference to the old trail (if an old trail was present) is dropped
320 * while a new weak reference is added to @trail. If @trail is destroyed,
321 * the weak reference callback will automatically unset #AdgMarker:trai
322 * and will set #AdgMarker:n-segment to %0.
324 void
325 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
327 g_return_if_fail(ADG_IS_MARKER(marker));
329 if (set_segment(marker, trail, n_segment))
330 g_object_notify((GObject *) marker, "trail");
334 * adg_marker_get_backup_segment:
335 * @marker: an #AdgMarker
337 * <note><para>
338 * This function is only useful in marker implementations.
339 * </para></note>
341 * Gets the original segment where the marker has been applied.
342 * Applying a marker could modify the underlying trail, usually
343 * by trimming the original segment of a #AdgMarker:size dependent
344 * length from the ends. The marker instance holds a copy of the
345 * original segment, generated by adg_marker_backup_segment(),
346 * to be used in recomputation, for example when the marker
347 * changes its size.
349 * When the subject segment is changed (either by changing
350 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
351 * is automatically restored.
353 * Returns: the original segment or %NULL on errors
355 const AdgSegment *
356 adg_marker_get_backup_segment(AdgMarker *marker)
358 AdgMarkerPrivate *data;
360 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
362 data = marker->data;
364 return data->backup_segment;
368 * adg_marker_backup_segment:
369 * @marker: an #AdgMarker
371 * <note><para>
372 * This function is only useful in marker implementations.
373 * </para></note>
375 * Duplicates the current subject segment for backup purpose: this
376 * segment can be accessed by adg_marker_get_backup_segment().
377 * Obviously, a current segment should exist (either the
378 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
379 * properly defined) or this method will fail without further
380 * processing.
382 * When the subject segment is changed (either by changing
383 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
384 * is automatically restored.
386 void
387 adg_marker_backup_segment(AdgMarker *marker)
389 AdgMarkerPrivate *data;
391 g_return_if_fail(ADG_IS_MARKER(marker));
393 data = marker->data;
395 if (data->n_segment > 0) {
396 g_return_if_fail(data->trail != NULL);
398 g_free(data->backup_segment);
400 /* Backup the segment, if a segment to backup exists */
401 if (adg_trail_get_segment(data->trail,
402 &data->segment, data->n_segment))
403 data->backup_segment = adg_segment_deep_dup(&data->segment);
408 * adg_marker_get_pos:
409 * @marker: an #AdgMarker
411 * Gets the current position of @marker. The returned value is a ratio
412 * position referred to the segment associated to @marker: %0 means the
413 * start point and %1 means the end point of the segment.
415 * Returns: the marker position
417 gdouble
418 adg_marker_get_pos(AdgMarker *marker)
420 AdgMarkerPrivate *data;
422 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
424 data = marker->data;
426 return data->pos;
430 * adg_marker_set_pos:
431 * @marker: an #AdgMarker
432 * @pos: the new pos
434 * Sets a new position on @marker. Check out adg_marker_get_pos() for
435 * details on what @pos represents.
437 void
438 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
440 g_return_if_fail(ADG_IS_MARKER(marker));
442 if (set_pos(marker, pos))
443 g_object_notify((GObject *) marker, "pos");
447 * adg_marker_get_size:
448 * @marker: an #AdgMarker
450 * Gets the current size of @marker.
452 * Returns: the marker size, in global space
454 gdouble
455 adg_marker_get_size(AdgMarker *marker)
457 AdgMarkerPrivate *data;
459 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
461 data = marker->data;
463 return data->size;
467 * adg_marker_set_size:
468 * @marker: an #AdgMarker
469 * @size: the new size
471 * Sets a new size on @marker. The @size is an implementation-dependent
472 * property: it has meaning only when used by an #AdgMarker derived type.
474 void
475 adg_marker_set_size(AdgMarker *marker, gdouble size)
477 g_return_if_fail(ADG_IS_MARKER(marker));
479 if (set_size(marker, size))
480 g_object_notify((GObject *) marker, "size");
484 * adg_marker_model:
485 * @marker: an #AdgMarker
487 * <note><para>
488 * This function is only useful in marker implementations.
489 * </para></note>
491 * Gets the model of @marker. If the model is not found, it is
492 * automatically created by calling the create_model() virtual method.
494 * Returns: the current model owned by @marker or %NULL on errors
496 AdgModel *
497 adg_marker_model(AdgMarker *marker)
499 AdgMarkerPrivate *data;
501 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
503 data = marker->data;
505 if (data->model == NULL) {
506 /* Model not found: regenerate it */
507 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
509 adg_marker_set_model(marker, marker_class->create_model(marker));
512 return data->model;
516 * adg_marker_get_model:
517 * @marker: an #AdgMarker
519 * <note><para>
520 * This function is only useful in marker implementations.
521 * </para></note>
523 * Gets the current model of @marker. This is an accessor method:
524 * if you need to get the model for rendering, use adg_marker_model()
525 * instead.
527 * Returns: the cached model owned by @marker or %NULL on errors
529 AdgModel *
530 adg_marker_get_model(AdgMarker *marker)
532 AdgMarkerPrivate *data;
534 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
536 data = marker->data;
538 return data->model;
542 * adg_marker_set_model:
543 * @marker: an #AdgMarker
544 * @model: a new #AdgModel
546 * <note><para>
547 * This function is only useful in marker implementations.
548 * </para></note>
550 * Sets a new model for @marker. The reference to the old model (if an
551 * old model was present) is dropped while a new reference is added to
552 * @model.
554 void
555 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
557 g_return_if_fail(ADG_IS_MARKER(marker));
559 if (set_model(marker, model))
560 g_object_notify((GObject *) marker, "model");
564 static void
565 local_changed(AdgEntity *entity)
567 AdgMarkerPrivate *data;
568 CpmlPair pair;
569 CpmlVector vector;
570 AdgMatrix map;
572 data = ((AdgMarker *) entity)->data;
573 if (data->trail == NULL)
574 return;
576 cpml_segment_pair_at(&data->segment, &pair, data->pos);
577 cpml_segment_vector_at(&data->segment, &vector, data->pos);
578 cpml_vector_set_length(&vector, data->size);
580 if (data->pos > 0.5) {
581 vector.x = -vector.x;
582 vector.y = -vector.y;
585 cairo_matrix_init(&map, vector.x, vector.y,
586 -vector.y, vector.x, pair.x, pair.y);
588 adg_entity_set_local_map(entity, &map);
590 PARENT_ENTITY_CLASS->local_changed(entity);
593 static void
594 invalidate(AdgEntity *entity)
596 adg_marker_set_model((AdgMarker *) entity, NULL);
600 static gboolean
601 set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
603 AdgMarkerPrivate *data;
604 AdgEntity *entity;
606 data = marker->data;
607 entity = (AdgEntity *) marker;
609 if (trail == data->trail && n_segment == data->n_segment)
610 return FALSE;
612 if (data->trail != NULL) {
613 /* Restore the original segment in the old trail */
614 set_n_segment(marker, 0);
616 g_object_weak_unref((GObject *) data->trail,
617 (GWeakNotify) unset_trail, marker);
618 adg_model_remove_dependency((AdgModel *) data->trail, entity);
621 data->trail = trail;
622 data->n_segment = 0;
624 if (data->trail != NULL) {
625 g_object_weak_ref((GObject *) data->trail,
626 (GWeakNotify) unset_trail, marker);
627 adg_model_add_dependency((AdgModel *) data->trail, entity);
629 set_n_segment(marker, n_segment);
632 return TRUE;
635 static void
636 unset_trail(AdgMarker *marker)
638 AdgMarkerPrivate *data = marker->data;
640 if (data->trail != NULL)
641 set_segment(marker, NULL, 0);
644 static gboolean
645 set_n_segment(AdgMarker *marker, guint n_segment)
647 AdgMarkerPrivate *data = marker->data;
649 if (n_segment == data->n_segment)
650 return FALSE;
652 if (data->backup_segment != NULL) {
653 /* Restore the original segment, if any */
654 if (data->trail != NULL)
655 adg_segment_deep_copy(&data->segment, data->backup_segment);
657 g_free(data->backup_segment);
658 data->backup_segment = NULL;
661 data->n_segment = n_segment;
663 if (n_segment == 0) {
664 memset(&data->segment, 0, sizeof(data->segment));
665 return TRUE;
668 return adg_trail_get_segment(data->trail, &data->segment, n_segment);
671 static gboolean
672 set_pos(AdgMarker *marker, gdouble pos)
674 AdgMarkerPrivate *data = marker->data;
676 if (pos == data->pos)
677 return FALSE;
679 data->pos = pos;
681 return TRUE;
684 static gboolean
685 set_size(AdgMarker *marker, gdouble size)
687 AdgMarkerPrivate *data = marker->data;
689 if (size == data->size)
690 return FALSE;
692 data->size = size;
694 return TRUE;
697 static gboolean
698 set_model(AdgMarker *marker, AdgModel *model)
700 AdgMarkerPrivate *data = marker->data;
702 if (model == data->model)
703 return FALSE;
705 if (data->model != NULL)
706 g_object_unref((GObject *) data->model);
708 data->model = model;
710 if (data->model != NULL) {
711 g_object_ref((GObject *) data->model);
712 adg_entity_local_changed((AdgEntity *) marker);
715 return TRUE;
718 static AdgModel *
719 create_model(AdgMarker *marker)
721 g_warning("%s: `create_model' method not implemented for type `%s'",
722 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
723 return NULL;