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.
278 * <title>TODO</title>
280 * <listitem>Closed path are not yet managed: an elegant solution is not
281 * so obvious: use cpml_close_offset() when available.</listitem>
285 * Return value: 1 on success, 0 on errors
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
;
297 data
= segment
->data
;
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
:
323 case CAIRO_PATH_LINE_TO
:
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]);
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]);
344 case CAIRO_PATH_CURVE_TO
:
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]);
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]);
369 case CAIRO_PATH_CLOSE_PATH
:
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
;
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
401 segment_normalize(CpmlSegment
*segment
)
403 cairo_path_data_t
*data
;
405 if (segment
->source
->data
->header
.type
!= CAIRO_PATH_MOVE_TO
) {
409 data
= segment
->data
;
411 while (segment
->num_data
>= 0) {
413 if (data
->header
.type
!= CAIRO_PATH_MOVE_TO
)
416 segment
->data
= data
;
417 segment
->num_data
-= 2;
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).
434 * <title>TODO</title>
436 * <listitem>This approach is quite naive when curves are involved.</listitem>
441 join_primitives(cairo_path_data_t
*last_data
, const CpmlVector
*last_vector
,
442 const CpmlPair
*new_point
, const CpmlVector
*new_vector
)
446 if (last_data
== NULL
)
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
;
458 last_data
->point
.x
= new_point
->x
;
459 last_data
->point
.y
= new_point
->y
;