[AdgLDim] Quote management throught maps
[adg.git] / adg / adg-trail.c
blob2af56a85b1e118c3239b5fed23ee76e0793684ef
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 applicable 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_get_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-trail.h"
60 #include "adg-trail-private.h"
62 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_trail_parent_class)
65 static void finalize (GObject *object);
66 static cairo_path_t * get_cairo_path (AdgTrail *trail);
67 static CpmlPath * get_cpml_path (AdgTrail *trail);
68 static GArray * arc_to_curves (GArray *array,
69 const cairo_path_data_t
70 *src);
73 G_DEFINE_TYPE(AdgTrail, adg_trail, ADG_TYPE_MODEL);
76 static void
77 adg_trail_class_init(AdgTrailClass *klass)
79 GObjectClass *gobject_class;
80 AdgModelClass *model_class;
82 gobject_class = (GObjectClass *) klass;
83 model_class = (AdgModelClass *) klass;
85 g_type_class_add_private(klass, sizeof(AdgTrailPrivate));
87 gobject_class->finalize = finalize;
89 klass->get_cpml_path = get_cpml_path;
92 static void
93 adg_trail_init(AdgTrail *trail)
95 AdgTrailPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(trail, ADG_TYPE_TRAIL,
96 AdgTrailPrivate);
98 data->cairo_path.status = CAIRO_STATUS_INVALID_PATH_DATA;
99 data->cairo_path.data = NULL;
100 data->cairo_path.num_data = 0;
102 data->callback = NULL;
103 data->user_data = NULL;
105 trail->data = data;
108 static void
109 finalize(GObject *object)
111 AdgTrail *trail;
112 AdgTrailPrivate *data;
114 trail = (AdgTrail *) object;
115 data = trail->data;
117 adg_trail_clear_cairo_path(trail);
119 if (PARENT_OBJECT_CLASS->finalize != NULL)
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 structure of @trail. The return path
156 * is owned by @trail and must be considered read-only.
158 * This function also converts %CAIRO_PATH_ARC_TO primitives, not
159 * recognized by cairo, into approximated Bézier curves. The conversion
160 * is cached so any furter request is O(1). This cache is cleared
161 * whenever @trail is modified (by adding a new primitive or by calling
162 * adg_path_clear()).
164 * <important>
165 * <title>TODO</title>
166 * <itemizedlist>
167 * <listitem>Actually, the arcs are approximated to Bézier using the
168 * hardcoded max angle of PI/2. This should be customizable
169 * by adding, for instance, a property to the #AdgTrail class
170 * with a default value of PI/2.</listitem>
171 * </itemizedlist>
172 * </important>
174 * Returns: a pointer to the internal cairo path or %NULL on errors
176 const cairo_path_t *
177 adg_trail_get_cairo_path(AdgTrail *trail)
179 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
181 return get_cairo_path(trail);
185 * adg_trail_clear_cairo_path:
186 * @trail: an #AdgTrail
188 * Clears the internal cairo path of @trail so it will be recomputed
189 * the next time is requested throught adg_trail_get_cairo_path().
191 void
192 adg_trail_clear_cairo_path(AdgTrail *trail)
194 AdgTrailPrivate *data;
195 cairo_path_t *cairo_path;
197 data = trail->data;
198 cairo_path = &data->cairo_path;
200 if (cairo_path->data == NULL)
201 return;
203 g_free(cairo_path->data);
205 cairo_path->status = CAIRO_STATUS_INVALID_PATH_DATA;
206 cairo_path->data = NULL;
207 cairo_path->num_data = 0;
211 * adg_trail_get_cpml_path:
212 * @trail: an #AdgTrail
214 * Gets a pointer to the CPML path structure of @trail. The return
215 * value is owned by @trail and must not be freed.
217 * This function is similar to adg_trail_get_cairo_path() but with
218 * two important differences: firstly the arc primitives are not
219 * expanded to Bézier curves and secondly the returned path is
220 * not read-only. This means it is allowed to modify the returned
221 * path as long as its size is retained and its data contains a
222 * valid path (this is needed by the #AdgMarker infrastructure).
224 * Any further call to this method will probably make the pointer
225 * previously returned useless because the internal #CpmlPath could
226 * be relocated and the old #CpmlPath will likely contain rubbish.
228 * Returns: a pointer to the internal #CpmlPath or %NULL on errors
230 CpmlPath *
231 adg_trail_get_cpml_path(AdgTrail *trail)
233 g_return_val_if_fail(ADG_IS_TRAIL(trail), NULL);
235 return ADG_TRAIL_GET_CLASS(trail)->get_cpml_path(trail);
239 * adg_trail_get_segment:
240 * @trail: an #AdgTrail
241 * @segment: the destination #AdgSegment
242 * @n: the segment number to retrieve, where %1 is the first segment
244 * Convenient function to get a segment from @trail. The segment is
245 * got from the CPML path: check out adg_trail_get_cpml_path() for
246 * further information.
248 * Returns: %TRUE on success or %FALSE on errors
250 gboolean
251 adg_trail_get_segment(AdgTrail *trail, AdgSegment *segment, guint n)
253 CpmlPath *cpml_path;
254 guint cnt;
256 g_return_val_if_fail(ADG_IS_TRAIL(trail), FALSE);
258 if (n == 0) {
259 g_warning("%s: requested undefined segment for type `%s'",
260 G_STRLOC, g_type_name(G_OBJECT_TYPE(trail)));
261 return FALSE;
264 cpml_path = adg_trail_get_cpml_path(trail);
266 cpml_segment_from_cairo(segment, cpml_path);
267 for (cnt = 1; cnt < n; ++cnt)
268 if (!cpml_segment_next(segment)) {
269 g_warning("%s: segment `%u' out of range for type `%s'",
270 G_STRLOC, n, g_type_name(G_OBJECT_TYPE(trail)));
271 return FALSE;
274 return TRUE;
278 * adg_trail_dump:
279 * @trail: an #AdgTrail
281 * Dumps the data content of @trail to stdout in a human readable format.
283 void
284 adg_trail_dump(AdgTrail *trail)
286 CpmlSegment segment;
287 cairo_path_t *cairo_path;
289 g_return_if_fail(ADG_IS_TRAIL(trail));
291 cairo_path = get_cairo_path(trail);
293 g_return_if_fail(cairo_path != NULL);
295 if (!cpml_segment_from_cairo(&segment, cairo_path)) {
296 g_warning("Invalid path data to dump!\n");
297 } else {
298 do {
299 cpml_segment_dump(&segment);
300 } while (cpml_segment_next(&segment));
304 static cairo_path_t *
305 get_cairo_path(AdgTrail *trail)
307 AdgTrailPrivate *data;
308 cairo_path_t *cairo_path;
309 CpmlPath *src;
310 GArray *dst;
311 const cairo_path_data_t *p_src;
312 int i;
314 data = trail->data;
315 cairo_path = &data->cairo_path;
317 /* Check for cached result */
318 if (cairo_path->data != NULL)
319 return cairo_path;
321 src = adg_trail_get_cpml_path(trail);
322 dst = g_array_sized_new(FALSE, FALSE,
323 sizeof(cairo_path_data_t), src->num_data);
325 /* Cycle the CpmlPath and convert arcs to Bézier curves */
326 for (i = 0; i < src->num_data; i += p_src->header.length) {
327 p_src = (const cairo_path_data_t *) src->data + i;
329 if (p_src->header.type == CAIRO_PATH_ARC_TO)
330 dst = arc_to_curves(dst, p_src);
331 else
332 dst = g_array_append_vals(dst, p_src, p_src->header.length);
335 cairo_path->status = CAIRO_STATUS_SUCCESS;
336 cairo_path->num_data = dst->len;
337 cairo_path->data = (cairo_path_data_t *) g_array_free(dst, FALSE);
339 return cairo_path;
342 static CpmlPath *
343 get_cpml_path(AdgTrail *trail)
345 AdgTrailPrivate *data = trail->data;
347 if (data->callback == NULL) {
348 g_warning("%s: callback not defined for instance of type `%s'",
349 G_STRLOC, g_type_name(G_OBJECT_TYPE(trail)));
350 return NULL;
353 return data->callback(trail, data->user_data);
356 static GArray *
357 arc_to_curves(GArray *array, const cairo_path_data_t *src)
359 CpmlPrimitive arc;
360 double start, end;
362 /* Build the arc primitive: the arc origin is supposed to be the previous
363 * point (src-1): this means a primitive must exist before the arc */
364 arc.segment = NULL;
365 arc.org = (cairo_path_data_t *) (src-1);
366 arc.data = (cairo_path_data_t *) src;
368 if (cpml_arc_info(&arc, NULL, NULL, &start, &end)) {
369 CpmlSegment segment;
370 int n_curves;
371 cairo_path_data_t *curves;
373 n_curves = ceil(fabs(end-start) / M_PI_2);
374 curves = g_new(cairo_path_data_t, n_curves * 4);
375 segment.data = curves;
376 cpml_arc_to_curves(&arc, &segment, n_curves);
378 array = g_array_append_vals(array, curves, n_curves * 4);
380 g_free(curves);
383 return array;