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.
22 * SECTION:cpml-segment
23 * @Section_Id:CpmlSegment
25 * @short_description: Contiguous segment that can be a fragment
26 * or a whole cairo path
28 * A segment is a single contiguous line got from a cairo path. The
29 * CPML library relies on one assumption to let the data be independent
30 * from the current point (and thus from the cairo context): any segment
31 * MUST be preceded by at least one %CAIRO_PATH_MOVE_TO primitive.
32 * This means a valid segment in cairo could be rejected by CPML.
34 * #CpmlSegment provides an unobtrusive way to access a cairo path.
35 * This means #CpmlSegment itsself does not hold any coordinates but
36 * instead a bunch of pointers to the original #cairo_path_t struct:
37 * modifying data throught this struct also changes the original path.
39 * Every #CpmlPath struct can contain more than one segment: the CPML
40 * library provides iteration APIs to browse the segments of a path.
41 * Use cpml_segment_reset() to reset the iterator at the start of the
42 * cairo path (will point the first segment) and cpml_segment_next()
43 * to get the next segment. Getting the previous segment is not provided
44 * as the underlying cairo struct is not accessible in reverse order.
46 * When initialized, #CpmlSegment yet refers to the first segment so
47 * the initial reset is not required.
53 * This is another name for the #cairo_path_t type. Although phisically
54 * they are the same struct, #CpmlPath conceptually embodies an important
55 * difference: it is a cairo path that can embed %CAIRO_PATH_ARC_TO
56 * primitives. This is not a native cairo primitive and having two
57 * different data types is a good way to make clear when a function
58 * expect or not embedded arc-to primitives.
63 * @path: the source #CpmlPath struct
64 * @data: the data points of the segment; the first primitive
65 * will always be a %CAIRO_PATH_MOVE_TO
66 * @num_data: size of @data
68 * This is an unobtrusive struct to identify a segment inside a
69 * cairo path. Unobtrusive means that the real coordinates are
70 * still stored in @path: CpmlSegment only provides a way to
75 #include "cpml-segment.h"
76 #include "cpml-primitive.h"
77 #include "cpml-line.h"
78 #include "cpml-curve.h"
79 #include "cpml-pair.h"
80 #include "cpml-alloca.h"
85 static cairo_bool_t
normalize (CpmlSegment
*segment
);
86 static cairo_bool_t
ensure_one_move_to (CpmlSegment
*segment
);
87 static void reshape (CpmlSegment
*segment
);
91 * cpml_segment_from_cairo:
92 * @segment: a #CpmlSegment
93 * @path: the source #CpmlPath
95 * Builds a CpmlSegment from a #CpmlPath structure. This operation
96 * involves stripping the leading %CAIRO_PATH_MOVE_TO primitives and
97 * setting the internal segment structure accordling. A pointer to the
98 * source cairo path is kept.
100 * This function will fail if @path is null, empty or if its
101 * <structfield>status</structfield> member is not %CAIRO_STATUS_SUCCESS.
102 * Also, the first primitive must be a %CAIRO_PATH_MOVE_TO, so no
103 * dependency on the cairo context is needed.
105 * Return value: 1 on success, 0 on errors
108 cpml_segment_from_cairo(CpmlSegment
*segment
, CpmlPath
*path
)
110 /* The cairo path should be defined and in a perfect state */
111 if (path
== NULL
|| path
->num_data
== 0 ||
112 path
->status
!= CAIRO_STATUS_SUCCESS
)
115 segment
->path
= path
;
116 segment
->data
= path
->data
;
117 segment
->num_data
= path
->num_data
;
119 return normalize(segment
);
124 * @segment: a #CpmlSegment
125 * @src: the source segment to copy
127 * Makes a shallow copy of @src into @segment.
129 * Return value: @segment or %NULL on errors
132 cpml_segment_copy(CpmlSegment
*segment
, const CpmlSegment
*src
)
134 if (segment
== NULL
|| src
== NULL
)
137 return memcpy(segment
, src
, sizeof(CpmlSegment
));
142 * cpml_segment_reset:
143 * @segment: a #CpmlSegment
145 * Modifies @segment to point to the first segment of the source cairo path.
148 cpml_segment_reset(CpmlSegment
*segment
)
150 segment
->data
= segment
->path
->data
;
151 segment
->num_data
= segment
->path
->num_data
;
157 * @segment: a #CpmlSegment
159 * Modifies @segment to point to the next segment of the source cairo path.
161 * Return value: 1 on success, 0 if no next segment found or errors
164 cpml_segment_next(CpmlSegment
*segment
)
166 int rest
= segment
->path
->num_data
- segment
->num_data
+
167 segment
->path
->data
- segment
->data
;
172 segment
->data
+= segment
->num_data
;
173 segment
->num_data
= rest
;
175 return normalize(segment
);
180 * cpml_segment_length:
181 * @segment: a #CpmlSegment
183 * Gets the whole length of @segment.
185 * Returns: the requested length
188 cpml_segment_length(const CpmlSegment
*segment
)
190 CpmlPrimitive primitive
;
193 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
197 length
+= cpml_primitive_length(&primitive
);
198 } while (cpml_primitive_next(&primitive
));
204 * cpml_segment_extents:
205 * @segment: a #CpmlSegment
206 * @extents: where to store the extents
208 * Gets the whole extents of @segment.
211 cpml_segment_extents(const CpmlSegment
*segment
, CpmlExtents
*extents
)
213 CpmlPrimitive primitive
;
214 CpmlExtents primitive_extents
;
216 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
217 extents
->is_defined
= 0;
220 cpml_primitive_extents(&primitive
, &primitive_extents
);
221 cpml_extents_add(extents
, &primitive_extents
);
222 } while (cpml_primitive_next(&primitive
));
226 * cpml_segment_pair_at:
227 * @segment: a #CpmlSegment
228 * @pair: the destination #CpmlPair
229 * @pos: the position value
231 * Gets the coordinates of the point lying on @segment at position
232 * @pos. @pos is an homogeneous factor where %0 is the start point,
233 * %1 the end point, %0.5 the mid point and so on.
234 * The relation %0 < @pos < %1 should be satisfied, although some
235 * cases accept value outside this range.
238 * <title>TODO</title>
240 * <listitem>The actual implementation returns only the start and end points,
241 * that is only when @pos is %0 or %1.</listitem>
246 cpml_segment_pair_at(const CpmlSegment
*segment
, CpmlPair
*pair
, double pos
)
248 CpmlPrimitive primitive
;
250 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
252 /* Handle the common cases: start and end points */
254 return cpml_primitive_pair_at(&primitive
, pair
, 0);
257 while (cpml_primitive_next(&primitive
))
259 return cpml_primitive_pair_at(&primitive
, pair
, 1);
264 * cpml_segment_vector_at:
265 * @segment: a #CpmlSegment
266 * @vector: the destination #CpmlVector
267 * @pos: the position value
269 * Gets the steepness of the point lying on @segment at position
270 * @pos. @pos is an homogeneous factor where %0 is the start point,
271 * %1 the end point, %0.5 the mid point and so on.
272 * The relation %0 < @pos < %1 should be satisfied, although some
273 * cases accept value outside this range.
276 * <title>TODO</title>
278 * <listitem>The actual implementation returns only the start and end
279 * steepness, that is only when @pos is %0 or %1.</listitem>
284 cpml_segment_vector_at(const CpmlSegment
*segment
,
285 CpmlVector
*vector
, double pos
)
287 CpmlPrimitive primitive
;
289 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
291 /* Handle the common cases: start and end points */
293 cpml_primitive_vector_at(&primitive
, vector
, 0);
298 while (cpml_primitive_next(&primitive
))
300 cpml_primitive_vector_at(&primitive
, vector
, 1);
307 * cpml_segment_to_cairo:
308 * @segment: a #CpmlSegment
309 * @cr: the destination cairo context
311 * Appends the path of @segment to @cr. The segment is "flattened",
312 * that is %CAIRO_PATH_ARC_TO primitives are approximated by one
313 * or more %CAIRO_PATH_CURVE_TO using cpml_arc_to_cairo(). Check
314 * its documentation for further details.
317 cpml_segment_to_cairo(const CpmlSegment
*segment
, cairo_t
*cr
)
319 CpmlPrimitive primitive
;
321 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
324 cpml_primitive_to_cairo(&primitive
, cr
);
325 } while (cpml_primitive_next(&primitive
));
330 * @segment: a #CpmlSegment
332 * Dumps the specified @segment to stdout. Useful for debugging purposes.
335 cpml_segment_dump(const CpmlSegment
*segment
)
337 CpmlPrimitive primitive
;
338 cairo_bool_t first_call
= 1;
340 cpml_primitive_from_segment(&primitive
, (CpmlSegment
*) segment
);
343 cpml_primitive_dump(&primitive
, first_call
);
345 } while (cpml_primitive_next(&primitive
));
350 * cpml_segment_reverse:
351 * @segment: a #CpmlSegment
353 * Reverses @segment in-place. The resulting rendering will be the same,
354 * but with the primitives generated in reverse order.
357 cpml_segment_reverse(CpmlSegment
*segment
)
359 cairo_path_data_t
*data
, *dst_data
;
362 int n
, num_points
, n_point
;
363 const cairo_path_data_t
*src_data
;
365 data_size
= sizeof(cairo_path_data_t
) * segment
->num_data
;
366 data
= cpml_alloca(data_size
);
367 end_x
= segment
->data
[1].point
.x
;
368 end_y
= segment
->data
[1].point
.y
;
370 for (n
= 2; n
< segment
->num_data
; ++n
) {
371 src_data
= segment
->data
+ n
;
372 num_points
= src_data
->header
.length
;
374 dst_data
= data
+ segment
->num_data
- n
- num_points
+ 2;
375 dst_data
->header
.type
= src_data
->header
.type
;
376 dst_data
->header
.length
= num_points
;
378 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
379 dst_data
[num_points
- n_point
].point
.x
= end_x
;
380 dst_data
[num_points
- n_point
].point
.y
= end_y
;
381 end_x
= src_data
[n_point
].point
.x
;
382 end_y
= src_data
[n_point
].point
.y
;
388 data
[0].header
.type
= CAIRO_PATH_MOVE_TO
;
389 data
[0].header
.length
= 2;
390 data
[1].point
.x
= end_x
;
391 data
[1].point
.y
= end_y
;
392 memcpy(segment
->data
, data
, data_size
);
396 * cpml_segment_transform:
397 * @segment: a #CpmlSegment
398 * @matrix: the matrix to be applied
400 * Applies @matrix on all the points of @segment.
403 cpml_segment_transform(CpmlSegment
*segment
, const cairo_matrix_t
*matrix
)
405 cairo_path_data_t
*data
;
406 int n
, n_point
, num_points
;
408 data
= segment
->data
;
410 for (n
= 0; n
< segment
->num_data
; n
+= num_points
) {
411 num_points
= data
->header
.length
;
413 for (n_point
= 1; n_point
< num_points
; ++n_point
) {
414 cairo_matrix_transform_point(matrix
, &data
->point
.x
, &data
->point
.y
);
421 * cpml_segment_intersection:
422 * @segment: the first #CpmlSegment
423 * @segment2: the second #CpmlSegment
424 * @dest: the destination vector of #CpmlPair
425 * @max: maximum number of intersections to return
427 * Computes the intersections between @segment and @segment2 and
428 * returns the found points in @dest. If the intersections are more
429 * than @max, only the first @max pairs are stored in @dest.
431 * To get the job done, the primitives of @segment are sequentially
432 * scanned for intersections with any primitive in @segment2. This
433 * means @segment has a higher precedence over @segment2.
435 * Return value: the number of intersections found
438 cpml_segment_intersection(const CpmlSegment
*segment
,
439 const CpmlSegment
*segment2
,
440 CpmlPair
*dest
, int max
)
442 CpmlPrimitive portion
;
445 cpml_primitive_from_segment(&portion
, (CpmlSegment
*) segment
);
449 partial
= cpml_primitive_intersection_with_segment(&portion
,
454 } while (total
< max
&& cpml_primitive_next(&portion
));
460 * cpml_segment_offset:
461 * @segment: a #CpmlSegment
462 * @offset: the offset distance
464 * Offsets a segment of the specified amount, that is builds a "parallel"
465 * segment at the @offset distance from the original one and returns the
466 * result by replacing the original @segment.
469 * <title>TODO</title>
471 * <listitem>Closed path are not yet managed: an elegant solution is not
472 * so obvious: use cpml_close_offset() when available.</listitem>
473 * <listitem>Degenerated primitives, such as lines of length 0, are not
474 * managed properly.</listitem>
479 cpml_segment_offset(CpmlSegment
*segment
, double offset
)
481 CpmlPrimitive primitive
;
482 CpmlPrimitive last_primitive
;
483 cairo_path_data_t org
, old_end
;
484 cairo_bool_t first_cycle
;
486 cpml_primitive_from_segment(&primitive
, segment
);
492 primitive
.org
= &org
;
495 old_end
= *cpml_primitive_get_point(&primitive
, -1);
496 cpml_primitive_offset(&primitive
, offset
);
499 cpml_primitive_join(&last_primitive
, &primitive
);
500 primitive
.org
= cpml_primitive_get_point(&last_primitive
, -1);
503 cpml_primitive_copy(&last_primitive
, &primitive
);
505 } while (cpml_primitive_next(&primitive
));
510 * @segment: a #CpmlSegment
512 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
513 * the CpmlSegment structure accordling. One, and only one,
514 * %CAIRO_PATH_MOVE_TO primitive is left.
516 * Return value: 1 on success, 0 on no leading MOVE_TOs or on errors
519 normalize(CpmlSegment
*segment
)
521 if (!ensure_one_move_to(segment
))
529 * ensure_one_move_to:
530 * @segment: a #CpmlSegment
532 * Strips the leading %CAIRO_PATH_MOVE_TO primitives, updating
533 * the <structname>CpmlSegment</structname> structure accordling.
534 * One, and only one, %CAIRO_PATH_MOVE_TO primitive is left.
536 * Return value: 1 on success, 0 on no leading MOVE_TOs or on empty path
539 ensure_one_move_to(CpmlSegment
*segment
)
541 cairo_path_data_t
*new_data
;
542 int new_num_data
, length
;
544 new_data
= segment
->data
;
546 /* Check for at least one move to */
547 if (new_data
->header
.type
!= CAIRO_PATH_MOVE_TO
)
550 new_num_data
= segment
->num_data
;
553 /* Strip the leading CAIRO_PATH_MOVE_TO, leaving only the last one */
556 new_num_data
-= length
;
557 length
= new_data
->header
.length
;
559 /* Check for end of cairo path data */
560 if (length
>= new_num_data
)
562 } while (new_data
[length
].header
.type
== CAIRO_PATH_MOVE_TO
);
564 segment
->data
= new_data
;
565 segment
->num_data
= new_num_data
;
572 * @segment: a #CpmlSegment
574 * Looks for the segment termination and modify the
575 * <structfield>num_data</structfield> field of @segment accordling.
576 * @segment must have only one leading %CAIRO_PATH_MOVE_TO and
577 * it is supposed to be non-empty, conditions yet imposed by the
578 * ensure_one_move_to() function.
581 reshape(CpmlSegment
*segment
)
583 cairo_path_data_t
*data
;
584 int num_data
, new_num_data
, length
;
586 /* Skip the leading move to */
588 data
= segment
->data
+ new_num_data
;
590 /* Calculate the remaining data in the cairo path */
591 num_data
= segment
->path
->num_data
-
592 (segment
->data
- segment
->path
->data
);
594 while (new_num_data
< num_data
) {
595 /* A primitive is considered valid if it has implemented
596 * its own type_get_npoints() */
597 if (cpml_primitive_type_get_npoints(data
->header
.type
) < 0)
600 length
= data
->header
.length
;
602 new_num_data
+= length
;
605 segment
->num_data
= new_num_data
;