[CpmlArc] Added cpml_arc_length()
[adg.git] / cpml / cpml-segment.c
blob7efc0c5139453ebb4dd77b6ea7b5e6a223290391
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 CpmlPrimitive primitive;
172 cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment);
174 do {
175 cpml_primitive_to_cairo(&primitive, cr);
176 } while (cpml_primitive_next(&primitive));
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_intersection:
273 * @segment: the first #CpmlSegment
274 * @segment2: the second #CpmlSegment
275 * @dest: the destination vector of #CpmlPair
276 * @max: maximum number of intersections to return
278 * Computes the intersections between @segment and @segment2 and
279 * returns the found points in @dest. If the intersections are more
280 * than @max, only the first @max pairs are stored in @dest.
282 * To get the job done, the primitives of @segment are sequentially
283 * scanned for intersections with any primitive in @segment2. This
284 * means @segment has a higher precedence over @segment2.
286 * Return value: the number of intersections found
289 cpml_segment_intersection(const CpmlSegment *segment,
290 const CpmlSegment *segment2,
291 CpmlPair *dest, int max)
293 CpmlPrimitive portion;
294 int partial, total;
296 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
297 total = 0;
299 do {
300 partial = cpml_primitive_intersection_with_segment(&portion,
301 segment2,
302 dest + total,
303 max - total);
304 total += partial;
305 } while (total < max && cpml_primitive_next(&portion));
307 return total;
311 * cpml_segment_offset:
312 * @segment: a #CpmlSegment
313 * @offset: the offset distance
315 * Offsets a segment of the specified amount, that is builds a "parallel"
316 * segment at the @offset distance from the original one and returns the
317 * result by replacing the original @segment.
319 * <important>
320 * <title>TODO</title>
321 * <itemizedlist>
322 * <listitem>Closed path are not yet managed: an elegant solution is not
323 * so obvious: use cpml_close_offset() when available.</listitem>
324 * <listitem>Degenerated primitives, such as lines of length 0, are not
325 * managed properly.</listitem>
326 * </itemizedlist>
327 * </important>
329 void
330 cpml_segment_offset(CpmlSegment *segment, double offset)
332 CpmlPrimitive primitive;
333 CpmlPrimitive last_primitive;
334 cairo_path_data_t org, old_end;
335 cairo_bool_t first_cycle;
337 cpml_primitive_from_segment(&primitive, segment);
338 first_cycle = 1;
340 do {
341 if (!first_cycle) {
342 org = old_end;
343 primitive.org = &org;
346 old_end = *cpml_primitive_get_point(&primitive, -1);
347 cpml_primitive_offset(&primitive, offset);
349 if (!first_cycle) {
350 cpml_primitive_join(&last_primitive, &primitive);
351 primitive.org = cpml_primitive_get_point(&last_primitive, -1);
354 cpml_primitive_copy(&last_primitive, &primitive);
355 first_cycle = 0;
356 } while (cpml_primitive_next(&primitive));
360 * normalize:
361 * @segment: a #CpmlSegment
363 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
364 * the CpmlSegment structure accordling. One, and only one,
365 * %CAIRO_PATH_MOVE_TO primitive is left.
367 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
369 static cairo_bool_t
370 normalize(CpmlSegment *segment)
372 if (!ensure_one_move_to(segment))
373 return 0;
375 reshape(segment);
376 return 1;
380 * ensure_one_move_to:
381 * @segment: a #CpmlSegment
383 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
384 * the <structname>CpmlSegment</structname> structure accordling.
385 * One, and only one, %CAIRO_PATH_MOVE_TO primitive is left.
387 * Return value: 1 on success, 0 on no leading MOVE_TOs or on empty path
389 static cairo_bool_t
390 ensure_one_move_to(CpmlSegment *segment)
392 cairo_path_data_t *new_data;
393 int new_num_data, length;
395 new_data = segment->data;
397 /* Check for at least one move to */
398 if (new_data->header.type != CAIRO_PATH_MOVE_TO)
399 return 0;
401 new_num_data = segment->num_data;
402 length = 0;
404 /* Strip the leading CAIRO_PATH_MOVE_TO, leaving only the last one */
405 do {
406 new_data += length;
407 new_num_data -= length;
408 length = new_data->header.length;
410 /* Check for end of cairo path data */
411 if (length >= new_num_data)
412 return 0;
413 } while (new_data[length].header.type == CAIRO_PATH_MOVE_TO);
415 segment->data = new_data;
416 segment->num_data = new_num_data;
418 return 1;
422 * reshape:
423 * @segment: a #CpmlSegment
425 * Looks for the segment termination and modify the
426 * <structfield>num_data</structfield> field of @segment accordling.
427 * @segment must have only one leading %CAIRO_PATH_MOVE_TO and
428 * it is supposed to be non-empty, conditions yet imposed by the
429 * ensure_one_move_to() function.
431 static void
432 reshape(CpmlSegment *segment)
434 cairo_path_data_t *data;
435 int num_data, new_num_data, length;
437 /* Skip the leading move to */
438 new_num_data = 2;
439 data = segment->data + new_num_data;
441 /* Calculate the remaining data in the cairo path */
442 num_data = segment->cairo_path->num_data -
443 (segment->data - segment->cairo_path->data);
445 while (new_num_data < num_data) {
446 /* A primitive is considered valid if it has implemented
447 * its own type_get_npoints() */
448 if (cpml_primitive_type_get_npoints(data->header.type) < 0)
449 break;
451 length = data->header.length;
452 data += length;
453 new_num_data += length;
456 segment->num_data = new_num_data;