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.
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.
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.
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"
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
);
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
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
)
93 segment
->source
= cairo_path
;
94 segment
->data
= cairo_path
->data
;
95 segment
->num_data
= cairo_path
->num_data
;
97 return segment_normalize(segment
);
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
110 cpml_segment_copy(CpmlSegment
*segment
, const CpmlSegment
*src
)
112 if (segment
== NULL
|| src
== NULL
)
115 return memcpy(segment
, src
, sizeof(CpmlSegment
));
120 * @segment: a #CpmlSegment
122 * Dumps the specified @segment to stdout. Useful for debugging purposes.
125 cpml_segment_dump(const CpmlSegment
*segment
)
127 cairo_path_data_t
*data
;
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
:
137 case CAIRO_PATH_LINE_TO
:
140 case CAIRO_PATH_CURVE_TO
:
143 case CAIRO_PATH_CLOSE_PATH
:
144 printf("Path close");
147 printf("Unknown entity (%d)", data
->header
.type
);
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
);
161 * cpml_segment_reset:
162 * @segment: a #CpmlSegment
164 * Modifies @segment to point to the first segment of the source path.
167 cpml_segment_reset(CpmlSegment
*segment
)
169 segment
->data
= segment
->source
->data
;
170 segment
->num_data
= segment
->source
->num_data
;
171 segment_normalize(segment
);
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
183 cpml_segment_next(CpmlSegment
*segment
)
185 int rest
= segment
->source
->num_data
- segment
->num_data
+
186 segment
->source
->data
- segment
->data
;
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.
205 cpml_segment_reverse(CpmlSegment
*segment
)
207 cairo_path_data_t
*data
, *dst_data
;
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
;
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.
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
;
261 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
262 cairo_matrix_transform_point(matrix
, &data
->point
.x
, &data
->point
.y
);
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.
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
;
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
:
316 case CAIRO_PATH_LINE_TO
:
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]);
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]);
337 case CAIRO_PATH_CURVE_TO
:
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]);
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]);
362 case CAIRO_PATH_CLOSE_PATH
:
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
;
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
393 segment_normalize(CpmlSegment
*segment
)
395 cairo_path_data_t
*data
;
397 if (segment
->source
->data
->header
.type
!= CAIRO_PATH_MOVE_TO
) {
401 data
= segment
->data
;
403 while (segment
->num_data
>= 0) {
405 if (data
->header
.type
!= CAIRO_PATH_MOVE_TO
)
408 segment
->data
= data
;
409 segment
->num_data
-= 2;
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.
428 join_primitives(cairo_path_data_t
*last_data
, const CpmlVector
*last_vector
,
429 const CpmlPair
*new_point
, const CpmlVector
*new_vector
)
433 if (last_data
== NULL
)
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
;
445 last_data
->point
.x
= new_point
->x
;
446 last_data
->point
.y
= new_point
->y
;