cpml: make CpmlPrimitiveType an alias for cairo_path_data_type_t
[adg.git] / src / cpml / cpml-primitive.c
blobc59a4e1343f0e543414fdcbc9cf8541d96cc12c4
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.
21 /**
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.
34 * Since: 1.0
35 **/
37 /**
38 * CpmlPrimitiveType:
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().
60 * Since: 1.0
61 **/
63 /**
64 * CpmlPrimitive:
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).
74 * Since: 1.0
75 **/
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"
83 #include "cpml-arc.h"
84 #include "cpml-curve.h"
85 #include <string.h>
86 #include <stdio.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,
95 int n_point);
96 static void _cpml_dump_point (const cairo_path_data_t *path_data);
99 /**
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
107 * Since: 1.0
109 size_t
110 cpml_primitive_type_get_n_points(CpmlPrimitiveType type)
112 const _CpmlPrimitiveClass *class_data = _cpml_class_from_type(type);
114 if (class_data == NULL)
115 return 0;
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.
127 * Since: 1.0
129 void
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.
152 * Since: 1.0
154 void
155 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
157 memcpy(primitive, src, sizeof(CpmlPrimitive));
161 * cpml_primitive_reset:
162 * @primitive: (inout): a #CpmlPrimitive
164 * Resets @primitive so it refers to the first primitive of the
165 * source segment.
167 * Since: 1.0
169 void
170 cpml_primitive_reset(CpmlPrimitive *primitive)
172 cpml_primitive_from_segment(primitive, primitive->segment);
176 * cpml_primitive_next:
177 * @primitive: (inout): a #CpmlPrimitive
179 * Changes @primitive so it refers to the next primitive on the
180 * source segment. If there are no more primitives, @primitive is
181 * not changed and 0 is returned.
183 * Returns: (type boolean): 1 on success, 0 if no next primitive found or errors.
185 * Since: 1.0
188 cpml_primitive_next(CpmlPrimitive *primitive)
190 cairo_path_data_t *new_data;
191 const cairo_path_data_t *end_data;
193 new_data = primitive->data + primitive->data->header.length;
194 end_data = primitive->segment->data + primitive->segment->num_data;
196 if (new_data >= end_data)
197 return 0;
199 primitive->org = _cpml_get_point(primitive, -1);
200 primitive->data = new_data;
202 return 1;
206 * cpml_primitive_get_n_points:
207 * @primitive: a #CpmlPrimitive
209 * Gets the number of points required to identify @primitive.
210 * It is similar to cpml_primitive_type_get_n_points() but using
211 * a @primitive instance instead of a type.
213 * Returns: the number of points or 0 on errors.
215 * <!-- Virtual: n_points -->
217 * Since: 1.0
219 size_t
220 cpml_primitive_get_n_points(const CpmlPrimitive *primitive)
222 return cpml_primitive_type_get_n_points(primitive->data->header.type);
226 * cpml_primitive_get_length:
227 * @primitive: a #CpmlPrimitive
229 * Abstracts the length() family functions by providing a common
230 * way to access the underlying primitive-specific implementation.
231 * The function returns the length of @primitive.
233 * Returns: the requested length or 0 on errors
235 * <!-- Virtual: get_length -->
237 * Since: 1.0
239 double
240 cpml_primitive_get_length(const CpmlPrimitive *primitive)
242 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
244 if (class_data == NULL || class_data->get_length == NULL)
245 return 0;
247 return class_data->get_length(primitive);
251 * cpml_primitive_put_extents:
252 * @primitive: (in): a #CpmlPrimitive
253 * @extents: (out): where to store the extents
255 * Abstracts the extents() family functions by providing a common
256 * way to access the underlying primitive-specific implementation.
258 * This function stores in @extents the bounding box of @primitive.
260 * On errors, that is if the extents cannot be calculated for some
261 * reason, this function does nothing.
263 * <!-- Virtual: put_extents -->
265 * Since: 1.0
267 void
268 cpml_primitive_put_extents(const CpmlPrimitive *primitive,
269 CpmlExtents *extents)
271 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
273 if (class_data == NULL || class_data->put_extents == NULL)
274 return;
276 class_data->put_extents(primitive, extents);
280 * cpml_primitive_set_point:
281 * @primitive: a #CpmlPrimitive
282 * @n_point: the index of the point to retrieve
283 * @pair: the source #CpmlPair
285 * Sets the specified @n_point of @primitive to @pair. The @n_point
286 * index starts from 0: if @n_point is 0, the start point (the origin)
287 * is changed, 1 for the second point and so on. If @n_point is
288 * negative, it is considered as a negative index from the end, so
289 * that -1 is the end point, -2 the point before the end point and
290 * so on.
292 * #CPML_CLOSE is managed in a special way: if @n_point
293 * is -1 or 1 and @primitive is a close-path, this function cycles
294 * the source #CpmlSegment and returns the first point. This is
295 * needed because requesting the end point (or the second point)
296 * of a close path is a valid operation and must returns the origin
297 * of the segment.
299 * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise.
301 * Since: 1.0
304 cpml_primitive_set_point(CpmlPrimitive *primitive,
305 int n_point, const CpmlPair *pair)
307 cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
309 if (point == NULL)
310 return 0;
312 if (pair != NULL)
313 cpml_pair_to_cairo(pair, point);
315 return 1;
319 * cpml_primitive_put_point:
320 * @primitive: a #CpmlPrimitive
321 * @n_point: the index of the point to retrieve
322 * @pair: (out caller-allocates): the destination #CpmlPair
324 * Gets the specified @n_point from @primitive and stores it into
325 * @pair. The @n_point index is subject to the same rules explained
326 * in the cpml_primitive_set_point() function.
328 * Returns: (type gboolean): 1 if the point is found, 0 otherwise.
330 * Since: 1.0
333 cpml_primitive_put_point(const CpmlPrimitive *primitive,
334 int n_point, CpmlPair *pair)
336 const cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
338 if (point == NULL)
339 return 0;
341 cpml_pair_from_cairo(pair, point);
342 return 1;
346 * cpml_primitive_put_pair_at:
347 * @primitive: (in): a #CpmlPrimitive
348 * @pos: (in): the position value
349 * @pair: (out): the destination #CpmlPair
351 * Abstracts the <function>put_pair_at</function> family functions by
352 * providing a common way to access the underlying primitive-specific
353 * implementation.
355 * It gets the coordinates of the point lying on @primitive
356 * at position @pos. @pos is an homogeneous factor where 0 is the
357 * start point, 1 the end point, 0.5 the mid point and so on.
358 * @pos can be less than 0 or greater than 1, in which case the
359 * coordinates of @pair are interpolated.
361 * On errors, that is if the coordinates cannot be calculated for
362 * some reason, this function does nothing.
364 * <!-- Virtual: put_pair_at -->
366 * Since: 1.0
368 void
369 cpml_primitive_put_pair_at(const CpmlPrimitive *primitive,
370 double pos, CpmlPair *pair)
372 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
374 if (class_data == NULL || class_data->put_pair_at == NULL)
375 return;
377 class_data->put_pair_at(primitive, pos, pair);
381 * cpml_primitive_put_vector_at:
382 * @primitive: (in): a #CpmlPrimitive
383 * @pos: (in): the position value
384 * @vector: (out): the destination #CpmlVector
386 * Abstracts the <function>put_vector_at</function> family functions by
387 * providing a common way to access the underlying primitive-specific
388 * implementation.
390 * It gets the steepness of the point at position @pos on @primitive.
391 * @pos is an homogeneous factor where 0 is the start point, 1 the
392 * end point, 0.5 the mid point and so on.
393 * @pos can be less than 0 or greater than 1, in which case the
394 * coordinates of @pair are interpolated.
396 * On errors, that is if the steepness cannot be calculated for
397 * some reason, this function does nothing.
399 * <!-- Virtual: put_vector_at -->
401 * Since: 1.0
403 void
404 cpml_primitive_put_vector_at(const CpmlPrimitive *primitive,
405 double pos, CpmlVector *vector)
407 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
409 if (class_data == NULL || class_data->put_vector_at == NULL)
410 return;
412 class_data->put_vector_at(primitive, pos, vector);
416 * cpml_primitive_is_inside:
417 * @primitive: a #CpmlPrimitive
418 * @pair: the coordinates of the subject point
420 * Checks if @pair is inside the bounding box of @primitive. This
421 * can be useful e.g. to verify if an intersection point is a real
422 * intersection or an hypothetical one.
424 * Returns: 1 if @pair is inside the bounding box of @primitive, 0 otherwise.
426 * Since: 1.0
429 cpml_primitive_is_inside(const CpmlPrimitive *primitive, const CpmlPair *pair)
431 CpmlExtents extents = { 0 };
432 cpml_primitive_put_extents(primitive, &extents);
433 return cpml_extents_pair_is_inside(&extents, pair);
437 * cpml_primitive_get_closest_pos:
438 * @primitive: a #CpmlPrimitive
439 * @pair: the coordinates of the subject point
441 * Returns the pos value of the point on @primitive nearest to @pair.
442 * The returned value is always clamped between 0 and 1.
444 * Returns: the requested pos value between 0 and 1, or -1 on errors.
446 * <!-- Virtual: get_closest_pos -->
448 * Since: 1.0
450 double
451 cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive,
452 const CpmlPair *pair)
454 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
456 if (class_data == NULL || class_data->get_closest_pos == NULL)
457 return -1;
459 return class_data->get_closest_pos(primitive, pair);
463 * cpml_primitive_put_intersections:
464 * @primitive: the first #CpmlPrimitive
465 * @primitive2: the second #CpmlPrimitive
466 * @n_dest: maximum number of intersections to return
467 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
469 * Finds the intersection points between the given primitives and
470 * returns the result in @dest. The size of @dest should be enough
471 * to store @n_dest #CpmlPair. The maximum number of intersections
472 * is dependent on the type of the primitive involved in the
473 * operation. If there are at least one Bézier curve involved, up to
474 * 4 intersections could be returned. Otherwise, if there is an arc
475 * the intersections will be 2 at maximum. For line primitives, there
476 * is only 1 point (or 0 if the lines are parallel).
478 * Also hypothetical intersections are returned, that is intersections
479 * made by extending the primitives outside their bounds. This means e.g.
480 * two lines always return one intersection if they are not parallel. To
481 * discriminate real intersections you should check the returned points
482 * with cpml_primitive_is_inside(), for example:
484 * <informalexample><programlisting language="C">
485 * if (cpml_primitive_put_intersections(line1, line2, 1, &pair) == 0) {
486 * // line1 and line2 are parallels
487 * } else if (cpml_primitive_is_inside(line1, &pair) &&
488 * cpml_primitive_is_inside(line2, &pair)) {
489 * // This is a real intersection
490 * } else {
491 * // This is an hypothetical intersection
493 * </programlisting></informalexample>
495 * <note>
496 * <para>
497 * The convention used by the CPML library is that a primitive should
498 * implement only the intersection algorithms with lower degree
499 * primitives. This is required to avoid code duplication: intersection
500 * between arc and Bézier curves must be implemented by #CPML_CURVE and
501 * intersection between lines and arcs must be implemented by #CPML_ARC.
502 * cpml_primitive_put_intersections() will take care of swapping the
503 * arguments if they are not properly ordered.
504 * </para>
505 * </note>
507 * Returns: the number of intersection points found or 0 if the
508 * primitives do not intersect or on errors
510 * <!-- Virtual: put_intersections -->
512 * Since: 1.0
514 size_t
515 cpml_primitive_put_intersections(const CpmlPrimitive *primitive,
516 const CpmlPrimitive *primitive2,
517 size_t n_dest, CpmlPair *dest)
519 const _CpmlPrimitiveClass *class_data;
520 size_t n_points, n_points2;
522 if (n_dest == 0)
523 return 0;
525 n_points = cpml_primitive_get_n_points(primitive);
526 n_points2 = cpml_primitive_get_n_points(primitive2);
528 /* Check if the primitives can intersect */
529 if (n_points == 0 || n_points2 == 0)
530 return 0;
532 /* Primitives reordering: the first must be the more complex one */
533 if (n_points < n_points2) {
534 return cpml_primitive_put_intersections(primitive2, primitive,
535 n_dest, dest);
538 /* Check if put_intersections is implemented */
539 class_data = _cpml_class_from_obj(primitive);
540 if (class_data == NULL || class_data->put_intersections == NULL)
541 return 0;
543 return class_data->put_intersections(primitive, primitive2, n_dest, dest);
547 * cpml_primitive_put_intersections_with_segment:
548 * @primitive: a #CpmlPrimitive
549 * @segment: a #CpmlSegment
550 * @n_dest: maximum number of intersections to return
551 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
553 * Computes the intersections between @segment and @primitive by
554 * sequentially scanning the primitives in @segment and looking
555 * for their intersections with @primitive.
557 * If the intersections are more than @n_dest, only the first
558 * @n_dest pairs are stored.
560 * Returns: the number of intersections found
562 * Since: 1.0
564 size_t
565 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
566 const CpmlSegment *segment,
567 size_t n_dest, CpmlPair *dest)
569 CpmlPrimitive portion;
570 size_t found;
572 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
573 found = 0;
575 while (found < n_dest) {
576 found += cpml_primitive_put_intersections(&portion, primitive,
577 n_dest-found, dest+found);
578 if (!cpml_primitive_next(&portion))
579 break;
582 return found;
586 * cpml_primitive_offset:
587 * @primitive: (inout): a #CpmlPrimitive
588 * @offset: (in): distance for the computed offset primitive
590 * Given a primitive, computes the same (or approximated) parallel
591 * primitive distant @offset from the original one and returns
592 * the result by changing @primitive.
594 * On errors, that is if the offset primitive cannot be calculated
595 * for some reason, this function does nothing.
597 * <!-- Virtual: offset -->
599 * Since: 1.0
601 void
602 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
604 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
606 if (class_data == NULL || class_data->offset == NULL)
607 return;
609 class_data->offset(primitive, offset);
613 * cpml_primitive_join:
614 * @primitive: (inout): the first #CpmlPrimitive
615 * @primitive2: (inout): the second #CpmlPrimitive
617 * Joins two primitive modifying the end point of @primitive and the
618 * start point of @primitive2 so that the resulting points will overlap.
620 * <important>
621 * <title>TODO</title>
622 * <itemizedlist>
623 * <listitem>Actually, the join is done by extending the end vector
624 * of @primitive and the start vector of @primitive2 and
625 * interpolating the intersection: this means no primitive
626 * dependent code is needed. Anyway, it is likely to change
627 * in the future because this approach is quite naive when
628 * curves are involved.</listitem>
629 * </itemizedlist>
630 * </important>
632 * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined.
634 * <!-- Virtual: join -->
636 * Since: 1.0
639 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
641 cairo_path_data_t *end1, *start2;
642 CpmlPrimitive line1, line2;
643 cairo_path_data_t data1[2], data2[2];
644 CpmlPair joint;
646 end1 = _cpml_get_point(primitive, -1);
647 start2 = _cpml_get_point(primitive2, 0);
649 /* Check if the primitives are yet connected */
650 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
651 return 1;
653 line1.org = _cpml_get_point(primitive, -2);
654 line1.data = data1;
655 data1[0].header.type = CPML_LINE;
656 data1[1] = *end1;
658 line2.org = start2;
659 line2.data = data2;
660 data2[0].header.type = CPML_LINE;
661 data2[1] = *_cpml_get_point(primitive2, 1);
663 if (!cpml_primitive_put_intersections(&line1, &line2, 1, &joint))
664 return 0;
666 cpml_pair_to_cairo(&joint, end1);
667 cpml_pair_to_cairo(&joint, start2);
669 return 1;
673 * cpml_primitive_to_cairo:
674 * @primitive: (in): a #CpmlPrimitive
675 * @cr: (inout): the destination cairo context
677 * Renders a single @primitive to the @cr cairo context.
678 * As a special case, if the primitive is a #CPML_CLOSE, an
679 * equivalent line is rendered, because a close path left alone
680 * is not renderable.
682 * Also a #CPML_ARC primitive is treated specially, as it is not
683 * natively supported by cairo and has its own rendering API.
685 * Since: 1.0
687 void
688 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
690 CpmlPrimitiveType type;
691 cairo_path_t path;
692 cairo_path_data_t *path_data;
694 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
696 type = primitive->data->header.type;
698 if (type == CPML_CLOSE) {
699 path_data = _cpml_get_point(primitive, -1);
700 cairo_line_to(cr, path_data->point.x, path_data->point.y);
701 } else if (type == CPML_ARC) {
702 cpml_arc_to_cairo(primitive, cr);
703 } else {
704 path.status = CAIRO_STATUS_SUCCESS;
705 path.data = primitive->data;
706 path.num_data = primitive->data->header.length;
707 cairo_append_path(cr, &path);
712 * cpml_primitive_dump:
713 * @primitive: a #CpmlPrimitive
714 * @org_also: whether to output also the origin coordinates
716 * Dumps info on the specified @primitive to stdout: useful for
717 * debugging purposes. If @org_also is 1, a #CPML_MOVE to the
718 * origin is prepended to the data otherwise the
719 * <structfield>org</structfield> field is not used.
721 * Since: 1.0
723 void
724 cpml_primitive_dump(const CpmlPrimitive *primitive, int org_also)
726 const cairo_path_data_t *data;
727 int type;
728 const _CpmlPrimitiveClass *class_data;
729 size_t n, n_points;
731 data = primitive->data;
732 type = data->header.type;
733 class_data = _cpml_class_from_type(type);
735 if (class_data == NULL) {
736 printf("Unknown primitive type (%d)\n", type);
737 return;
740 /* Dump the origin, if requested */
741 if (org_also) {
742 printf("move to ");
743 _cpml_dump_point(primitive->org);
744 printf("\n");
747 printf("%s ", class_data->name);
749 n_points = cpml_primitive_get_n_points(primitive);
750 for (n = 1; n < n_points; ++n)
751 _cpml_dump_point(_cpml_get_point(primitive, n));
753 printf("\n");
757 static const _CpmlPrimitiveClass *
758 _cpml_class_from_type(CpmlPrimitiveType type)
760 if (type == CPML_LINE)
761 return _cpml_line_get_class();
762 else if (type == CPML_ARC)
763 return _cpml_arc_get_class();
764 else if (type == CPML_CURVE)
765 return _cpml_curve_get_class();
766 else if (type == CPML_CLOSE)
767 return _cpml_close_get_class();
769 return NULL;
772 static const _CpmlPrimitiveClass *
773 _cpml_class_from_obj(const CpmlPrimitive *primitive)
775 return _cpml_class_from_type(primitive->data->header.type);
779 * _cpml_get_point:
780 * @primitive: a #CpmlPrimitive
781 * @n_point: the index of the point to retrieve
783 * Gets the specified @n_point from @primitive. The index starts
784 * at 0: if @n_point is 0, the start point (the origin) is
785 * returned, 1 for the second point and so on. If @n_point is
786 * negative, it is considered as a negative index from the end,
787 * so that -1 is the end point, -2 the point before the end point
788 * and so on.
790 * #CPML_CLOSE is managed in a special way: if @n_point
791 * is -1 or 1 and @primitive is a close-path, this function cycles
792 * the source #CpmlSegment and returns the first point. This is
793 * needed because requesting the end point (or the second point)
794 * of a close path is a valid operation and must returns the start
795 * of the segment.
797 * Returns: a pointer to the requested point (in cairo format)
798 * or <constant>NULL</constant> if the point is outside
799 * the valid range.
801 * Since: 1.0
803 static cairo_path_data_t *
804 _cpml_get_point(const CpmlPrimitive *primitive, int n_point)
806 size_t n_points;
808 /* For a start point request, simply return the origin
809 * without further checking */
810 if (n_point == 0)
811 return primitive->org;
813 /* The CPML_CLOSE special case */
814 if (primitive->data->header.type == CPML_CLOSE &&
815 (n_point == 1 || n_point == -1))
816 return &primitive->segment->data[1];
818 n_points = cpml_primitive_get_n_points(primitive);
819 if (n_points == 0)
820 return NULL;
822 /* If n_point is negative, consider it as a negative index from the end */
823 if (n_point < 0)
824 n_point = n_points + n_point;
826 /* Out of range condition */
827 if (n_point < 0 || n_point >= n_points)
828 return NULL;
830 return n_point == 0 ? primitive->org : &primitive->data[n_point];
833 static void
834 _cpml_dump_point(const cairo_path_data_t *path_data)
836 printf("(%g %g) ", path_data->point.x, path_data->point.y);