1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2007-2015 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-primitive
23 * @Section_Id:Primitive
24 * @title: CpmlPrimitive
25 * @short_description: Basic component of segments
27 * A primitive is an atomic geometric element found inside #CpmlSegment.
28 * The available primitives are the same defined by #cairo_path_data_type_t
29 * with the additional #CPML_ARC type (check #CpmlPrimitiveType
30 * for further information) and without #CPML_MOVE as it is not
31 * considered a primitive and it is managed in different way: the move-to
32 * primitives are only used to define the origin of a segment.
39 * @CPML_MOVE: equivalent to %CAIRO_PATH_MOVE_TO
40 * @CPML_LINE: equivalent to %CAIRO_PATH_LINE_TO
41 * @CPML_CURVE: equivalent to %CAIRO_PATH_CURVE_TO
42 * @CPML_CLOSE: equivalent to %CAIRO_PATH_CLOSE_PATH
43 * @CPML_ARC: an arc representation at CPML level
45 * This is a type compatible with #cairo_path_data_type_t type. It is
46 * basically the same enum but it embodies an important difference:
47 * it can be used to specify the special #CPML_ARC primitive. Having
48 * two different types is a good way to make clear when a function
49 * expect or not embedded #CPML_ARC primitives.
51 * Arcs are used extensively in technical drawing: some operations are
52 * trivials with arcs and a nightmare with cubic Bézier curves. Actually,
53 * at least up to version 1.10.2, the cairo library does not support arc
54 * primitives natively and there is no plan they will be ever supported.
56 * The CPML library supports arc natively, converting them to curves
57 * when the #CpmlSegment is returned to the cairo context, for instance
58 * when using cpml_segment_to_cairo().
65 * @segment: the source #CpmlSegment
66 * @org: a pointer to the first point of the primitive
67 * @data: the array of the path data, prepended by the header
69 * As for #CpmlSegment, also the primitive is unobtrusive. This
70 * means CpmlPrimitive does not include any coordinates but instead
71 * keeps pointers to the original segment (and, by transition, to
72 * the underlying #cairo_data_path_t struct).
78 #include "cpml-internal.h"
79 #include "cpml-extents.h"
80 #include "cpml-segment.h"
81 #include "cpml-primitive.h"
82 #include "cpml-primitive-private.h"
84 #include "cpml-curve.h"
89 static const _CpmlPrimitiveClass
*
90 _cpml_class_from_type (CpmlPrimitiveType type
);
91 static const _CpmlPrimitiveClass
*
92 _cpml_class_from_obj (const CpmlPrimitive
*primitive
);
93 static cairo_path_data_t
*
94 _cpml_get_point (const CpmlPrimitive
*primitive
,
96 static void _cpml_dump_point (const cairo_path_data_t
*path_data
);
100 * cpml_primitive_type_get_n_points:
101 * @type: a primitive type
103 * Gets the number of points required to identify the @type primitive.
105 * Returns: the number of points or 0 on errors
110 cpml_primitive_type_get_n_points(CpmlPrimitiveType type
)
112 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_type(type
);
114 if (class_data
== NULL
)
117 return class_data
->n_points
;
121 * cpml_primitive_from_segment:
122 * @primitive: (out): the destination #CpmlPrimitive struct
123 * @segment: (in): the source segment
125 * Initializes @primitive to the first primitive of @segment.
130 cpml_primitive_from_segment(CpmlPrimitive
*primitive
, CpmlSegment
*segment
)
132 primitive
->segment
= segment
;
134 /* The first element of a CpmlSegment is always a CPML_MOVE,
135 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
136 * so the origin is in the second data item */
137 primitive
->org
= segment
->data
+ 1;
139 /* Also, the segment APIs ensure that @segment is prepended by
140 * only one CPML_MOVE */
141 primitive
->data
= segment
->data
+ segment
->data
->header
.length
;
145 * cpml_primitive_copy:
146 * @primitive: (out): the destination #CpmlPrimitive
147 * @src: (in): the source #CpmlPrimitive
149 * Copies @src in @primitive. This is a shallow copy: the internal fields
150 * of @primitive refer to the same memory as the original @src primitive.
155 cpml_primitive_copy(CpmlPrimitive
*primitive
, const CpmlPrimitive
*src
)
157 memcpy(primitive
, src
, sizeof(CpmlPrimitive
));
161 * cpml_primitive_copy_data:
162 * @primitive: the destination #CpmlPrimitive
163 * @src: the source primitive to copy
165 * Copies the memory referenced by the <structfield>org</structfield>
166 * and <structfield>data</structfield> fields from @src to @primitive.
167 * For a shallow copy, check out cpml_primitive_copy().
169 * This could seem a somewhat unusual operation because @primitive
170 * should contain the same primitive as @src (i.e., the
171 * <structfield>data->header</structfield> field must be the same)
172 * but it can be convenient in some situation, such as when restoring
173 * the original data from a backup primitive, e.g.:
175 * <informalexample><programlisting language="C">
176 * CpmlPrimitive *backup;
178 * backup = cpml_primitive_deep_dup(&primitive);
179 * // Now &primitive points can be freely modified
181 * // Let's restore &primitive original points
182 * cpml_primitive_copy_data(&primitive, backup);
184 * </programlisting></informalexample>
186 * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors.
191 cpml_primitive_copy_data(CpmlPrimitive
*primitive
, const CpmlPrimitive
*src
)
193 if (primitive
->data
[0].header
.type
!= src
->data
[0].header
.type
||
194 primitive
->data
[0].header
.length
!= src
->data
[0].header
.length
)
197 memcpy(primitive
->org
, src
->org
, sizeof(cairo_path_data_t
));
198 memcpy(primitive
->data
, src
->data
,
199 sizeof(cairo_path_data_t
) * src
->data
[0].header
.length
);
204 * cpml_primitive_reset:
205 * @primitive: (inout): a #CpmlPrimitive
207 * Resets @primitive so it refers to the first primitive of the
213 cpml_primitive_reset(CpmlPrimitive
*primitive
)
215 cpml_primitive_from_segment(primitive
, primitive
->segment
);
219 * cpml_primitive_next:
220 * @primitive: (inout): a #CpmlPrimitive
222 * Changes @primitive so it refers to the next primitive on the
223 * source segment. If there are no more primitives, @primitive is
224 * not changed and 0 is returned.
226 * Returns: (type boolean): 1 on success, 0 if no next primitive found or errors.
231 cpml_primitive_next(CpmlPrimitive
*primitive
)
233 cairo_path_data_t
*new_data
;
234 const cairo_path_data_t
*end_data
;
236 new_data
= primitive
->data
+ primitive
->data
->header
.length
;
237 end_data
= primitive
->segment
->data
+ primitive
->segment
->num_data
;
239 if (new_data
>= end_data
)
242 primitive
->org
= _cpml_get_point(primitive
, -1);
243 primitive
->data
= new_data
;
249 * cpml_primitive_get_n_points:
250 * @primitive: a #CpmlPrimitive
252 * Gets the number of points required to identify @primitive.
253 * It is similar to cpml_primitive_type_get_n_points() but using
254 * a @primitive instance instead of a type.
256 * Returns: the number of points or 0 on errors.
258 * <!-- Virtual: n_points -->
263 cpml_primitive_get_n_points(const CpmlPrimitive
*primitive
)
265 return cpml_primitive_type_get_n_points(primitive
->data
->header
.type
);
269 * cpml_primitive_get_length:
270 * @primitive: a #CpmlPrimitive
272 * Abstracts the length() family functions by providing a common
273 * way to access the underlying primitive-specific implementation.
274 * The function returns the length of @primitive.
276 * Returns: the requested length or 0 on errors
278 * <!-- Virtual: get_length -->
283 cpml_primitive_get_length(const CpmlPrimitive
*primitive
)
285 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
287 if (class_data
== NULL
|| class_data
->get_length
== NULL
)
290 return class_data
->get_length(primitive
);
294 * cpml_primitive_put_extents:
295 * @primitive: (in): a #CpmlPrimitive
296 * @extents: (out): where to store the extents
298 * Abstracts the extents() family functions by providing a common
299 * way to access the underlying primitive-specific implementation.
301 * This function stores in @extents the bounding box of @primitive.
303 * On errors, that is if the extents cannot be calculated for some
304 * reason, this function does nothing.
306 * <!-- Virtual: put_extents -->
311 cpml_primitive_put_extents(const CpmlPrimitive
*primitive
,
312 CpmlExtents
*extents
)
314 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
316 if (class_data
== NULL
|| class_data
->put_extents
== NULL
)
319 class_data
->put_extents(primitive
, extents
);
323 * cpml_primitive_set_point:
324 * @primitive: a #CpmlPrimitive
325 * @n_point: the index of the point to retrieve
326 * @pair: the source #CpmlPair
328 * Sets the specified @n_point of @primitive to @pair. The @n_point
329 * index starts from 0: if @n_point is 0, the start point (the origin)
330 * is changed, 1 for the second point and so on. If @n_point is
331 * negative, it is considered as a negative index from the end, so
332 * that -1 is the end point, -2 the point before the end point and
335 * #CPML_CLOSE is managed in a special way: if @n_point
336 * is -1 or 1 and @primitive is a close-path, this function cycles
337 * the source #CpmlSegment and returns the first point. This is
338 * needed because requesting the end point (or the second point)
339 * of a close path is a valid operation and must returns the origin
342 * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise.
347 cpml_primitive_set_point(CpmlPrimitive
*primitive
,
348 int n_point
, const CpmlPair
*pair
)
350 cairo_path_data_t
*point
= _cpml_get_point(primitive
, n_point
);
355 cpml_pair_to_cairo(pair
, point
);
360 * cpml_primitive_put_point:
361 * @primitive: a #CpmlPrimitive
362 * @n_point: the index of the point to retrieve
363 * @pair: (out caller-allocates): the destination #CpmlPair
365 * Gets the specified @n_point from @primitive and stores it into
366 * @pair. The @n_point index is subject to the same rules explained
367 * in the cpml_primitive_set_point() function.
369 * Returns: (type gboolean): 1 if the point is found, 0 otherwise.
374 cpml_primitive_put_point(const CpmlPrimitive
*primitive
,
375 int n_point
, CpmlPair
*pair
)
377 const cairo_path_data_t
*point
= _cpml_get_point(primitive
, n_point
);
382 cpml_pair_from_cairo(pair
, point
);
387 * cpml_primitive_put_pair_at:
388 * @primitive: (in): a #CpmlPrimitive
389 * @pos: (in): the position value
390 * @pair: (out): the destination #CpmlPair
392 * Abstracts the <function>put_pair_at</function> family functions by
393 * providing a common way to access the underlying primitive-specific
396 * It gets the coordinates of the point lying on @primitive
397 * at position @pos. @pos is an homogeneous factor where 0 is the
398 * start point, 1 the end point, 0.5 the mid point and so on.
399 * @pos can be less than 0 or greater than 1, in which case the
400 * coordinates of @pair are interpolated.
402 * On errors, that is if the coordinates cannot be calculated for
403 * some reason, this function does nothing.
405 * <!-- Virtual: put_pair_at -->
410 cpml_primitive_put_pair_at(const CpmlPrimitive
*primitive
,
411 double pos
, CpmlPair
*pair
)
413 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
415 if (class_data
== NULL
|| class_data
->put_pair_at
== NULL
)
418 class_data
->put_pair_at(primitive
, pos
, pair
);
422 * cpml_primitive_put_vector_at:
423 * @primitive: (in): a #CpmlPrimitive
424 * @pos: (in): the position value
425 * @vector: (out): the destination #CpmlVector
427 * Abstracts the <function>put_vector_at</function> family functions by
428 * providing a common way to access the underlying primitive-specific
431 * It gets the steepness of the point at position @pos on @primitive.
432 * @pos is an homogeneous factor where 0 is the start point, 1 the
433 * end point, 0.5 the mid point and so on.
434 * @pos can be less than 0 or greater than 1, in which case the
435 * coordinates of @pair are interpolated.
437 * On errors, that is if the steepness cannot be calculated for
438 * some reason, this function does nothing.
440 * <!-- Virtual: put_vector_at -->
445 cpml_primitive_put_vector_at(const CpmlPrimitive
*primitive
,
446 double pos
, CpmlVector
*vector
)
448 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
450 if (class_data
== NULL
|| class_data
->put_vector_at
== NULL
)
453 class_data
->put_vector_at(primitive
, pos
, vector
);
457 * cpml_primitive_is_inside:
458 * @primitive: a #CpmlPrimitive
459 * @pair: the coordinates of the subject point
461 * Checks if @pair is inside the bounding box of @primitive. This
462 * can be useful e.g. to verify if an intersection point is a real
463 * intersection or an hypothetical one.
465 * Returns: 1 if @pair is inside the bounding box of @primitive, 0 otherwise.
470 cpml_primitive_is_inside(const CpmlPrimitive
*primitive
, const CpmlPair
*pair
)
472 CpmlExtents extents
= { 0 };
473 cpml_primitive_put_extents(primitive
, &extents
);
474 return cpml_extents_pair_is_inside(&extents
, pair
);
478 * cpml_primitive_get_closest_pos:
479 * @primitive: a #CpmlPrimitive
480 * @pair: the coordinates of the subject point
482 * Returns the pos value of the point on @primitive nearest to @pair.
483 * The returned value is always clamped between 0 and 1.
485 * Returns: the requested pos value between 0 and 1, or -1 on errors.
487 * <!-- Virtual: get_closest_pos -->
492 cpml_primitive_get_closest_pos(const CpmlPrimitive
*primitive
,
493 const CpmlPair
*pair
)
495 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
497 if (class_data
== NULL
|| class_data
->get_closest_pos
== NULL
)
500 return class_data
->get_closest_pos(primitive
, pair
);
504 * cpml_primitive_put_intersections:
505 * @primitive: the first #CpmlPrimitive
506 * @primitive2: the second #CpmlPrimitive
507 * @n_dest: maximum number of intersections to return
508 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
510 * Finds the intersection points between the given primitives and
511 * returns the result in @dest. The size of @dest should be enough
512 * to store @n_dest #CpmlPair. The maximum number of intersections
513 * is dependent on the type of the primitive involved in the
514 * operation. If there are at least one Bézier curve involved, up to
515 * 4 intersections could be returned. Otherwise, if there is an arc
516 * the intersections will be 2 at maximum. For line primitives, there
517 * is only 1 point (or 0 if the lines are parallel).
519 * Also hypothetical intersections are returned, that is intersections
520 * made by extending the primitives outside their bounds. This means e.g.
521 * two lines always return one intersection if they are not parallel. To
522 * discriminate real intersections you should check the returned points
523 * with cpml_primitive_is_inside(), for example:
525 * <informalexample><programlisting language="C">
526 * if (cpml_primitive_put_intersections(line1, line2, 1, &pair) == 0) {
527 * // line1 and line2 are parallels
528 * } else if (cpml_primitive_is_inside(line1, &pair) &&
529 * cpml_primitive_is_inside(line2, &pair)) {
530 * // This is a real intersection
532 * // This is an hypothetical intersection
534 * </programlisting></informalexample>
538 * The convention used by the CPML library is that a primitive should
539 * implement only the intersection algorithms with lower degree
540 * primitives. This is required to avoid code duplication: intersection
541 * between arc and Bézier curves must be implemented by #CPML_CURVE and
542 * intersection between lines and arcs must be implemented by #CPML_ARC.
543 * cpml_primitive_put_intersections() will take care of swapping the
544 * arguments if they are not properly ordered.
548 * Returns: the number of intersection points found or 0 if the
549 * primitives do not intersect or on errors
551 * <!-- Virtual: put_intersections -->
556 cpml_primitive_put_intersections(const CpmlPrimitive
*primitive
,
557 const CpmlPrimitive
*primitive2
,
558 size_t n_dest
, CpmlPair
*dest
)
560 const _CpmlPrimitiveClass
*class_data
;
561 size_t n_points
, n_points2
;
566 n_points
= cpml_primitive_get_n_points(primitive
);
567 n_points2
= cpml_primitive_get_n_points(primitive2
);
569 /* Check if the primitives can intersect */
570 if (n_points
== 0 || n_points2
== 0)
573 /* Primitives reordering: the first must be the more complex one */
574 if (n_points
< n_points2
) {
575 return cpml_primitive_put_intersections(primitive2
, primitive
,
579 /* Check if put_intersections is implemented */
580 class_data
= _cpml_class_from_obj(primitive
);
581 if (class_data
== NULL
|| class_data
->put_intersections
== NULL
)
584 return class_data
->put_intersections(primitive
, primitive2
, n_dest
, dest
);
588 * cpml_primitive_put_intersections_with_segment:
589 * @primitive: a #CpmlPrimitive
590 * @segment: a #CpmlSegment
591 * @n_dest: maximum number of intersections to return
592 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
594 * Computes the intersections between @segment and @primitive by
595 * sequentially scanning the primitives in @segment and looking
596 * for their intersections with @primitive.
598 * If the intersections are more than @n_dest, only the first
599 * @n_dest pairs are stored.
601 * Returns: the number of intersections found
606 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive
*primitive
,
607 const CpmlSegment
*segment
,
608 size_t n_dest
, CpmlPair
*dest
)
610 CpmlPrimitive portion
;
613 cpml_primitive_from_segment(&portion
, (CpmlSegment
*) segment
);
616 while (found
< n_dest
) {
617 found
+= cpml_primitive_put_intersections(&portion
, primitive
,
618 n_dest
-found
, dest
+found
);
619 if (!cpml_primitive_next(&portion
))
627 * cpml_primitive_offset:
628 * @primitive: (inout): a #CpmlPrimitive
629 * @offset: (in): distance for the computed offset primitive
631 * Given a primitive, computes the same (or approximated) parallel
632 * primitive distant @offset from the original one and returns
633 * the result by changing @primitive.
635 * On errors, that is if the offset primitive cannot be calculated
636 * for some reason, this function does nothing.
638 * <!-- Virtual: offset -->
643 cpml_primitive_offset(CpmlPrimitive
*primitive
, double offset
)
645 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
647 if (class_data
== NULL
|| class_data
->offset
== NULL
)
650 class_data
->offset(primitive
, offset
);
654 * cpml_primitive_join:
655 * @primitive: (inout): the first #CpmlPrimitive
656 * @primitive2: (inout): the second #CpmlPrimitive
658 * Joins two primitive modifying the end point of @primitive and the
659 * start point of @primitive2 so that the resulting points will overlap.
662 * <title>TODO</title>
664 * <listitem>Actually, the join is done by extending the end vector
665 * of @primitive and the start vector of @primitive2 and
666 * interpolating the intersection: this means no primitive
667 * dependent code is needed. Anyway, it is likely to change
668 * in the future because this approach is quite naive when
669 * curves are involved.</listitem>
673 * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined.
675 * <!-- Virtual: join -->
680 cpml_primitive_join(CpmlPrimitive
*primitive
, CpmlPrimitive
*primitive2
)
682 cairo_path_data_t
*end1
, *start2
;
683 CpmlPrimitive line1
, line2
;
684 cairo_path_data_t data1
[2], data2
[2];
687 end1
= _cpml_get_point(primitive
, -1);
688 start2
= _cpml_get_point(primitive2
, 0);
690 /* Check if the primitives are yet connected */
691 if (end1
->point
.x
== start2
->point
.x
&& end1
->point
.y
== start2
->point
.y
)
694 line1
.org
= _cpml_get_point(primitive
, -2);
696 data1
[0].header
.type
= CPML_LINE
;
701 data2
[0].header
.type
= CPML_LINE
;
702 data2
[1] = *_cpml_get_point(primitive2
, 1);
704 if (!cpml_primitive_put_intersections(&line1
, &line2
, 1, &joint
))
707 cpml_pair_to_cairo(&joint
, end1
);
708 cpml_pair_to_cairo(&joint
, start2
);
714 * cpml_primitive_to_cairo:
715 * @primitive: (in): a #CpmlPrimitive
716 * @cr: (inout): the destination cairo context
718 * Renders a single @primitive to the @cr cairo context.
719 * As a special case, if the primitive is a #CPML_CLOSE, an
720 * equivalent line is rendered, because a close path left alone
723 * Also a #CPML_ARC primitive is treated specially, as it is not
724 * natively supported by cairo and has its own rendering API.
729 cpml_primitive_to_cairo(const CpmlPrimitive
*primitive
, cairo_t
*cr
)
731 CpmlPrimitiveType type
;
733 cairo_path_data_t
*path_data
;
735 cairo_move_to(cr
, primitive
->org
->point
.x
, primitive
->org
->point
.y
);
737 type
= primitive
->data
->header
.type
;
739 if (type
== CPML_CLOSE
) {
740 path_data
= _cpml_get_point(primitive
, -1);
741 cairo_line_to(cr
, path_data
->point
.x
, path_data
->point
.y
);
742 } else if (type
== CPML_ARC
) {
743 cpml_arc_to_cairo(primitive
, cr
);
745 path
.status
= CAIRO_STATUS_SUCCESS
;
746 path
.data
= primitive
->data
;
747 path
.num_data
= primitive
->data
->header
.length
;
748 cairo_append_path(cr
, &path
);
753 * cpml_primitive_dump:
754 * @primitive: a #CpmlPrimitive
755 * @org_also: whether to output also the origin coordinates
757 * Dumps info on the specified @primitive to stdout: useful for
758 * debugging purposes. If @org_also is 1, a #CPML_MOVE to the
759 * origin is prepended to the data otherwise the
760 * <structfield>org</structfield> field is not used.
765 cpml_primitive_dump(const CpmlPrimitive
*primitive
, int org_also
)
767 const cairo_path_data_t
*data
;
769 const _CpmlPrimitiveClass
*class_data
;
772 data
= primitive
->data
;
773 type
= data
->header
.type
;
774 class_data
= _cpml_class_from_type(type
);
776 if (class_data
== NULL
) {
777 printf("Unknown primitive type (%d)\n", type
);
781 /* Dump the origin, if requested */
784 _cpml_dump_point(primitive
->org
);
788 printf("%s ", class_data
->name
);
790 n_points
= cpml_primitive_get_n_points(primitive
);
791 for (n
= 1; n
< n_points
; ++n
)
792 _cpml_dump_point(_cpml_get_point(primitive
, n
));
798 static const _CpmlPrimitiveClass
*
799 _cpml_class_from_type(CpmlPrimitiveType type
)
801 if (type
== CPML_LINE
)
802 return _cpml_line_get_class();
803 else if (type
== CPML_ARC
)
804 return _cpml_arc_get_class();
805 else if (type
== CPML_CURVE
)
806 return _cpml_curve_get_class();
807 else if (type
== CPML_CLOSE
)
808 return _cpml_close_get_class();
813 static const _CpmlPrimitiveClass
*
814 _cpml_class_from_obj(const CpmlPrimitive
*primitive
)
816 return _cpml_class_from_type(primitive
->data
->header
.type
);
821 * @primitive: a #CpmlPrimitive
822 * @n_point: the index of the point to retrieve
824 * Gets the specified @n_point from @primitive. The index starts
825 * at 0: if @n_point is 0, the start point (the origin) is
826 * returned, 1 for the second point and so on. If @n_point is
827 * negative, it is considered as a negative index from the end,
828 * so that -1 is the end point, -2 the point before the end point
831 * #CPML_CLOSE is managed in a special way: if @n_point
832 * is -1 or 1 and @primitive is a close-path, this function cycles
833 * the source #CpmlSegment and returns the first point. This is
834 * needed because requesting the end point (or the second point)
835 * of a close path is a valid operation and must returns the start
838 * Returns: a pointer to the requested point (in cairo format)
839 * or <constant>NULL</constant> if the point is outside
844 static cairo_path_data_t
*
845 _cpml_get_point(const CpmlPrimitive
*primitive
, int n_point
)
849 /* For a start point request, simply return the origin
850 * without further checking */
852 return primitive
->org
;
854 /* The CPML_CLOSE special case */
855 if (primitive
->data
->header
.type
== CPML_CLOSE
&&
856 (n_point
== 1 || n_point
== -1))
857 return &primitive
->segment
->data
[1];
859 n_points
= cpml_primitive_get_n_points(primitive
);
863 /* If n_point is negative, consider it as a negative index from the end */
865 n_point
= n_points
+ n_point
;
867 /* Out of range condition */
868 if (n_point
< 0 || n_point
>= n_points
)
871 return n_point
== 0 ? primitive
->org
: &primitive
->data
[n_point
];
875 _cpml_dump_point(const cairo_path_data_t
*path_data
)
877 printf("(%g %g) ", path_data
->point
.x
, path_data
->point
.y
);