[CPML] Reworked to include actions in their own constructs
[adg.git] / cpml / cpml-segment.c
blobfcb00556570bbf57d7b513cf2ce2c933c7ecea37
1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 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.
20 /**
21 * SECTION:segment
22 * @title: CpmlSegment
23 * @short_description: Contiguous lines
25 * A segment is a single contiguous line got from a cairo path.
27 * Every #cairo_path_t struct can contain more than one segment:
28 * the CPML library provides iteration APIs to browse these segments.
29 * The <structname>CpmlSegment</structname> struct internally keeps
30 * a reference to the source #cairo_path_t so it can be used both
31 * for getting segment data and browsing the segments (that is,
32 * it is used also as an iterator).
34 * Use cpml_segment_reset() to reset the iterator at the start of the
35 * cairo path (will point the first segment) and cpml_segment_next()
36 * to get the next segment. When initialized,
37 * <structname>CpmlSegment</structname> yet refers to the first
38 * segment, so the initial reset is useless.
40 * Getting the previous segment is not provided, as the underlying
41 * cairo struct is not accessible in reverse order.
42 **/
44 /**
45 * CpmlSegment:
46 * @cairo_path: the source #cairo_path_t struct
47 * @data: the segment data
48 * @num_data: size of @data
50 * This is an unobtrusive struct to identify a segment inside a
51 * cairo path. Unobtrusive means that the real coordinates are
52 * still stored in @source: CpmlSegment only provides a way to
53 * access the underlying cairo path.
54 **/
56 #include "cpml-segment.h"
57 #include "cpml-primitive.h"
58 #include "cpml-line.h"
59 #include "cpml-curve.h"
60 #include "cpml-pair.h"
61 #include "cpml-alloca.h"
63 #include <stdio.h>
64 #include <string.h>
66 static cairo_bool_t normalize (CpmlSegment *segment);
67 static cairo_bool_t ensure_one_move_to (CpmlSegment *segment);
68 static void reshape (CpmlSegment *segment);
71 /**
72 * cpml_segment_from_cairo:
73 * @segment: a #CpmlSegment
74 * @cairo_path: the source #cairo_path_t
76 * Builds a CpmlSegment from a cairo_path_t structure. This operation
77 * involves stripping the leading %CAIRO_PATH_MOVE_TO primitives and
78 * setting the internal segment structure accordling. A pointer to the
79 * source cairo path is kept.
81 * This function will fail if @cairo_path is null, empty or if its
82 * <structfield>status</structfield> member is not %CAIRO_STATUS_SUCCESS.
83 * Also, the first primitive must be a %CAIRO_PATH_MOVE_TO, so no
84 * dependency on the cairo context is needed.
86 * Return value: 1 on success, 0 on errors
87 **/
88 cairo_bool_t
89 cpml_segment_from_cairo(CpmlSegment *segment, cairo_path_t *cairo_path)
91 /* The cairo path should be defined and in perfect state */
92 if (cairo_path == NULL || cairo_path->num_data == 0 ||
93 cairo_path->status != CAIRO_STATUS_SUCCESS)
94 return 0;
96 segment->cairo_path = cairo_path;
97 segment->data = cairo_path->data;
98 segment->num_data = cairo_path->num_data;
100 return normalize(segment);
104 * cpml_segment_copy:
105 * @segment: a #CpmlSegment
106 * @src: the source segment to copy
108 * Makes a shallow copy of @src into @segment.
110 * Return value: @segment or %NULL on errors
112 CpmlSegment *
113 cpml_segment_copy(CpmlSegment *segment, const CpmlSegment *src)
115 if (segment == NULL || src == NULL)
116 return NULL;
118 return memcpy(segment, src, sizeof(CpmlSegment));
123 * cpml_segment_reset:
124 * @segment: a #CpmlSegment
126 * Modifies @segment to point to the first segment of the source cairo path.
128 void
129 cpml_segment_reset(CpmlSegment *segment)
131 segment->data = segment->cairo_path->data;
132 segment->num_data = segment->cairo_path->num_data;
133 normalize(segment);
137 * cpml_segment_next:
138 * @segment: a #CpmlSegment
140 * Modifies @segment to point to the next segment of the source cairo path.
142 * Return value: 1 on success, 0 if no next segment found or errors
144 cairo_bool_t
145 cpml_segment_next(CpmlSegment *segment)
147 int rest = segment->cairo_path->num_data - segment->num_data +
148 segment->cairo_path->data - segment->data;
150 if (rest <= 0)
151 return 0;
153 segment->data += segment->num_data;
154 segment->num_data = rest;
156 return normalize(segment);
161 * cpml_segment_to_cairo:
162 * @segment: a #CpmlSegment
163 * @cr: the destination cairo context
165 * Appends the path of @segment to @cr using cairo_append_path().
167 void
168 cpml_segment_to_cairo(const CpmlSegment *segment, cairo_t *cr)
170 cairo_path_t path;
172 path.status = CAIRO_STATUS_SUCCESS;
173 path.data = segment->data;
174 path.num_data = segment->num_data;
176 cairo_append_path(cr, &path);
180 * cpml_segment_dump:
181 * @segment: a #CpmlSegment
183 * Dumps the specified @segment to stdout. Useful for debugging purposes.
185 void
186 cpml_segment_dump(const CpmlSegment *segment)
188 CpmlPrimitive primitive;
189 cairo_bool_t first_call = 1;
191 cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment);
193 do {
194 cpml_primitive_dump(&primitive, first_call);
195 first_call = 0;
196 } while (cpml_primitive_next(&primitive));
201 * cpml_segment_reverse:
202 * @segment: a #CpmlSegment
204 * Reverses @segment in-place. The resulting rendering will be the same,
205 * but with the primitives generated in reverse order.
207 void
208 cpml_segment_reverse(CpmlSegment *segment)
210 cairo_path_data_t *data, *dst_data;
211 size_t data_size;
212 double end_x, end_y;
213 int n, num_points, n_point;
214 const cairo_path_data_t *src_data;
216 data_size = sizeof(cairo_path_data_t) * segment->num_data;
217 data = cpml_alloca(data_size);
218 end_x = segment->data[1].point.x;
219 end_y = segment->data[1].point.y;
221 for (n = 2; n < segment->num_data; ++n) {
222 src_data = segment->data + n;
223 num_points = src_data->header.length;
225 dst_data = data + segment->num_data - n - num_points + 2;
226 dst_data->header.type = src_data->header.type;
227 dst_data->header.length = num_points;
229 for (n_point = 1; n_point < num_points; ++n_point) {
230 dst_data[num_points - n_point].point.x = end_x;
231 dst_data[num_points - n_point].point.y = end_y;
232 end_x = src_data[n_point].point.x;
233 end_y = src_data[n_point].point.y;
236 n += n_point - 1;
239 data[0].header.type = CAIRO_PATH_MOVE_TO;
240 data[0].header.length = 2;
241 data[1].point.x = end_x;
242 data[1].point.y = end_y;
243 memcpy(segment->data, data, data_size);
247 * cpml_segment_transform:
248 * @segment: a #CpmlSegment
249 * @matrix: the matrix to be applied
251 * Applies @matrix on all the points of @segment.
253 void
254 cpml_segment_transform(CpmlSegment *segment, const cairo_matrix_t *matrix)
256 cairo_path_data_t *data;
257 int n, n_point, num_points;
259 data = segment->data;
261 for (n = 0; n < segment->num_data; n += num_points) {
262 num_points = data->header.length;
263 ++data;
264 for (n_point = 1; n_point < num_points; ++n_point) {
265 cairo_matrix_transform_point(matrix, &data->point.x, &data->point.y);
266 ++data;
272 * cpml_segment_offset:
273 * @segment: a #CpmlSegment
274 * @offset: the offset distance
276 * Offsets a segment of the specified amount, that is builds a "parallel"
277 * segment at the @offset distance from the original one and returns the
278 * result by replacing the original @segment.
280 * <important>
281 * <title>TODO</title>
282 * <itemizedlist>
283 * <listitem>Closed path are not yet managed: an elegant solution is not
284 * so obvious: use cpml_close_offset() when available.</listitem>
285 * <listitem>Degenerated primitives, such as lines of length 0, are not
286 * managed properly.</listitem>
287 * </itemizedlist>
288 * </important>
290 void
291 cpml_segment_offset(CpmlSegment *segment, double offset)
293 CpmlPrimitive primitive;
294 CpmlPrimitive last_primitive;
295 cairo_path_data_t org, old_end;
296 cairo_bool_t first_cycle;
298 cpml_primitive_from_segment(&primitive, segment);
299 first_cycle = 1;
301 do {
302 if (!first_cycle) {
303 org = old_end;
304 primitive.org = &org;
307 old_end = *cpml_primitive_get_point(&primitive, -1);
308 cpml_primitive_offset(&primitive, offset);
310 if (!first_cycle) {
311 cpml_primitive_join(&last_primitive, &primitive);
312 primitive.org = cpml_primitive_get_point(&last_primitive, -1);
315 cpml_primitive_copy(&last_primitive, &primitive);
316 first_cycle = 0;
317 } while (cpml_primitive_next(&primitive));
321 * normalize:
322 * @segment: a #CpmlSegment
324 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
325 * the CpmlSegment structure accordling. One, and only one,
326 * %CAIRO_PATH_MOVE_TO primitive is left.
328 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
330 static cairo_bool_t
331 normalize(CpmlSegment *segment)
333 if (!ensure_one_move_to(segment))
334 return 0;
336 reshape(segment);
337 return 1;
341 * ensure_one_move_to:
342 * @segment: a #CpmlSegment
344 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
345 * the <structname>CpmlSegment</structname> structure accordling.
346 * One, and only one, %CAIRO_PATH_MOVE_TO primitive is left.
348 * Return value: 1 on success, 0 on no leading MOVE_TOs or on empty path
350 static cairo_bool_t
351 ensure_one_move_to(CpmlSegment *segment)
353 cairo_path_data_t *new_data;
354 int new_num_data, length;
356 new_data = segment->data;
358 /* Check for at least one move to */
359 if (new_data->header.type != CAIRO_PATH_MOVE_TO)
360 return 0;
362 new_num_data = segment->num_data;
363 length = 0;
365 /* Strip the leading CAIRO_PATH_MOVE_TO, leaving only the last one */
366 do {
367 new_data += length;
368 new_num_data -= length;
369 length = new_data->header.length;
371 /* Check for end of cairo path data */
372 if (length >= new_num_data)
373 return 0;
374 } while (new_data[length].header.type == CAIRO_PATH_MOVE_TO);
376 segment->data = new_data;
377 segment->num_data = new_num_data;
379 return 1;
383 * reshape:
384 * @segment: a #CpmlSegment
386 * Looks for the segment termination and modify the
387 * <structfield>num_data</structfield> field of @segment accordling.
388 * @segment must have only one leading %CAIRO_PATH_MOVE_TO and
389 * it is supposed to be non-empty, conditions yet imposed by the
390 * ensure_one_move_to() function.
392 static void
393 reshape(CpmlSegment *segment)
395 cairo_path_data_t *data;
396 int num_data, new_num_data, length;
398 /* Skip the leading move to */
399 new_num_data = 2;
400 data = segment->data + new_num_data;
402 /* Calculate the remaining data in the cairo path */
403 num_data = segment->cairo_path->num_data -
404 (segment->data - segment->cairo_path->data);
406 while (new_num_data < num_data) {
407 /* A primitive is considered valid if it has implemented
408 * its own type_get_npoints() */
409 if (cpml_primitive_type_get_npoints(data->header.type) < 0)
410 break;
412 length = data->header.length;
413 data += length;
414 new_num_data += length;
417 segment->num_data = new_num_data;