1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2007-2021 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.
40 * This is a type compatible with #cairo_path_data_type_t type. It is
41 * basically the same enum but it embodies an important difference:
42 * it can be used to specify the special %CPML_ARC primitive. Having
43 * two different types is a good way to make clear when a function
44 * expect or not embedded %CPML_ARC primitives.
46 * Arcs are used extensively in technical drawing: some operations are
47 * trivials with arcs and a nightmare with cubic Bézier curves. Actually,
48 * at least up to version 1.10.2, the cairo library does not support arc
49 * primitives natively and there is no plan they will be ever supported.
51 * The CPML library supports arc natively, converting them to curves
52 * when the #CpmlSegment is returned to the cairo context, for instance
53 * when using cpml_segment_to_cairo().
61 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_MOVE_TO.
69 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_LINE_TO.
77 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CURVE_TO.
85 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CLOSE_PATH.
93 * A #CpmlPrimitiveType value that represents an arc representation at
101 * @segment: the source #CpmlSegment
102 * @org: a pointer to the first point of the primitive
103 * @data: the array of the path data, prepended by the header
105 * As for #CpmlSegment, also the primitive is unobtrusive. This
106 * means CpmlPrimitive does not include any coordinates but instead
107 * keeps pointers to the original segment (and, by transition, to
108 * the underlying #cairo_data_path_t struct).
114 #include "cpml-internal.h"
115 #include "cpml-extents.h"
116 #include "cpml-segment.h"
117 #include "cpml-primitive.h"
118 #include "cpml-primitive-private.h"
119 #include "cpml-arc.h"
120 #include "cpml-curve.h"
124 /* This is the best way I found to get the maximum value
125 * of a bunch of constant by using only the preprocessor */
126 #define CPML_MAX (CPML_LINE | CPML_ARC | CPML_CURVE | CPML_CLOSE)
129 static const _CpmlPrimitiveClass
*
130 _cpml_class_from_type (CpmlPrimitiveType type
);
131 static const _CpmlPrimitiveClass
*
132 _cpml_class_from_obj (const CpmlPrimitive
*primitive
);
133 static cairo_path_data_t
*
134 _cpml_get_point (const CpmlPrimitive
*primitive
,
136 static void _cpml_dump_point (const cairo_path_data_t
*path_data
);
140 * cpml_primitive_type_get_n_points:
141 * @type: a primitive type
143 * Gets the number of points required to identify the @type primitive.
145 * Returns: the number of points or 0 on errors
150 cpml_primitive_type_get_n_points(CpmlPrimitiveType type
)
152 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_type(type
);
154 if (class_data
== NULL
)
157 return class_data
->n_points
;
161 * cpml_primitive_type:
162 * @primitive: a #CpmlPrimitive struct
164 * Returns the type of @primitive, i.e. %CPML_LINE for lines,
165 * %CPML_ARC for arcs or circles, %CPML_CURVE for cubic Bézier
166 * curves and %CPML_CLOSE for closing lines. The %CPML_MOVE is
167 * used extensively *between* segments but it is not considered
168 * by itself a primitive, so it should never been returned.
170 * Returns: the type of the primitive.
175 cpml_primitive_type(const CpmlPrimitive
*primitive
)
177 return primitive
->data
[0].header
.type
;
181 * cpml_primitive_from_segment:
182 * @primitive: (out): the destination #CpmlPrimitive struct
183 * @segment: (in): the source segment
185 * Initializes @primitive to the first primitive of @segment.
190 cpml_primitive_from_segment(CpmlPrimitive
*primitive
, CpmlSegment
*segment
)
192 primitive
->segment
= segment
;
194 /* The first element of a CpmlSegment is always a CPML_MOVE,
195 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
196 * so the origin is in the second data item */
197 primitive
->org
= segment
->data
+ 1;
199 /* Also, the segment APIs ensure that @segment is prepended by
200 * only one CPML_MOVE */
201 primitive
->data
= segment
->data
+ segment
->data
->header
.length
;
205 * cpml_primitive_copy:
206 * @primitive: (out): the destination #CpmlPrimitive
207 * @src: (in): the source #CpmlPrimitive
209 * Copies @src in @primitive. This is a shallow copy: the internal fields
210 * of @primitive refer to the same memory as the original @src primitive.
215 cpml_primitive_copy(CpmlPrimitive
*primitive
, const CpmlPrimitive
*src
)
217 memcpy(primitive
, src
, sizeof(CpmlPrimitive
));
221 * cpml_primitive_copy_data:
222 * @primitive: the destination #CpmlPrimitive
223 * @src: the source primitive to copy
225 * Copies the memory referenced by the <structfield>org</structfield>
226 * and <structfield>data</structfield> fields from @src to @primitive.
227 * For a shallow copy, check out cpml_primitive_copy().
229 * This could seem a somewhat unusual operation because @primitive
230 * should contain the same primitive as @src (i.e., the
231 * <structfield>data->header</structfield> field must be the same)
232 * but it can be convenient in some situation, such as when restoring
233 * the original data from a backup primitive, e.g.:
235 * <informalexample><programlisting language="C">
236 * CpmlPrimitive *backup;
238 * backup = cpml_primitive_deep_dup(&primitive);
239 * // Now &primitive points can be freely modified
241 * // Let's restore &primitive original points
242 * cpml_primitive_copy_data(&primitive, backup);
244 * </programlisting></informalexample>
246 * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors.
251 cpml_primitive_copy_data(CpmlPrimitive
*primitive
, const CpmlPrimitive
*src
)
253 if (primitive
->data
[0].header
.type
!= src
->data
[0].header
.type
||
254 primitive
->data
[0].header
.length
!= src
->data
[0].header
.length
)
257 memcpy(primitive
->org
, src
->org
, sizeof(cairo_path_data_t
));
258 memcpy(primitive
->data
, src
->data
,
259 sizeof(cairo_path_data_t
) * src
->data
[0].header
.length
);
264 * cpml_primitive_reset:
265 * @primitive: (inout): a #CpmlPrimitive
267 * Resets @primitive so it refers to the first primitive of the
273 cpml_primitive_reset(CpmlPrimitive
*primitive
)
275 cpml_primitive_from_segment(primitive
, primitive
->segment
);
279 * cpml_primitive_next:
280 * @primitive: (inout): a #CpmlPrimitive
282 * Changes @primitive so it refers to the next primitive on the
283 * source segment. If there are no more primitives, @primitive is
284 * not changed and 0 is returned.
286 * Returns: (type boolean): 1 on success, 0 if no next primitive found or errors.
291 cpml_primitive_next(CpmlPrimitive
*primitive
)
293 cairo_path_data_t
*new_data
;
294 const cairo_path_data_t
*end_data
;
296 new_data
= primitive
->data
+ primitive
->data
->header
.length
;
297 end_data
= primitive
->segment
->data
+ primitive
->segment
->num_data
;
299 if (new_data
>= end_data
)
302 primitive
->org
= _cpml_get_point(primitive
, -1);
303 primitive
->data
= new_data
;
309 * cpml_primitive_get_n_points:
310 * @primitive: a #CpmlPrimitive
312 * Gets the number of points required to identify @primitive.
313 * It is similar to cpml_primitive_type_get_n_points() but using
314 * a @primitive instance instead of a type.
316 * Returns: the number of points or 0 on errors.
318 * <!-- Virtual: n_points -->
323 cpml_primitive_get_n_points(const CpmlPrimitive
*primitive
)
325 return cpml_primitive_type_get_n_points(primitive
->data
->header
.type
);
329 * cpml_primitive_get_length:
330 * @primitive: a #CpmlPrimitive
332 * Abstracts the length() family functions by providing a common
333 * way to access the underlying primitive-specific implementation.
334 * The function returns the length of @primitive.
336 * Returns: the requested length or 0 on errors
338 * <!-- Virtual: get_length -->
343 cpml_primitive_get_length(const CpmlPrimitive
*primitive
)
345 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
347 if (class_data
== NULL
|| class_data
->get_length
== NULL
)
350 return class_data
->get_length(primitive
);
354 * cpml_primitive_put_extents:
355 * @primitive: (in): a #CpmlPrimitive
356 * @extents: (out): where to store the extents
358 * Abstracts the extents() family functions by providing a common
359 * way to access the underlying primitive-specific implementation.
361 * This function stores in @extents the bounding box of @primitive.
363 * On errors, that is if the extents cannot be calculated for some
364 * reason, this function does nothing.
366 * <!-- Virtual: put_extents -->
371 cpml_primitive_put_extents(const CpmlPrimitive
*primitive
,
372 CpmlExtents
*extents
)
374 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
376 if (class_data
== NULL
|| class_data
->put_extents
== NULL
)
379 class_data
->put_extents(primitive
, extents
);
383 * cpml_primitive_set_point:
384 * @primitive: a #CpmlPrimitive
385 * @n_point: the index of the point to retrieve
386 * @pair: the source #CpmlPair
388 * Sets the specified @n_point of @primitive to @pair. The @n_point
389 * index starts from 0: if @n_point is 0, the start point (the origin)
390 * is changed, 1 for the second point and so on. If @n_point is
391 * negative, it is considered as a negative index from the end, so
392 * that -1 is the end point, -2 the point before the end point and
395 * %CPML_CLOSE is managed in a special way: if @n_point
396 * is -1 or 1 and @primitive is a close-path, this function cycles
397 * the source #CpmlSegment and returns the first point. This is
398 * needed because requesting the end point (or the second point)
399 * of a close path is a valid operation and must returns the origin
402 * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise.
407 cpml_primitive_set_point(CpmlPrimitive
*primitive
,
408 int n_point
, const CpmlPair
*pair
)
410 cairo_path_data_t
*point
= _cpml_get_point(primitive
, n_point
);
415 cpml_pair_to_cairo(pair
, point
);
420 * cpml_primitive_put_point:
421 * @primitive: a #CpmlPrimitive
422 * @n_point: the index of the point to retrieve
423 * @pair: (out caller-allocates): the destination #CpmlPair
425 * Gets the specified @n_point from @primitive and stores it into
426 * @pair. The @n_point index is subject to the same rules explained
427 * in the cpml_primitive_set_point() function.
429 * Returns: (type gboolean): 1 if the point is found, 0 otherwise.
434 cpml_primitive_put_point(const CpmlPrimitive
*primitive
,
435 int n_point
, CpmlPair
*pair
)
437 const cairo_path_data_t
*point
= _cpml_get_point(primitive
, n_point
);
442 cpml_pair_from_cairo(pair
, point
);
447 * cpml_primitive_put_pair_at:
448 * @primitive: (in): a #CpmlPrimitive
449 * @pos: (in): the position value
450 * @pair: (out): the destination #CpmlPair
452 * Abstracts the <function>put_pair_at</function> family functions by
453 * providing a common way to access the underlying primitive-specific
456 * It gets the coordinates of the point lying on @primitive
457 * at position @pos. @pos is an homogeneous factor where 0 is the
458 * start point, 1 the end point, 0.5 the mid point and so on.
459 * @pos can be less than 0 or greater than 1, in which case the
460 * coordinates of @pair are interpolated.
462 * On errors, that is if the coordinates cannot be calculated for
463 * some reason, this function does nothing.
465 * <!-- Virtual: put_pair_at -->
470 cpml_primitive_put_pair_at(const CpmlPrimitive
*primitive
,
471 double pos
, CpmlPair
*pair
)
473 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
475 if (class_data
== NULL
|| class_data
->put_pair_at
== NULL
)
478 class_data
->put_pair_at(primitive
, pos
, pair
);
482 * cpml_primitive_put_vector_at:
483 * @primitive: (in): a #CpmlPrimitive
484 * @pos: (in): the position value
485 * @vector: (out): the destination #CpmlVector
487 * Abstracts the <function>put_vector_at</function> family functions by
488 * providing a common way to access the underlying primitive-specific
491 * It gets the steepness of the point at position @pos on @primitive.
492 * @pos is an homogeneous factor where 0 is the start point, 1 the
493 * end point, 0.5 the mid point and so on.
494 * @pos can be less than 0 or greater than 1, in which case the
495 * coordinates of @pair are interpolated.
497 * On errors, that is if the steepness cannot be calculated for
498 * some reason, this function does nothing.
500 * <!-- Virtual: put_vector_at -->
505 cpml_primitive_put_vector_at(const CpmlPrimitive
*primitive
,
506 double pos
, CpmlVector
*vector
)
508 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
510 if (class_data
== NULL
|| class_data
->put_vector_at
== NULL
)
513 class_data
->put_vector_at(primitive
, pos
, vector
);
517 * cpml_primitive_is_inside:
518 * @primitive: a #CpmlPrimitive
519 * @pair: the coordinates of the subject point
521 * Checks if @pair is inside the bounding box of @primitive. This
522 * can be useful e.g. to verify if an intersection point is a real
523 * intersection or an hypothetical one.
525 * Returns: 1 if @pair is inside the bounding box of @primitive, 0 otherwise.
530 cpml_primitive_is_inside(const CpmlPrimitive
*primitive
, const CpmlPair
*pair
)
532 CpmlExtents extents
= { 0 };
533 cpml_primitive_put_extents(primitive
, &extents
);
534 return cpml_extents_pair_is_inside(&extents
, pair
);
538 * cpml_primitive_get_closest_pos:
539 * @primitive: a #CpmlPrimitive
540 * @pair: the coordinates of the subject point
542 * Returns the pos value of the point on @primitive nearest to @pair.
543 * The returned value is always clamped between 0 and 1.
545 * Returns: the requested pos value between 0 and 1, or -1 on errors.
547 * <!-- Virtual: get_closest_pos -->
552 cpml_primitive_get_closest_pos(const CpmlPrimitive
*primitive
,
553 const CpmlPair
*pair
)
555 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
557 if (class_data
== NULL
|| class_data
->get_closest_pos
== NULL
)
560 return class_data
->get_closest_pos(primitive
, pair
);
564 * cpml_primitive_put_intersections:
565 * @primitive: the first #CpmlPrimitive
566 * @primitive2: the second #CpmlPrimitive
567 * @n_dest: maximum number of intersections to return
568 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
570 * Finds the intersection points between the given primitives and
571 * returns the result in @dest. The size of @dest should be enough
572 * to store @n_dest #CpmlPair. The maximum number of intersections
573 * is dependent on the type of the primitive involved in the
574 * operation. If there are at least one Bézier curve involved, up to
575 * 4 intersections could be returned. Otherwise, if there is an arc
576 * the intersections will be 2 at maximum. For line primitives, there
577 * is only 1 point (or 0 if the lines are parallel).
579 * Also hypothetical intersections are returned, that is intersections
580 * made by extending the primitives outside their bounds. This means e.g.
581 * two lines always return one intersection if they are not parallel. To
582 * discriminate real intersections you should check the returned points
583 * with cpml_primitive_is_inside(), for example:
585 * <informalexample><programlisting language="C">
586 * if (cpml_primitive_put_intersections(line1, line2, 1, &pair) == 0) {
587 * // line1 and line2 are parallels
588 * } else if (cpml_primitive_is_inside(line1, &pair) &&
589 * cpml_primitive_is_inside(line2, &pair)) {
590 * // This is a real intersection
592 * // This is an hypothetical intersection
594 * </programlisting></informalexample>
598 * The convention used by the CPML library is that a primitive should
599 * implement only the intersection algorithms with lower degree
600 * primitives. This is required to avoid code duplication: intersection
601 * between arc and Bézier curves must be implemented by %CPML_CURVE and
602 * intersection between lines and arcs must be implemented by %CPML_ARC.
603 * cpml_primitive_put_intersections() will take care of swapping the
604 * arguments if they are not properly ordered.
608 * Returns: the number of intersection points found or 0 if the
609 * primitives do not intersect or on errors
611 * <!-- Virtual: put_intersections -->
616 cpml_primitive_put_intersections(const CpmlPrimitive
*primitive
,
617 const CpmlPrimitive
*primitive2
,
618 size_t n_dest
, CpmlPair
*dest
)
620 const _CpmlPrimitiveClass
*class_data
;
621 size_t n_points
, n_points2
;
626 n_points
= cpml_primitive_get_n_points(primitive
);
627 n_points2
= cpml_primitive_get_n_points(primitive2
);
629 /* Check if the primitives can intersect */
630 if (n_points
== 0 || n_points2
== 0)
633 /* Primitives reordering: the first must be the more complex one */
634 if (n_points
< n_points2
) {
635 return cpml_primitive_put_intersections(primitive2
, primitive
,
639 /* Check if put_intersections is implemented */
640 class_data
= _cpml_class_from_obj(primitive
);
641 if (class_data
== NULL
|| class_data
->put_intersections
== NULL
)
644 return class_data
->put_intersections(primitive
, primitive2
, n_dest
, dest
);
648 * cpml_primitive_put_intersections_with_segment:
649 * @primitive: a #CpmlPrimitive
650 * @segment: a #CpmlSegment
651 * @n_dest: maximum number of intersections to return
652 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
654 * Computes the intersections between @segment and @primitive by
655 * sequentially scanning the primitives in @segment and looking
656 * for their intersections with @primitive.
658 * If the intersections are more than @n_dest, only the first
659 * @n_dest pairs are stored.
661 * Beware: this function returns only real intersections. This is in contrast
662 * to cpml_primitive_put_intersections() that, instead, returns all
663 * intersections. Check that function documentation to know what virtual and
664 * real intersections are.
666 * Returns: the number of real intersections found.
671 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive
*primitive
,
672 const CpmlSegment
*segment
,
673 size_t n_dest
, CpmlPair
*dest
)
675 CpmlPrimitive portion
;
677 const CpmlPair
*pair
;
680 cpml_primitive_from_segment(&portion
, (CpmlSegment
*) segment
);
683 while (total
< n_dest
) {
684 found
= cpml_primitive_put_intersections(&portion
, primitive
, 5, partial
);
686 /* Store only real intersections */
687 for (pair
= partial
; found
; -- found
, ++ pair
) {
688 if (cpml_primitive_is_inside(&portion
, pair
) &&
689 cpml_primitive_is_inside(primitive
, pair
)) {
690 cpml_pair_copy(dest
+total
, pair
);
695 if (!cpml_primitive_next(&portion
))
703 * cpml_primitive_offset:
704 * @primitive: (inout): a #CpmlPrimitive
705 * @offset: (in): distance for the computed offset primitive
707 * Given a primitive, computes the same (or approximated) parallel
708 * primitive distant @offset from the original one and returns
709 * the result by changing @primitive.
711 * On errors, that is if the offset primitive cannot be calculated
712 * for some reason, this function does nothing.
714 * <!-- Virtual: offset -->
719 cpml_primitive_offset(CpmlPrimitive
*primitive
, double offset
)
721 const _CpmlPrimitiveClass
*class_data
= _cpml_class_from_obj(primitive
);
723 if (class_data
== NULL
|| class_data
->offset
== NULL
)
726 class_data
->offset(primitive
, offset
);
730 * cpml_primitive_join:
731 * @primitive: (inout): the first #CpmlPrimitive
732 * @primitive2: (inout): the second #CpmlPrimitive
734 * Joins two primitive modifying the end point of @primitive and the
735 * start point of @primitive2 so that the resulting points will overlap.
738 * <title>TODO</title>
740 * <listitem>Actually, the join is done by extending the end vector
741 * of @primitive and the start vector of @primitive2 and
742 * interpolating the intersection: this means no primitive
743 * dependent code is needed. Anyway, it is likely to change
744 * in the future because this approach is quite naive when
745 * curves are involved.</listitem>
749 * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined.
751 * <!-- Virtual: join -->
756 cpml_primitive_join(CpmlPrimitive
*primitive
, CpmlPrimitive
*primitive2
)
758 cairo_path_data_t
*end1
, *start2
;
759 CpmlPrimitive line1
, line2
;
760 cairo_path_data_t data1
[2], data2
[2];
763 end1
= _cpml_get_point(primitive
, -1);
764 start2
= _cpml_get_point(primitive2
, 0);
766 /* Check if the primitives are yet connected */
767 if (end1
->point
.x
== start2
->point
.x
&& end1
->point
.y
== start2
->point
.y
)
770 line1
.org
= _cpml_get_point(primitive
, -2);
772 data1
[0].header
.type
= CPML_LINE
;
777 data2
[0].header
.type
= CPML_LINE
;
778 data2
[1] = *_cpml_get_point(primitive2
, 1);
780 if (!cpml_primitive_put_intersections(&line1
, &line2
, 1, &joint
))
783 cpml_pair_to_cairo(&joint
, end1
);
784 cpml_pair_to_cairo(&joint
, start2
);
790 * cpml_primitive_to_cairo:
791 * @primitive: (in): a #CpmlPrimitive
792 * @cr: (inout): the destination cairo context
794 * Renders a single @primitive to the @cr cairo context.
795 * As a special case, if the primitive is a %CPML_CLOSE, an
796 * equivalent line is rendered, because a close path left alone
799 * Also a %CPML_ARC primitive is treated specially, as it is not
800 * natively supported by cairo and has its own rendering API.
805 cpml_primitive_to_cairo(const CpmlPrimitive
*primitive
, cairo_t
*cr
)
807 CpmlPrimitiveType type
;
809 cairo_path_data_t
*path_data
;
811 cairo_move_to(cr
, primitive
->org
->point
.x
, primitive
->org
->point
.y
);
813 type
= primitive
->data
->header
.type
;
815 if (type
== CPML_CLOSE
) {
816 path_data
= _cpml_get_point(primitive
, -1);
817 cairo_line_to(cr
, path_data
->point
.x
, path_data
->point
.y
);
818 } else if (type
== CPML_ARC
) {
819 cpml_arc_to_cairo(primitive
, cr
);
821 path
.status
= CAIRO_STATUS_SUCCESS
;
822 path
.data
= primitive
->data
;
823 path
.num_data
= primitive
->data
->header
.length
;
824 cairo_append_path(cr
, &path
);
829 * cpml_primitive_dump:
830 * @primitive: a #CpmlPrimitive
831 * @org_also: (type boolean): whether to output also the origin coordinates
833 * Dumps info on the specified @primitive to stdout: useful for
834 * debugging purposes. If @org_also is 1, a %CPML_MOVE to the
835 * origin is prepended to the data otherwise the
836 * <structfield>org</structfield> field is not used.
841 cpml_primitive_dump(const CpmlPrimitive
*primitive
, int org_also
)
843 const cairo_path_data_t
*data
;
845 const _CpmlPrimitiveClass
*class_data
;
848 data
= primitive
->data
;
849 type
= data
->header
.type
;
850 class_data
= _cpml_class_from_type(type
);
852 if (class_data
== NULL
) {
853 printf("Unknown primitive type (%d)\n", type
);
857 /* Dump the origin, if requested */
860 _cpml_dump_point(primitive
->org
);
864 printf("%s ", class_data
->name
);
866 n_points
= cpml_primitive_get_n_points(primitive
);
867 for (n
= 1; n
< n_points
; ++n
)
868 _cpml_dump_point(_cpml_get_point(primitive
, n
));
873 static const _CpmlPrimitiveClass
*
874 _cpml_class_from_type(CpmlPrimitiveType type
)
876 static const _CpmlPrimitiveClass
*table
[CPML_MAX
+ 1] = { NULL
};
878 if (table
[CPML_LINE
] == NULL
) {
879 table
[CPML_LINE
] = _cpml_line_get_class();
880 table
[CPML_ARC
] = _cpml_arc_get_class();
881 table
[CPML_CURVE
] = _cpml_curve_get_class();
882 table
[CPML_CLOSE
] = _cpml_close_get_class();
885 return (int) type
<= (int) CPML_MAX
? table
[type
] : NULL
;
888 static const _CpmlPrimitiveClass
*
889 _cpml_class_from_obj(const CpmlPrimitive
*primitive
)
891 return _cpml_class_from_type(primitive
->data
->header
.type
);
896 * @primitive: a #CpmlPrimitive
897 * @n_point: the index of the point to retrieve
899 * Gets the specified @n_point from @primitive. The index starts
900 * at 0: if @n_point is 0, the start point (the origin) is
901 * returned, 1 for the second point and so on. If @n_point is
902 * negative, it is considered as a negative index from the end,
903 * so that -1 is the end point, -2 the point before the end point
906 * %CPML_CLOSE is managed in a special way: if @n_point
907 * is -1 or 1 and @primitive is a close-path, this function cycles
908 * the source #CpmlSegment and returns the first point. This is
909 * needed because requesting the end point (or the second point)
910 * of a close path is a valid operation and must returns the start
913 * Returns: a pointer to the requested point (in cairo format)
914 * or <constant>NULL</constant> if the point is outside
919 static cairo_path_data_t
*
920 _cpml_get_point(const CpmlPrimitive
*primitive
, int n_point
)
924 /* For a start point request, simply return the origin
925 * without further checking */
927 return primitive
->org
;
929 /* The CPML_CLOSE special case */
930 if (primitive
->data
->header
.type
== CPML_CLOSE
&&
931 (n_point
== 1 || n_point
== -1))
932 return &primitive
->segment
->data
[1];
934 n_points
= cpml_primitive_get_n_points(primitive
);
938 /* If n_point is negative, consider it as a negative index from the end */
940 n_point
= n_points
+ n_point
;
942 /* Out of range condition */
943 if (n_point
< 0 || n_point
>= n_points
)
946 return n_point
== 0 ? primitive
->org
: &primitive
->data
[n_point
];
950 _cpml_dump_point(const cairo_path_data_t
*path_data
)
952 printf("(%g %g) ", path_data
->point
.x
, path_data
->point
.y
);