[AdgRuledFill] Improved ruled creation algorithm
[adg.git] / adg / adg-marker.c
blobf65350f65949dec0e88e1a2be67ab61f5ad01bef
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 adg_entity_set_local_mode((AdgEntity *) marker, ADG_TRANSFORM_BEFORE);
174 marker->data = data;
177 static void
178 dispose(GObject *object)
180 AdgMarker *marker = (AdgMarker *) object;
182 adg_marker_set_model(marker, NULL);
183 adg_marker_set_segment(marker, NULL, 0);
185 if (PARENT_OBJECT_CLASS->dispose != NULL)
186 PARENT_OBJECT_CLASS->dispose(object);
190 static void
191 get_property(GObject *object,
192 guint prop_id, GValue *value, GParamSpec *pspec)
194 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
196 switch (prop_id) {
197 case PROP_TRAIL:
198 g_value_set_object(value, data->trail);
199 break;
200 case PROP_N_SEGMENT:
201 g_value_set_uint(value, data->n_segment);
202 break;
203 case PROP_POS:
204 g_value_set_double(value, data->pos);
205 break;
206 case PROP_SIZE:
207 g_value_set_double(value, data->size);
208 break;
209 case PROP_MODEL:
210 g_value_set_object(value, data->model);
211 break;
212 default:
213 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
214 break;
218 static void
219 set_property(GObject *object,
220 guint prop_id, const GValue *value, GParamSpec *pspec)
222 AdgMarker *marker = (AdgMarker *) object;
224 switch (prop_id) {
225 case PROP_TRAIL:
226 set_segment(marker, g_value_get_object(value), 1);
227 break;
228 case PROP_N_SEGMENT:
229 set_n_segment(marker, g_value_get_uint(value));
230 break;
231 case PROP_POS:
232 set_pos(marker, g_value_get_double(value));
233 break;
234 case PROP_SIZE:
235 set_size(marker, g_value_get_double(value));
236 break;
237 case PROP_MODEL:
238 set_model(marker, g_value_get_object(value));
239 break;
240 default:
241 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
242 break;
248 * adg_marker_get_trail:
249 * @marker: an #AdgMarker
251 * Gets the trail where this marker should be applied.
253 * Returns: the trail owned by @marker or %NULL on errors
255 AdgTrail *
256 adg_marker_get_trail(AdgMarker *marker)
258 AdgMarkerPrivate *data;
260 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
262 data = marker->data;
264 return data->trail;
268 * adg_marker_get_n_segment:
269 * @marker: an #AdgMarker
271 * Returns the segment of the associated trail where this marker
272 * will be applied, where %1 is the first segment.
274 * Returns: an index greather than %0 on success or %0 on errors
276 guint
277 adg_marker_get_n_segment(AdgMarker *marker)
279 AdgMarkerPrivate *data;
281 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
283 data = marker->data;
285 return data->n_segment;
289 * adg_marker_get_segment:
290 * @marker: an #AdgMarker
292 * <note><para>
293 * This function is only useful in marker implementations.
294 * </para></note>
296 * Gets the segment where the marker will be applied. This segment
297 * is eventually a modified version of the backup segment, after
298 * having applied the marker.
300 * Returns: the segment or %NULL on errors
302 AdgSegment *
303 adg_marker_get_segment(AdgMarker *marker)
305 AdgMarkerPrivate *data;
307 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
309 data = marker->data;
311 return &data->segment;
315 * adg_marker_set_segment:
316 * @marker: an #AdgMarker
317 * @trail: the #AdgTrail
318 * @n_segment: a segment index
320 * Sets the new segment where the marker should be applied. The weak
321 * reference to the old trail (if an old trail was present) is dropped
322 * while a new weak reference is added to @trail. If @trail is destroyed,
323 * the weak reference callback will automatically unset #AdgMarker:trai
324 * and will set #AdgMarker:n-segment to %0.
326 void
327 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
329 g_return_if_fail(ADG_IS_MARKER(marker));
331 if (set_segment(marker, trail, n_segment))
332 g_object_notify((GObject *) marker, "trail");
336 * adg_marker_get_backup_segment:
337 * @marker: an #AdgMarker
339 * <note><para>
340 * This function is only useful in marker implementations.
341 * </para></note>
343 * Gets the original segment where the marker has been applied.
344 * Applying a marker could modify the underlying trail, usually
345 * by trimming the original segment of a #AdgMarker:size dependent
346 * length from the ends. The marker instance holds a copy of the
347 * original segment, generated by adg_marker_backup_segment(),
348 * to be used in recomputation, for example when the marker
349 * changes its size.
351 * When the subject segment is changed (either by changing
352 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
353 * is automatically restored.
355 * Returns: the original segment or %NULL on errors
357 const AdgSegment *
358 adg_marker_get_backup_segment(AdgMarker *marker)
360 AdgMarkerPrivate *data;
362 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
364 data = marker->data;
366 return data->backup_segment;
370 * adg_marker_backup_segment:
371 * @marker: an #AdgMarker
373 * <note><para>
374 * This function is only useful in marker implementations.
375 * </para></note>
377 * Duplicates the current subject segment for backup purpose: this
378 * segment can be accessed by adg_marker_get_backup_segment().
379 * Obviously, a current segment should exist (either the
380 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
381 * properly defined) or this method will fail without further
382 * processing.
384 * When the subject segment is changed (either by changing
385 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
386 * is automatically restored.
388 void
389 adg_marker_backup_segment(AdgMarker *marker)
391 AdgMarkerPrivate *data;
393 g_return_if_fail(ADG_IS_MARKER(marker));
395 data = marker->data;
397 if (data->n_segment > 0) {
398 g_return_if_fail(data->trail != NULL);
400 g_free(data->backup_segment);
402 /* Backup the segment, if a segment to backup exists */
403 if (adg_trail_get_segment(data->trail,
404 &data->segment, data->n_segment))
405 data->backup_segment = adg_segment_deep_dup(&data->segment);
410 * adg_marker_get_pos:
411 * @marker: an #AdgMarker
413 * Gets the current position of @marker. The returned value is a ratio
414 * position referred to the segment associated to @marker: %0 means the
415 * start point and %1 means the end point of the segment.
417 * Returns: the marker position
419 gdouble
420 adg_marker_get_pos(AdgMarker *marker)
422 AdgMarkerPrivate *data;
424 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
426 data = marker->data;
428 return data->pos;
432 * adg_marker_set_pos:
433 * @marker: an #AdgMarker
434 * @pos: the new pos
436 * Sets a new position on @marker. Check out adg_marker_get_pos() for
437 * details on what @pos represents.
439 void
440 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
442 g_return_if_fail(ADG_IS_MARKER(marker));
444 if (set_pos(marker, pos))
445 g_object_notify((GObject *) marker, "pos");
449 * adg_marker_get_size:
450 * @marker: an #AdgMarker
452 * Gets the current size of @marker.
454 * Returns: the marker size, in global space
456 gdouble
457 adg_marker_get_size(AdgMarker *marker)
459 AdgMarkerPrivate *data;
461 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
463 data = marker->data;
465 return data->size;
469 * adg_marker_set_size:
470 * @marker: an #AdgMarker
471 * @size: the new size
473 * Sets a new size on @marker. The @size is an implementation-dependent
474 * property: it has meaning only when used by an #AdgMarker derived type.
476 void
477 adg_marker_set_size(AdgMarker *marker, gdouble size)
479 g_return_if_fail(ADG_IS_MARKER(marker));
481 if (set_size(marker, size))
482 g_object_notify((GObject *) marker, "size");
486 * adg_marker_model:
487 * @marker: an #AdgMarker
489 * <note><para>
490 * This function is only useful in marker implementations.
491 * </para></note>
493 * Gets the model of @marker. If the model is not found, it is
494 * automatically created by calling the create_model() virtual method.
496 * Returns: the current model owned by @marker or %NULL on errors
498 AdgModel *
499 adg_marker_model(AdgMarker *marker)
501 AdgMarkerPrivate *data;
503 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
505 data = marker->data;
507 if (data->model == NULL) {
508 /* Model not found: regenerate it */
509 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
511 adg_marker_set_model(marker, marker_class->create_model(marker));
514 return data->model;
518 * adg_marker_get_model:
519 * @marker: an #AdgMarker
521 * <note><para>
522 * This function is only useful in marker implementations.
523 * </para></note>
525 * Gets the current model of @marker. This is an accessor method:
526 * if you need to get the model for rendering, use adg_marker_model()
527 * instead.
529 * Returns: the cached model owned by @marker or %NULL on errors
531 AdgModel *
532 adg_marker_get_model(AdgMarker *marker)
534 AdgMarkerPrivate *data;
536 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
538 data = marker->data;
540 return data->model;
544 * adg_marker_set_model:
545 * @marker: an #AdgMarker
546 * @model: a new #AdgModel
548 * <note><para>
549 * This function is only useful in marker implementations.
550 * </para></note>
552 * Sets a new model for @marker. The reference to the old model (if an
553 * old model was present) is dropped while a new reference is added to
554 * @model.
556 void
557 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
559 g_return_if_fail(ADG_IS_MARKER(marker));
561 if (set_model(marker, model))
562 g_object_notify((GObject *) marker, "model");
566 static void
567 local_changed(AdgEntity *entity)
569 AdgMarkerPrivate *data;
570 CpmlPair pair;
571 CpmlVector vector;
572 AdgMatrix map;
574 data = ((AdgMarker *) entity)->data;
575 if (data->trail == NULL)
576 return;
578 cpml_segment_pair_at(&data->segment, &pair, data->pos);
579 cpml_segment_vector_at(&data->segment, &vector, data->pos);
580 cpml_vector_set_length(&vector, data->size);
582 if (data->pos > 0.5) {
583 vector.x = -vector.x;
584 vector.y = -vector.y;
587 cairo_matrix_init(&map, vector.x, vector.y,
588 -vector.y, vector.x, pair.x, pair.y);
590 adg_entity_set_local_map(entity, &map);
592 PARENT_ENTITY_CLASS->local_changed(entity);
595 static void
596 invalidate(AdgEntity *entity)
598 adg_marker_set_model((AdgMarker *) entity, NULL);
602 static gboolean
603 set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
605 AdgMarkerPrivate *data;
606 AdgEntity *entity;
608 data = marker->data;
609 entity = (AdgEntity *) marker;
611 if (trail == data->trail && n_segment == data->n_segment)
612 return FALSE;
614 if (data->trail != NULL) {
615 /* Restore the original segment in the old trail */
616 set_n_segment(marker, 0);
618 g_object_weak_unref((GObject *) data->trail,
619 (GWeakNotify) unset_trail, marker);
620 adg_model_remove_dependency((AdgModel *) data->trail, entity);
623 data->trail = trail;
624 data->n_segment = 0;
626 if (data->trail != NULL) {
627 g_object_weak_ref((GObject *) data->trail,
628 (GWeakNotify) unset_trail, marker);
629 adg_model_add_dependency((AdgModel *) data->trail, entity);
631 set_n_segment(marker, n_segment);
634 return TRUE;
637 static void
638 unset_trail(AdgMarker *marker)
640 AdgMarkerPrivate *data = marker->data;
642 if (data->trail != NULL)
643 set_segment(marker, NULL, 0);
646 static gboolean
647 set_n_segment(AdgMarker *marker, guint n_segment)
649 AdgMarkerPrivate *data = marker->data;
651 if (n_segment == data->n_segment)
652 return FALSE;
654 if (data->backup_segment != NULL) {
655 /* Restore the original segment, if any */
656 if (data->trail != NULL)
657 adg_segment_deep_copy(&data->segment, data->backup_segment);
659 g_free(data->backup_segment);
660 data->backup_segment = NULL;
663 data->n_segment = n_segment;
665 if (n_segment == 0) {
666 memset(&data->segment, 0, sizeof(data->segment));
667 return TRUE;
670 return adg_trail_get_segment(data->trail, &data->segment, n_segment);
673 static gboolean
674 set_pos(AdgMarker *marker, gdouble pos)
676 AdgMarkerPrivate *data = marker->data;
678 if (pos == data->pos)
679 return FALSE;
681 data->pos = pos;
683 return TRUE;
686 static gboolean
687 set_size(AdgMarker *marker, gdouble size)
689 AdgMarkerPrivate *data = marker->data;
691 if (size == data->size)
692 return FALSE;
694 data->size = size;
696 return TRUE;
699 static gboolean
700 set_model(AdgMarker *marker, AdgModel *model)
702 AdgMarkerPrivate *data = marker->data;
704 if (model == data->model)
705 return FALSE;
707 if (data->model != NULL)
708 g_object_unref((GObject *) data->model);
710 data->model = model;
712 if (data->model != NULL) {
713 g_object_ref((GObject *) data->model);
714 adg_entity_local_changed((AdgEntity *) marker);
717 return TRUE;
720 static AdgModel *
721 create_model(AdgMarker *marker)
723 g_warning("%s: `create_model' method not implemented for type `%s'",
724 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
725 return NULL;