[CpmlLine] Initial implementation
[adg.git] / cpml / cpml-segment.c
blob67f80413420fc3a0d51fa81f1d81930a6b05a4cd
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 * Return value: 1 on success, 0 on errors
279 * @todo: closed path are not yet managed; the solution is not so obvious.
281 cairo_bool_t
282 cpml_segment_offset(CpmlSegment *segment, double offset)
284 int n, num_points, n_point;
285 cairo_path_data_t *data;
286 cairo_path_data_t *last_data;
287 CpmlVector v_old, v_new;
288 CpmlPair p_old;
289 CpmlPair p[4];
291 last_data = NULL;
292 p_old.x = 0;
293 p_old.y = 0;
295 for (n = 0; n < segment->num_data; n += data->header.length) {
296 data = segment->data + n;
297 num_points = data->header.length - 1;
299 /* Fill the p[] vector */
300 cpml_pair_copy(&p[0], &p_old);
301 for (n_point = 1; n_point <= num_points; ++ n_point) {
302 p[n_point].x = data[n_point].point.x;
303 p[n_point].y = data[n_point].point.y;
306 /* Save the last direction vector in v_old */
307 cpml_pair_copy(&v_old, &v_new);
309 switch (data->header.type) {
311 case CAIRO_PATH_MOVE_TO:
312 v_new.x = 0;
313 v_new.y = 0;
314 break;
316 case CAIRO_PATH_LINE_TO:
318 CpmlPrimitive line;
319 cairo_path_data_t dummy[3];
321 cpml_pair_to_cairo(&p[0], &dummy[0]);
322 dummy[1].header.type = CAIRO_PATH_LINE_TO;
323 dummy[1].header.length = 2;
324 cpml_pair_to_cairo(&p[1], &dummy[2]);
326 line.source = NULL;
327 line.org = &dummy[0];
328 line.data = &dummy[1];
330 cpml_line_offset(&line, offset);
332 cpml_pair_from_cairo(&p[0], &dummy[0]);
333 cpml_pair_from_cairo(&p[1], &dummy[2]);
335 break;
337 case CAIRO_PATH_CURVE_TO:
339 CpmlPrimitive curve;
340 cairo_path_data_t dummy[5];
342 cpml_pair_to_cairo(&p[0], &dummy[0]);
343 dummy[1].header.type = CAIRO_PATH_CURVE_TO;
344 dummy[1].header.length = 4;
345 cpml_pair_to_cairo(&p[1], &dummy[2]);
346 cpml_pair_to_cairo(&p[2], &dummy[3]);
347 cpml_pair_to_cairo(&p[3], &dummy[4]);
349 curve.source = NULL;
350 curve.org = &dummy[0];
351 curve.data = &dummy[1];
353 cpml_curve_offset(&curve, offset);
355 cpml_pair_from_cairo(&p[0], &dummy[0]);
356 cpml_pair_from_cairo(&p[1], &dummy[2]);
357 cpml_pair_from_cairo(&p[2], &dummy[3]);
358 cpml_pair_from_cairo(&p[3], &dummy[4]);
360 break;
362 case CAIRO_PATH_CLOSE_PATH:
363 return 1;
366 join_primitives(last_data, &v_old, &p[0], &v_new);
368 /* Save the end point of the original primitive in p_old */
369 last_data = data + num_points;
370 p_old.x = last_data->point.x;
371 p_old.y = last_data->point.y;
373 /* Store the results got from the p[] vector in the cairo path */
374 for (n_point = 1; n_point <= num_points; ++ n_point) {
375 data[n_point].point.x = p[n_point].x;
376 data[n_point].point.y = p[n_point].y;
380 return 1;
384 * segment_normalize:
385 * @segment: a #CpmlSegment
387 * Strips the leading CAIRO_PATH_MOVE_TO primitives, updating the CpmlSegment
388 * structure accordling. One, and only once, MOVE_TO primitive is left.
390 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
392 static cairo_bool_t
393 segment_normalize(CpmlSegment *segment)
395 cairo_path_data_t *data;
397 if (segment->source->data->header.type != CAIRO_PATH_MOVE_TO) {
398 return 0;
401 data = segment->data;
403 while (segment->num_data >= 0) {
404 data += 2;
405 if (data->header.type != CAIRO_PATH_MOVE_TO)
406 return 1;
408 segment->data = data;
409 segment->num_data -= 2;
412 return 0;
416 * join_primitives:
417 * @last_data: the previous primitive end data point (if any)
418 * @last_vector: @last_data direction vector
419 * @new_point: the new primitive starting point
420 * @new_vector: @new_point direction vector
422 * Joins two primitive modifying the end point of the first one (stored
423 * as cairo path data in @last_data).
425 * @todo: this approach is quite naive when curves are involved.
427 static void
428 join_primitives(cairo_path_data_t *last_data, const CpmlVector *last_vector,
429 const CpmlPair *new_point, const CpmlVector *new_vector)
431 CpmlPair last_point;
433 if (last_data == NULL)
434 return;
436 last_point.x = last_data->point.x;
437 last_point.y = last_data->point.y;
439 if (cpml_pair_intersection_pv_pv(&last_point,
440 &last_point, last_vector,
441 new_point, new_vector)) {
442 last_data->point.x = last_point.x;
443 last_data->point.y = last_point.y;
444 } else {
445 last_data->point.x = new_point->x;
446 last_data->point.y = new_point->y;