[ADG] s/defined (/defined(/
[adg.git] / adg / adg-trail.c
blobc3c9eb2c7603618af18d00b69e87f600addfe78c
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-trail
23 * @short_description: A bare model built around #CpmlPath
25 * The #AdgTrail model is a really basic model built around the #CpmlPath
26 * struct: for a full fledged path model consider using #AdgPath.
28 * A trail is a path model that demands all the implementation details to
29 * the caller: this requires a deep knowledge of the ADG details but
30 * provides a great customization level. It should be used when an
31 * #AdgPath is not enough, such as when a model is subject to change
32 * dynamically and the global and local maps do not suffice to express
33 * this alteration. A typical example is the path used to draw extension
34 * lines and base line of #AdgLDim: every point is subject to different
35 * constrains not expressible with a single affine transformation.
36 **/
38 /**
39 * AdgTrail:
41 * All fields are private and should not be used directly.
42 * Use its public methods instead.
43 **/
45 /**
46 * AdgTrailCallback:
47 * @trail: an #AdgTrail
48 * @user_data: the general purpose pointer set by adg_trail_new()
50 * This is the callback used to generate the #CpmlPath and it is
51 * called directly by adg_trail_cpml_path(). The caller owns
52 * the returned path, that is the finalization of the returned
53 * #CpmlPath should be made by the caller when appropriate.
55 * Returns: the #CpmlPath of this trail model
56 **/
59 #include "adg-internal.h"
60 #include "adg-trail.h"
61 #include "adg-trail-private.h"
63 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_trail_parent_class)
64 #define PARENT_MODEL_CLASS ((AdgModelClass *) adg_trail_parent_class)
67 static void finalize (GObject *object);
68 static void clear (AdgModel *model);
69 static CpmlPath * get_cpml_path (AdgTrail *trail);
70 static GArray * arc_to_curves (GArray *array,
71 const cairo_path_data_t
72 *src);
75 G_DEFINE_TYPE(AdgTrail, adg_trail, ADG_TYPE_MODEL);
78 static void
79 adg_trail_class_init(AdgTrailClass *klass)
81 GObjectClass *gobject_class;
82 AdgModelClass *model_class;
84 gobject_class = (GObjectClass *) klass;
85 model_class = (AdgModelClass *) klass;
87 g_type_class_add_private(klass, sizeof(AdgTrailPrivate));
89 gobject_class->finalize = finalize;
91 model_class->clear = clear;
93 klass->get_cpml_path = get_cpml_path;
96 static void
97 adg_trail_init(AdgTrail *trail)
99 AdgTrailPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(trail, ADG_TYPE_TRAIL,
100 AdgTrailPrivate);
102 data->cairo_path.status = CAIRO_STATUS_INVALID_PATH_DATA;
103 data->cairo_path.data = NULL;
104 data->cairo_path.num_data = 0;
105 data->callback = NULL;
106 data->user_data = NULL;
108 data->in_construction = FALSE;
109 data->extents.is_defined = FALSE;
111 trail->data = data;
114 static void
115 finalize(GObject *object)
117 clear((AdgModel *) object);
119 if (PARENT_OBJECT_CLASS->finalize)
120 PARENT_OBJECT_CLASS->finalize(object);
125 * adg_trail_new:
126 * @callback: the #CpmlPath constructor function
127 * @user_data: generic pointer to pass to the callback
129 * Creates a new trail model. The #CpmlPath must be constructed by the
130 * @callback function: #AdgTrail will not cache anything, so you should
131 * implement any caching mechanism in the callback, if needed.
133 * Returns: a new trail model
135 AdgTrail *
136 adg_trail_new(AdgTrailCallback callback, gpointer user_data)
138 AdgTrail *trail;
139 AdgTrailPrivate *data;
141 trail = g_object_new(ADG_TYPE_TRAIL, NULL);
142 data = trail->data;
144 data->callback = callback;
145 data->user_data = user_data;
147 return trail;
152 * adg_trail_get_cairo_path:
153 * @trail: an #AdgTrail
155 * Gets a pointer to the cairo path of @trail. The return path is
156 * owned by @trail and must be considered read-only.
158 * This function gets the #CpmlPath of @trail by calling
159 * adg_trail_cpml_path() and converts its %CAIRO_PATH_ARC_TO
160 * primitives, not recognized by cairo, into approximated Bézier
161 * curves primitives (%CAIRO_PATH_CURVE_TO). The conversion is
162 * cached, so any furter request is O(1). This cache is cleared
163 * only by the adg_model_clear() method.
165 * <important>
166 * <title>TODO</title>
167 * <itemizedlist>
168 * <listitem>Actually, the arcs are approximated with Bézier using the
169 * hardcoded max angle of PI/2. This should be customizable
170 * by adding, for instance, a property to the #AdgTrail class
171 * with a default value of PI/2.</listitem>
172 * </itemizedlist>
173 * </important>
175 * Returns: a pointer to the internal cairo path or %NULL on errors
177 const cairo_path_t *
178 adg_trail_get_cairo_path(AdgTrail *trail)
180 AdgTrailPrivate *data;
181 cairo_path_t *cairo_path;
182 CpmlPath *cpml_path;
183 GArray *dst;
184 const cairo_path_data_t *p_src;
185 int i;
187 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
189 data = trail->data;
190 cairo_path = &data->cairo_path;
192 /* Check for cached result */
193 if (cairo_path->data != NULL)
194 return cairo_path;
196 cpml_path = adg_trail_cpml_path(trail);
197 if (cpml_path_is_empty(cpml_path))
198 return NULL;
200 dst = g_array_sized_new(FALSE, FALSE,
201 sizeof(cairo_path_data_t), cpml_path->num_data);
203 /* Cycle the CpmlPath and convert arcs to Bézier curves */
204 for (i = 0; i < cpml_path->num_data; i += p_src->header.length) {
205 p_src = (const cairo_path_data_t *) cpml_path->data + i;
207 if (p_src->header.type == CAIRO_PATH_ARC_TO)
208 dst = arc_to_curves(dst, p_src);
209 else
210 dst = g_array_append_vals(dst, p_src, p_src->header.length);
213 cairo_path->status = CAIRO_STATUS_SUCCESS;
214 cairo_path->num_data = dst->len;
215 cairo_path->data = (cairo_path_data_t *) g_array_free(dst, FALSE);
217 return cairo_path;
221 * adg_trail_cpml_path:
222 * @trail: an #AdgTrail
224 * Gets the CPML path structure defined by @trail. The returned
225 * value is managed by the #AdgTrail implementation, that is this
226 * function directly calls the #AdgTrailClass::get_cpml_path()
227 * virtual method that any trail instance must have.
229 * Whenever used internally by the ADG project, the returned path
230 * is (by convention) owned by @trail and so it should not be freed.
231 * Anyway, callers are allowed to modify it as long as its size is
232 * retained and its data contains a valid path: this is needed to
233 * let the #AdgMarker infrastructure work properly (the markers
234 * should be able to modify the trail where they are applied).
236 * Any further call to this method will probably make the pointer
237 * previously returned useless because the #CpmlPath could be
238 * relocated and the old #CpmlPath will likely contain rubbish.
240 * Returns: a pointer to the #CpmlPath or %NULL on errors
242 CpmlPath *
243 adg_trail_cpml_path(AdgTrail *trail)
245 AdgTrailClass *klass;
246 AdgTrailPrivate *data;
247 CpmlPath *cpml_path;
249 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
251 klass = ADG_TRAIL_GET_CLASS(trail);
252 if (klass->get_cpml_path == NULL)
253 return NULL;
255 data = trail->data;
256 if (data->in_construction) {
257 g_warning(_("%s: you cannot access the path from the callback you provided to build it"),
258 G_STRLOC);
259 return NULL;
262 data->in_construction = TRUE;
263 cpml_path = klass->get_cpml_path(trail);
264 data->in_construction = FALSE;
266 return cpml_path;
270 * adg_trail_put_segment:
271 * @trail: an #AdgTrail
272 * @n_segment: the segment to retrieve, where %1 is the first segment
273 * @segment: the destination #AdgSegment
275 * Convenient function to get a segment from @trail. The segment is
276 * got from the CPML path: check out adg_trail_cpml_path() for
277 * further information.
279 * Returns: %TRUE on success or %FALSE on errors
281 gboolean
282 adg_trail_put_segment(AdgTrail *trail, guint n_segment, AdgSegment *segment)
284 CpmlPath *cpml_path;
285 guint cnt;
287 g_return_val_if_fail(ADG_IS_TRAIL(trail), FALSE);
288 g_return_val_if_fail(segment != NULL, FALSE);
290 if (n_segment == 0) {
291 g_warning(_("%s: requested undefined segment for type `%s'"),
292 G_STRLOC, g_type_name(G_OBJECT_TYPE(trail)));
293 return FALSE;
296 cpml_path = adg_trail_cpml_path(trail);
297 if (cpml_path_is_empty(cpml_path))
298 return FALSE;
300 cpml_segment_from_cairo(segment, cpml_path);
301 for (cnt = 1; cnt < n_segment; ++cnt) {
302 if (!cpml_segment_next(segment)) {
303 g_warning(_("%s: segment %u is out of range for type `%s'"),
304 G_STRLOC, n_segment, g_type_name(G_OBJECT_TYPE(trail)));
305 return FALSE;
309 return TRUE;
313 * adg_trail_get_extents:
314 * @trail: an #AdgTrail
316 * Gets the extents of @trail. The returned pointer is owned by
317 * @trail and should not be freed nor modified.
319 * Returns: the requested extents or %NULL on errors
321 const CpmlExtents *
322 adg_trail_get_extents(AdgTrail *trail)
324 AdgTrailPrivate *data;
326 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
328 data = trail->data;
330 if (!data->extents.is_defined) {
331 CpmlPath *cpml_path;
332 CpmlSegment segment;
333 CpmlExtents extents;
335 cpml_path = adg_trail_cpml_path(trail);
336 if (!cpml_path_is_empty(cpml_path) &&
337 cpml_segment_from_cairo(&segment, cpml_path)) {
338 do {
339 cpml_segment_put_extents(&segment, &extents);
340 cpml_extents_add(&data->extents, &extents);
341 } while (cpml_segment_next(&segment));
345 return &data->extents;
349 * adg_trail_dump:
350 * @trail: an #AdgTrail
352 * Dumps the data content of @trail to stdout in a human readable format.
354 void
355 adg_trail_dump(AdgTrail *trail)
357 CpmlSegment segment;
358 cairo_path_t *cairo_path;
360 g_return_if_fail(ADG_IS_TRAIL(trail));
362 cairo_path = (cairo_path_t *) adg_trail_get_cairo_path(trail);
364 g_return_if_fail(cairo_path != NULL);
366 if (!cpml_segment_from_cairo(&segment, cairo_path)) {
367 g_warning(_("%s: Invalid path data to dump!"), G_STRLOC);
368 } else {
369 do {
370 cpml_segment_dump(&segment);
371 } while (cpml_segment_next(&segment));
376 static void
377 clear(AdgModel *model)
379 AdgTrailPrivate *data = ((AdgTrail *) model)->data;
381 g_free(data->cairo_path.data);
383 data->cairo_path.status = CAIRO_STATUS_INVALID_PATH_DATA;
384 data->cairo_path.data = NULL;
385 data->cairo_path.num_data = 0;
386 data->extents.is_defined = FALSE;
388 if (PARENT_MODEL_CLASS->clear)
389 PARENT_MODEL_CLASS->clear(model);
392 static CpmlPath *
393 get_cpml_path(AdgTrail *trail)
395 AdgTrailPrivate *data = trail->data;
397 if (data->callback == NULL) {
398 g_warning(_("%s: callback not defined for instance of type `%s'"),
399 G_STRLOC, g_type_name(G_OBJECT_TYPE(trail)));
400 return NULL;
403 return data->callback(trail, data->user_data);
406 static GArray *
407 arc_to_curves(GArray *array, const cairo_path_data_t *src)
409 CpmlPrimitive arc;
410 double start, end;
412 /* Build the arc primitive: the arc origin is supposed to be the previous
413 * point (src-1): this means a primitive must exist before the arc */
414 arc.segment = NULL;
415 arc.org = (cairo_path_data_t *) (src-1);
416 arc.data = (cairo_path_data_t *) src;
418 if (cpml_arc_info(&arc, NULL, NULL, &start, &end)) {
419 CpmlSegment segment;
420 int n_curves;
421 cairo_path_data_t *curves;
423 n_curves = ceil(fabs(end-start) / M_PI_2);
424 curves = g_new(cairo_path_data_t, n_curves * 4);
425 segment.data = curves;
426 cpml_arc_to_curves(&arc, &segment, n_curves);
428 array = g_array_append_vals(array, curves, n_curves * 4);
430 g_free(curves);
433 return array;