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.
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.
41 * All fields are private and should not be used directly.
42 * Use its public methods instead.
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
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
75 G_DEFINE_TYPE(AdgTrail
, adg_trail
, ADG_TYPE_MODEL
);
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
;
97 adg_trail_init(AdgTrail
*trail
)
99 AdgTrailPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(trail
, ADG_TYPE_TRAIL
,
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
;
115 finalize(GObject
*object
)
117 clear((AdgModel
*) object
);
119 if (PARENT_OBJECT_CLASS
->finalize
)
120 PARENT_OBJECT_CLASS
->finalize(object
);
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
136 adg_trail_new(AdgTrailCallback callback
, gpointer user_data
)
139 AdgTrailPrivate
*data
;
141 trail
= g_object_new(ADG_TYPE_TRAIL
, NULL
);
144 data
->callback
= callback
;
145 data
->user_data
= user_data
;
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.
166 * <title>TODO</title>
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>
175 * Returns: a pointer to the internal cairo path or %NULL on errors
178 adg_trail_get_cairo_path(AdgTrail
*trail
)
180 AdgTrailPrivate
*data
;
181 cairo_path_t
*cairo_path
;
184 const cairo_path_data_t
*p_src
;
187 g_return_val_if_fail(ADG_IS_TRAIL(trail
), NULL
);
190 cairo_path
= &data
->cairo_path
;
192 /* Check for cached result */
193 if (cairo_path
->data
!= NULL
)
196 src
= adg_trail_cpml_path(trail
);
197 dst
= g_array_sized_new(FALSE
, FALSE
,
198 sizeof(cairo_path_data_t
), src
->num_data
);
200 /* Cycle the CpmlPath and convert arcs to Bézier curves */
201 for (i
= 0; i
< src
->num_data
; i
+= p_src
->header
.length
) {
202 p_src
= (const cairo_path_data_t
*) src
->data
+ i
;
204 if (p_src
->header
.type
== CAIRO_PATH_ARC_TO
)
205 dst
= arc_to_curves(dst
, p_src
);
207 dst
= g_array_append_vals(dst
, p_src
, p_src
->header
.length
);
210 cairo_path
->status
= CAIRO_STATUS_SUCCESS
;
211 cairo_path
->num_data
= dst
->len
;
212 cairo_path
->data
= (cairo_path_data_t
*) g_array_free(dst
, FALSE
);
218 * adg_trail_cpml_path:
219 * @trail: an #AdgTrail
221 * Gets the CPML path structure defined by @trail. The returned
222 * value is managed by the #AdgTrail implementation, that is this
223 * function directly calls the #AdgTrailClass::get_cpml_path()
224 * virtual method that any trail instance must have.
226 * Whenever used internally by the ADG project, the returned path
227 * is (by convention) owned by @trail and so it should not be freed.
228 * Anyway, callers are allowed to modify it as long as its size is
229 * retained and its data contains a valid path: this is needed to
230 * let the #AdgMarker infrastructure work properly (the markers
231 * should be able to modify the trail where they are applied).
233 * Any further call to this method will probably make the pointer
234 * previously returned useless because the #CpmlPath could be
235 * relocated and the old #CpmlPath will likely contain rubbish.
237 * Returns: a pointer to the #CpmlPath or %NULL on errors
240 adg_trail_cpml_path(AdgTrail
*trail
)
242 AdgTrailClass
*klass
;
243 AdgTrailPrivate
*data
;
246 g_return_val_if_fail(ADG_IS_TRAIL(trail
), NULL
);
248 klass
= ADG_TRAIL_GET_CLASS(trail
);
249 if (klass
->get_cpml_path
== NULL
)
253 if (data
->in_construction
) {
254 g_warning(_("%s: you cannot access the path from the callback you provided to build it"),
259 data
->in_construction
= TRUE
;
260 cpml_path
= klass
->get_cpml_path(trail
);
261 data
->in_construction
= FALSE
;
267 * adg_trail_put_segment:
268 * @trail: an #AdgTrail
269 * @n_segment: the segment to retrieve, where %1 is the first segment
270 * @segment: the destination #AdgSegment
272 * Convenient function to get a segment from @trail. The segment is
273 * got from the CPML path: check out adg_trail_cpml_path() for
274 * further information.
276 * Returns: %TRUE on success or %FALSE on errors
279 adg_trail_put_segment(AdgTrail
*trail
, guint n_segment
, AdgSegment
*segment
)
284 g_return_val_if_fail(ADG_IS_TRAIL(trail
), FALSE
);
285 g_return_val_if_fail(segment
!= NULL
, FALSE
);
287 if (n_segment
== 0) {
288 g_warning(_("%s: requested undefined segment for type `%s'"),
289 G_STRLOC
, g_type_name(G_OBJECT_TYPE(trail
)));
293 cpml_path
= adg_trail_cpml_path(trail
);
295 cpml_segment_from_cairo(segment
, cpml_path
);
296 for (cnt
= 1; cnt
< n_segment
; ++cnt
)
297 if (!cpml_segment_next(segment
)) {
298 g_warning(_("%s: segment %u is out of range for type `%s'"),
299 G_STRLOC
, n_segment
, g_type_name(G_OBJECT_TYPE(trail
)));
307 * adg_trail_get_extents:
308 * @trail: an #AdgTrail
310 * Gets the extents of @trail. The returned pointer is owned by
311 * @trail and should not be freed nor modified.
313 * Returns: the requested extents or %NULL on errors
316 adg_trail_get_extents(AdgTrail
*trail
)
318 AdgTrailPrivate
*data
;
320 g_return_val_if_fail(ADG_IS_TRAIL(trail
), NULL
);
324 if (!data
->extents
.is_defined
) {
329 cpml_path
= adg_trail_cpml_path(trail
);
331 if (cpml_segment_from_cairo(&segment
, cpml_path
))
333 cpml_segment_extents(&segment
, &extents
);
334 cpml_extents_add(&data
->extents
, &extents
);
335 } while (cpml_segment_next(&segment
));
338 return &data
->extents
;
343 * @trail: an #AdgTrail
345 * Dumps the data content of @trail to stdout in a human readable format.
348 adg_trail_dump(AdgTrail
*trail
)
351 cairo_path_t
*cairo_path
;
353 g_return_if_fail(ADG_IS_TRAIL(trail
));
355 cairo_path
= (cairo_path_t
*) adg_trail_get_cairo_path(trail
);
357 g_return_if_fail(cairo_path
!= NULL
);
359 if (!cpml_segment_from_cairo(&segment
, cairo_path
)) {
360 g_warning(_("%s: Invalid path data to dump!"), G_STRLOC
);
363 cpml_segment_dump(&segment
);
364 } while (cpml_segment_next(&segment
));
370 clear(AdgModel
*model
)
372 AdgTrailPrivate
*data
= ((AdgTrail
*) model
)->data
;
374 g_free(data
->cairo_path
.data
);
376 data
->cairo_path
.status
= CAIRO_STATUS_INVALID_PATH_DATA
;
377 data
->cairo_path
.data
= NULL
;
378 data
->cairo_path
.num_data
= 0;
379 data
->extents
.is_defined
= FALSE
;
381 if (PARENT_MODEL_CLASS
->clear
)
382 PARENT_MODEL_CLASS
->clear(model
);
386 get_cpml_path(AdgTrail
*trail
)
388 AdgTrailPrivate
*data
= trail
->data
;
390 if (data
->callback
== NULL
) {
391 g_warning(_("%s: callback not defined for instance of type `%s'"),
392 G_STRLOC
, g_type_name(G_OBJECT_TYPE(trail
)));
396 return data
->callback(trail
, data
->user_data
);
400 arc_to_curves(GArray
*array
, const cairo_path_data_t
*src
)
405 /* Build the arc primitive: the arc origin is supposed to be the previous
406 * point (src-1): this means a primitive must exist before the arc */
408 arc
.org
= (cairo_path_data_t
*) (src
-1);
409 arc
.data
= (cairo_path_data_t
*) src
;
411 if (cpml_arc_info(&arc
, NULL
, NULL
, &start
, &end
)) {
414 cairo_path_data_t
*curves
;
416 n_curves
= ceil(fabs(end
-start
) / M_PI_2
);
417 curves
= g_new(cairo_path_data_t
, n_curves
* 4);
418 segment
.data
= curves
;
419 cpml_arc_to_curves(&arc
, &segment
, n_curves
);
421 array
= g_array_append_vals(array
, curves
, n_curves
* 4);