[CpmlPrimitive] Doc typos
[adg.git] / cpml / cpml-segment.c
blob9198075af1e6882b15aca5c2b4979d8e54383716
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 #CpmlPath 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 #CpmlPath so it can be used both
31 * for getting data from the current segment, browsing the next and
32 * reset to the first (it is used also as a forward 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 * CpmlPath:
47 * This is another name for the #cairo_path_t type. Although phisically
48 * they are the same struct, #CpmlPath conceptually embodies an important
49 * difference: it is a cairo path that can embed %CAIRO_PATH_ARC_TO
50 * primitives. This is not a native cairo primitive and having two
51 * different data types is a good way to make clear when a function
52 * expect or not embedded arc-to primitives.
53 **/
55 /**
56 * CpmlSegment:
57 * @path: the source #CpmlPath struct
58 * @data: the segment data
59 * @num_data: size of @data
61 * This is an unobtrusive struct to identify a segment inside a
62 * cairo path. Unobtrusive means that the real coordinates are
63 * still stored in @source: CpmlSegment only provides a way to
64 * access the underlying cairo path.
65 **/
67 #include "cpml-segment.h"
68 #include "cpml-primitive.h"
69 #include "cpml-line.h"
70 #include "cpml-curve.h"
71 #include "cpml-pair.h"
72 #include "cpml-alloca.h"
74 #include <stdio.h>
75 #include <string.h>
77 static cairo_bool_t normalize (CpmlSegment *segment);
78 static cairo_bool_t ensure_one_move_to (CpmlSegment *segment);
79 static void reshape (CpmlSegment *segment);
82 /**
83 * cpml_segment_from_cairo:
84 * @segment: a #CpmlSegment
85 * @path: the source #CpmlPath
87 * Builds a CpmlSegment from a #CpmlPath structure. This operation
88 * involves stripping the leading %CAIRO_PATH_MOVE_TO primitives and
89 * setting the internal segment structure accordling. A pointer to the
90 * source cairo path is kept.
92 * This function will fail if @path is null, empty or if its
93 * <structfield>status</structfield> member is not %CAIRO_STATUS_SUCCESS.
94 * Also, the first primitive must be a %CAIRO_PATH_MOVE_TO, so no
95 * dependency on the cairo context is needed.
97 * Return value: 1 on success, 0 on errors
98 **/
99 cairo_bool_t
100 cpml_segment_from_cairo(CpmlSegment *segment, CpmlPath *path)
102 /* The cairo path should be defined and in a perfect state */
103 if (path == NULL || path->num_data == 0 ||
104 path->status != CAIRO_STATUS_SUCCESS)
105 return 0;
107 segment->path = path;
108 segment->data = path->data;
109 segment->num_data = path->num_data;
111 return normalize(segment);
115 * cpml_segment_copy:
116 * @segment: a #CpmlSegment
117 * @src: the source segment to copy
119 * Makes a shallow copy of @src into @segment.
121 * Return value: @segment or %NULL on errors
123 CpmlSegment *
124 cpml_segment_copy(CpmlSegment *segment, const CpmlSegment *src)
126 if (segment == NULL || src == NULL)
127 return NULL;
129 return memcpy(segment, src, sizeof(CpmlSegment));
134 * cpml_segment_reset:
135 * @segment: a #CpmlSegment
137 * Modifies @segment to point to the first segment of the source cairo path.
139 void
140 cpml_segment_reset(CpmlSegment *segment)
142 segment->data = segment->path->data;
143 segment->num_data = segment->path->num_data;
144 normalize(segment);
148 * cpml_segment_next:
149 * @segment: a #CpmlSegment
151 * Modifies @segment to point to the next segment of the source cairo path.
153 * Return value: 1 on success, 0 if no next segment found or errors
155 cairo_bool_t
156 cpml_segment_next(CpmlSegment *segment)
158 int rest = segment->path->num_data - segment->num_data +
159 segment->path->data - segment->data;
161 if (rest <= 0)
162 return 0;
164 segment->data += segment->num_data;
165 segment->num_data = rest;
167 return normalize(segment);
172 * cpml_segment_to_cairo:
173 * @segment: a #CpmlSegment
174 * @cr: the destination cairo context
176 * Appends the path of @segment to @cr. The segment is "flattened",
177 * that is %CAIRO_PATH_ARC_TO primitives are approximated by one
178 * or more %CAIRO_PATH_CURVE_TO using cpml_arc_to_cairo(). Check
179 * its documentation for further details.
181 void
182 cpml_segment_to_cairo(const CpmlSegment *segment, cairo_t *cr)
184 CpmlPrimitive primitive;
186 cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment);
188 do {
189 cpml_primitive_to_cairo(&primitive, cr);
190 } while (cpml_primitive_next(&primitive));
194 * cpml_segment_dump:
195 * @segment: a #CpmlSegment
197 * Dumps the specified @segment to stdout. Useful for debugging purposes.
199 void
200 cpml_segment_dump(const CpmlSegment *segment)
202 CpmlPrimitive primitive;
203 cairo_bool_t first_call = 1;
205 cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment);
207 do {
208 cpml_primitive_dump(&primitive, first_call);
209 first_call = 0;
210 } while (cpml_primitive_next(&primitive));
215 * cpml_segment_reverse:
216 * @segment: a #CpmlSegment
218 * Reverses @segment in-place. The resulting rendering will be the same,
219 * but with the primitives generated in reverse order.
221 void
222 cpml_segment_reverse(CpmlSegment *segment)
224 cairo_path_data_t *data, *dst_data;
225 size_t data_size;
226 double end_x, end_y;
227 int n, num_points, n_point;
228 const cairo_path_data_t *src_data;
230 data_size = sizeof(cairo_path_data_t) * segment->num_data;
231 data = cpml_alloca(data_size);
232 end_x = segment->data[1].point.x;
233 end_y = segment->data[1].point.y;
235 for (n = 2; n < segment->num_data; ++n) {
236 src_data = segment->data + n;
237 num_points = src_data->header.length;
239 dst_data = data + segment->num_data - n - num_points + 2;
240 dst_data->header.type = src_data->header.type;
241 dst_data->header.length = num_points;
243 for (n_point = 1; n_point < num_points; ++n_point) {
244 dst_data[num_points - n_point].point.x = end_x;
245 dst_data[num_points - n_point].point.y = end_y;
246 end_x = src_data[n_point].point.x;
247 end_y = src_data[n_point].point.y;
250 n += n_point - 1;
253 data[0].header.type = CAIRO_PATH_MOVE_TO;
254 data[0].header.length = 2;
255 data[1].point.x = end_x;
256 data[1].point.y = end_y;
257 memcpy(segment->data, data, data_size);
261 * cpml_segment_transform:
262 * @segment: a #CpmlSegment
263 * @matrix: the matrix to be applied
265 * Applies @matrix on all the points of @segment.
267 void
268 cpml_segment_transform(CpmlSegment *segment, const cairo_matrix_t *matrix)
270 cairo_path_data_t *data;
271 int n, n_point, num_points;
273 data = segment->data;
275 for (n = 0; n < segment->num_data; n += num_points) {
276 num_points = data->header.length;
277 ++data;
278 for (n_point = 1; n_point < num_points; ++n_point) {
279 cairo_matrix_transform_point(matrix, &data->point.x, &data->point.y);
280 ++data;
286 * cpml_segment_intersection:
287 * @segment: the first #CpmlSegment
288 * @segment2: the second #CpmlSegment
289 * @dest: the destination vector of #CpmlPair
290 * @max: maximum number of intersections to return
292 * Computes the intersections between @segment and @segment2 and
293 * returns the found points in @dest. If the intersections are more
294 * than @max, only the first @max pairs are stored in @dest.
296 * To get the job done, the primitives of @segment are sequentially
297 * scanned for intersections with any primitive in @segment2. This
298 * means @segment has a higher precedence over @segment2.
300 * Return value: the number of intersections found
303 cpml_segment_intersection(const CpmlSegment *segment,
304 const CpmlSegment *segment2,
305 CpmlPair *dest, int max)
307 CpmlPrimitive portion;
308 int partial, total;
310 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
311 total = 0;
313 do {
314 partial = cpml_primitive_intersection_with_segment(&portion,
315 segment2,
316 dest + total,
317 max - total);
318 total += partial;
319 } while (total < max && cpml_primitive_next(&portion));
321 return total;
325 * cpml_segment_offset:
326 * @segment: a #CpmlSegment
327 * @offset: the offset distance
329 * Offsets a segment of the specified amount, that is builds a "parallel"
330 * segment at the @offset distance from the original one and returns the
331 * result by replacing the original @segment.
333 * <important>
334 * <title>TODO</title>
335 * <itemizedlist>
336 * <listitem>Closed path are not yet managed: an elegant solution is not
337 * so obvious: use cpml_close_offset() when available.</listitem>
338 * <listitem>Degenerated primitives, such as lines of length 0, are not
339 * managed properly.</listitem>
340 * </itemizedlist>
341 * </important>
343 void
344 cpml_segment_offset(CpmlSegment *segment, double offset)
346 CpmlPrimitive primitive;
347 CpmlPrimitive last_primitive;
348 cairo_path_data_t org, old_end;
349 cairo_bool_t first_cycle;
351 cpml_primitive_from_segment(&primitive, segment);
352 first_cycle = 1;
354 do {
355 if (!first_cycle) {
356 org = old_end;
357 primitive.org = &org;
360 old_end = *cpml_primitive_get_point(&primitive, -1);
361 cpml_primitive_offset(&primitive, offset);
363 if (!first_cycle) {
364 cpml_primitive_join(&last_primitive, &primitive);
365 primitive.org = cpml_primitive_get_point(&last_primitive, -1);
368 cpml_primitive_copy(&last_primitive, &primitive);
369 first_cycle = 0;
370 } while (cpml_primitive_next(&primitive));
374 * normalize:
375 * @segment: a #CpmlSegment
377 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
378 * the CpmlSegment structure accordling. One, and only one,
379 * %CAIRO_PATH_MOVE_TO primitive is left.
381 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
383 static cairo_bool_t
384 normalize(CpmlSegment *segment)
386 if (!ensure_one_move_to(segment))
387 return 0;
389 reshape(segment);
390 return 1;
394 * ensure_one_move_to:
395 * @segment: a #CpmlSegment
397 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
398 * the <structname>CpmlSegment</structname> structure accordling.
399 * One, and only one, %CAIRO_PATH_MOVE_TO primitive is left.
401 * Return value: 1 on success, 0 on no leading MOVE_TOs or on empty path
403 static cairo_bool_t
404 ensure_one_move_to(CpmlSegment *segment)
406 cairo_path_data_t *new_data;
407 int new_num_data, length;
409 new_data = segment->data;
411 /* Check for at least one move to */
412 if (new_data->header.type != CAIRO_PATH_MOVE_TO)
413 return 0;
415 new_num_data = segment->num_data;
416 length = 0;
418 /* Strip the leading CAIRO_PATH_MOVE_TO, leaving only the last one */
419 do {
420 new_data += length;
421 new_num_data -= length;
422 length = new_data->header.length;
424 /* Check for end of cairo path data */
425 if (length >= new_num_data)
426 return 0;
427 } while (new_data[length].header.type == CAIRO_PATH_MOVE_TO);
429 segment->data = new_data;
430 segment->num_data = new_num_data;
432 return 1;
436 * reshape:
437 * @segment: a #CpmlSegment
439 * Looks for the segment termination and modify the
440 * <structfield>num_data</structfield> field of @segment accordling.
441 * @segment must have only one leading %CAIRO_PATH_MOVE_TO and
442 * it is supposed to be non-empty, conditions yet imposed by the
443 * ensure_one_move_to() function.
445 static void
446 reshape(CpmlSegment *segment)
448 cairo_path_data_t *data;
449 int num_data, new_num_data, length;
451 /* Skip the leading move to */
452 new_num_data = 2;
453 data = segment->data + new_num_data;
455 /* Calculate the remaining data in the cairo path */
456 num_data = segment->path->num_data -
457 (segment->data - segment->path->data);
459 while (new_num_data < num_data) {
460 /* A primitive is considered valid if it has implemented
461 * its own type_get_npoints() */
462 if (cpml_primitive_type_get_npoints(data->header.type) < 0)
463 break;
465 length = data->header.length;
466 data += length;
467 new_num_data += length;
470 segment->num_data = new_num_data;