[docs] Improved CPML doc
[adg.git] / cpml / cpml-segment.c
blob6ae8a0a76469d402bb4e8adcb23ac78eed188520
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 CpmlSegment struct internally keeps a reference to the source
30 * cairo path so it can be used both for getting segment data and
31 * browsing the segments (that is, it is used also as an iterator).
33 * Use cpml_segment_reset() to reset the iterator at the start of the cairo
34 * path (at the first segment) and cpml_segment_next() to get the next
35 * segment. Getting the previous segment is not provided, as the underlying
36 * struct is not accessible in reverse order.
37 **/
39 /**
40 * CpmlSegment:
41 * @source: the source #cairo_path_t struct
42 * @data: the segment data
43 * @num_data: size of @data
45 * This is an unobtrusive struct to identify a segment inside a
46 * cairo path. Unobtrusive means that the real coordinates are
47 * still stored in @source: CpmlSegment only provides a way to
48 * access the underlying cairo path.
49 **/
51 #include "cpml-segment.h"
52 #include "cpml-primitive.h"
53 #include "cpml-line.h"
54 #include "cpml-curve.h"
55 #include "cpml-pair.h"
56 #include "cpml-alloca.h"
58 #include <stdio.h>
59 #include <string.h>
61 static cairo_bool_t segment_normalize (CpmlSegment *segment);
62 static void join_primitives (cairo_path_data_t *last_data,
63 const CpmlVector *last_vector,
64 const CpmlPair *new_point,
65 const CpmlVector *new_vector);
68 /**
69 * cpml_segment_from_cairo:
70 * @segment: a #CpmlSegment
71 * @cairo_path: the source cairo_path_t
73 * Builds a CpmlSegment from a cairo_path_t structure. This operation
74 * involves stripping the leading %CAIRO_PATH_MOVE_TO primitives and
75 * setting the internal segment structure accordling. A pointer to the
76 * source cairo path is kept.
78 * This function will fail if @cairo_path is null, empty or if its
79 * %status member is not %CAIRO_STATUS_SUCCESS. Also, the first
80 * primitive must be a %CAIRO_PATH_MOVE_TO, so no dependency on the
81 * cairo context is needed.
83 * Return value: 1 on success, 0 on errors
84 **/
85 cairo_bool_t
86 cpml_segment_from_cairo(CpmlSegment *segment, cairo_path_t *cairo_path)
88 /* The cairo path should be defined and in perfect state */
89 if (cairo_path == NULL || cairo_path->num_data == 0 ||
90 cairo_path->status != CAIRO_STATUS_SUCCESS)
91 return 0;
93 segment->source = cairo_path;
94 segment->data = cairo_path->data;
95 segment->num_data = cairo_path->num_data;
97 return segment_normalize(segment);
101 * cpml_segment_copy:
102 * @segment: a #CpmlSegment
103 * @src: the source segment to copy
105 * Makes a shallow copy of @src into @segment.
107 * Return value: @segment or %NULL on errors
109 CpmlSegment *
110 cpml_segment_copy(CpmlSegment *segment, const CpmlSegment *src)
112 if (segment == NULL || src == NULL)
113 return NULL;
115 return memcpy(segment, src, sizeof(CpmlSegment));
119 * cpml_segment_dump:
120 * @segment: a #CpmlSegment
122 * Dumps the specified @segment to stdout. Useful for debugging purposes.
124 void
125 cpml_segment_dump(const CpmlSegment *segment)
127 cairo_path_data_t *data;
128 int n, n_point;
130 for (n = 0; n < segment->num_data; ++n) {
131 data = segment->data + n;
133 switch (data->header.type) {
134 case CAIRO_PATH_MOVE_TO:
135 printf("Move to ");
136 break;
137 case CAIRO_PATH_LINE_TO:
138 printf("Line to ");
139 break;
140 case CAIRO_PATH_CURVE_TO:
141 printf("Curve to ");
142 break;
143 case CAIRO_PATH_CLOSE_PATH:
144 printf("Path close");
145 break;
146 default:
147 printf("Unknown entity (%d)", data->header.type);
148 break;
151 for (n_point = 1; n_point < data->header.length; ++n_point)
152 printf("(%lf, %lf) ", data[n_point].point.x,
153 data[n_point].point.y);
155 n += n_point - 1;
156 printf("\n");
161 * cpml_segment_reset:
162 * @segment: a #CpmlSegment
164 * Modifies @segment to point to the first segment of the source path.
166 void
167 cpml_segment_reset(CpmlSegment *segment)
169 segment->data = segment->source->data;
170 segment->num_data = segment->source->num_data;
171 segment_normalize(segment);
175 * cpml_segment_next:
176 * @segment: a #CpmlSegment
178 * Modifies @segment to point to the next segment of the source path.
180 * Return value: 1 on success, 0 if no next segment found or errors
182 cairo_bool_t
183 cpml_segment_next(CpmlSegment *segment)
185 int rest = segment->source->num_data - segment->num_data +
186 segment->source->data - segment->data;
188 if (rest <= 0)
189 return 0;
191 segment->data += segment->num_data;
192 segment->num_data = rest;
194 return segment_normalize(segment);
198 * cpml_segment_reverse:
199 * @segment: a #CpmlSegment
201 * Reverses @segment in-place. The resulting rendering will be the same,
202 * but with the primitives generated in reverse order.
204 void
205 cpml_segment_reverse(CpmlSegment *segment)
207 cairo_path_data_t *data, *dst_data;
208 size_t data_size;
209 double end_x, end_y;
210 int n, num_points, n_point;
211 const cairo_path_data_t *src_data;
213 data_size = sizeof(cairo_path_data_t) * segment->num_data;
214 data = cpml_alloca(data_size);
215 end_x = segment->data[1].point.x;
216 end_y = segment->data[1].point.y;
218 for (n = 2; n < segment->num_data; ++n) {
219 src_data = segment->data + n;
220 num_points = src_data->header.length;
222 dst_data = data + segment->num_data - n - num_points + 2;
223 dst_data->header.type = src_data->header.type;
224 dst_data->header.length = num_points;
226 for (n_point = 1; n_point < num_points; ++n_point) {
227 dst_data[num_points - n_point].point.x = end_x;
228 dst_data[num_points - n_point].point.y = end_y;
229 end_x = src_data[n_point].point.x;
230 end_y = src_data[n_point].point.y;
233 n += n_point - 1;
236 data[0].header.type = CAIRO_PATH_MOVE_TO;
237 data[0].header.length = 2;
238 data[1].point.x = end_x;
239 data[1].point.y = end_y;
240 memcpy(segment->data, data, data_size);
244 * cpml_segment_transform:
245 * @segment: a #CpmlSegment
246 * @matrix: the matrix to be applied
248 * Applies @matrix on all the points of @segment.
250 void
251 cpml_segment_transform(CpmlSegment *segment, const cairo_matrix_t *matrix)
253 cairo_path_data_t *data;
254 int n, n_point, num_points;
256 data = segment->data;
258 for (n = 0; n < segment->num_data; n += num_points) {
259 num_points = data->header.length;
260 ++data;
261 for (n_point = 1; n_point < num_points; ++n_point) {
262 cairo_matrix_transform_point(matrix, &data->point.x, &data->point.y);
263 ++data;
269 * cpml_segment_offset:
270 * @segment: a #CpmlSegment
271 * @offset: the offset distance
273 * Offsets a segment of the specified amount, that is builds a "parallel"
274 * segment at the @offset distance from the original one and returns the
275 * result by replacing the original @segment.
277 * <important>
278 * <title>TODO</title>
279 * <itemizedlist>
280 * <listitem>Closed path are not yet managed: an elegant solution is not
281 * so obvious: use cpml_close_offset() when available.</listitem>
282 * </itemizedlist>
283 * </important>
285 * Return value: 1 on success, 0 on errors
287 cairo_bool_t
288 cpml_segment_offset(CpmlSegment *segment, double offset)
290 int n, num_points, n_point;
291 cairo_path_data_t *data;
292 cairo_path_data_t *last_data;
293 CpmlVector v_old, v_new;
294 CpmlPair p_old;
295 CpmlPair p[4];
297 data = segment->data;
298 last_data = NULL;
299 p_old.x = 0;
300 p_old.y = 0;
302 for (n = 0; n < segment->num_data; n += data->header.length) {
303 data = segment->data + n;
304 num_points = data->header.length - 1;
306 /* Fill the p[] vector */
307 cpml_pair_copy(&p[0], &p_old);
308 for (n_point = 1; n_point <= num_points; ++ n_point) {
309 p[n_point].x = data[n_point].point.x;
310 p[n_point].y = data[n_point].point.y;
313 /* Save the last direction vector in v_old */
314 cpml_pair_copy(&v_old, &v_new);
316 switch (data->header.type) {
318 case CAIRO_PATH_MOVE_TO:
319 v_new.x = 0;
320 v_new.y = 0;
321 break;
323 case CAIRO_PATH_LINE_TO:
325 CpmlPrimitive line;
326 cairo_path_data_t dummy[3];
328 cpml_pair_to_cairo(&p[0], &dummy[0]);
329 dummy[1].header.type = CAIRO_PATH_LINE_TO;
330 dummy[1].header.length = 2;
331 cpml_pair_to_cairo(&p[1], &dummy[2]);
333 line.source = NULL;
334 line.org = &dummy[0];
335 line.data = &dummy[1];
337 cpml_line_offset(&line, offset);
339 cpml_pair_from_cairo(&p[0], &dummy[0]);
340 cpml_pair_from_cairo(&p[1], &dummy[2]);
342 break;
344 case CAIRO_PATH_CURVE_TO:
346 CpmlPrimitive curve;
347 cairo_path_data_t dummy[5];
349 cpml_pair_to_cairo(&p[0], &dummy[0]);
350 dummy[1].header.type = CAIRO_PATH_CURVE_TO;
351 dummy[1].header.length = 4;
352 cpml_pair_to_cairo(&p[1], &dummy[2]);
353 cpml_pair_to_cairo(&p[2], &dummy[3]);
354 cpml_pair_to_cairo(&p[3], &dummy[4]);
356 curve.source = NULL;
357 curve.org = &dummy[0];
358 curve.data = &dummy[1];
360 cpml_curve_offset(&curve, offset);
362 cpml_pair_from_cairo(&p[0], &dummy[0]);
363 cpml_pair_from_cairo(&p[1], &dummy[2]);
364 cpml_pair_from_cairo(&p[2], &dummy[3]);
365 cpml_pair_from_cairo(&p[3], &dummy[4]);
367 break;
369 case CAIRO_PATH_CLOSE_PATH:
370 return 1;
373 join_primitives(last_data, &v_old, &p[0], &v_new);
375 /* Save the end point of the original primitive in p_old */
376 last_data = data + num_points;
377 p_old.x = last_data->point.x;
378 p_old.y = last_data->point.y;
380 /* Store the results got from the p[] vector in the cairo path */
381 for (n_point = 1; n_point <= num_points; ++ n_point) {
382 data[n_point].point.x = p[n_point].x;
383 data[n_point].point.y = p[n_point].y;
387 return 1;
391 * segment_normalize:
392 * @segment: a #CpmlSegment
394 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
395 * the CpmlSegment structure accordling. One, and only one,
396 * %CAIRO_PATH_MOVE_TO primitive is left.
398 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
400 static cairo_bool_t
401 segment_normalize(CpmlSegment *segment)
403 cairo_path_data_t *data;
405 if (segment->source->data->header.type != CAIRO_PATH_MOVE_TO) {
406 return 0;
409 data = segment->data;
411 while (segment->num_data >= 0) {
412 data += 2;
413 if (data->header.type != CAIRO_PATH_MOVE_TO)
414 return 1;
416 segment->data = data;
417 segment->num_data -= 2;
420 return 0;
424 * join_primitives:
425 * @last_data: the previous primitive end data point (if any)
426 * @last_vector: @last_data direction vector
427 * @new_point: the new primitive starting point
428 * @new_vector: @new_point direction vector
430 * Joins two primitive modifying the end point of the first one (stored
431 * as cairo path data in @last_data).
433 * <important>
434 * <title>TODO</title>
435 * <itemizedlist>
436 * <listitem>This approach is quite naive when curves are involved.</listitem>
437 * </itemizedlist>
438 * </important>
440 static void
441 join_primitives(cairo_path_data_t *last_data, const CpmlVector *last_vector,
442 const CpmlPair *new_point, const CpmlVector *new_vector)
444 CpmlPair last_point;
446 if (last_data == NULL)
447 return;
449 last_point.x = last_data->point.x;
450 last_point.y = last_data->point.y;
452 if (cpml_pair_intersection_pv_pv(&last_point,
453 &last_point, last_vector,
454 new_point, new_vector)) {
455 last_data->point.x = last_point.x;
456 last_data->point.y = last_point.y;
457 } else {
458 last_data->point.x = new_point->x;
459 last_data->point.y = new_point->y;