adg: improved AdgDress annotations
[adg.git] / src / cpml / cpml-primitive.c
blob2c313c90efacae76abde5d57dadedb42813df819
1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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:CpmlPrimitive
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 #CpmlPath 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: the destination #CpmlPrimitive struct
123 * @segment: (allow-none): 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 if (segment != NULL) {
135 /* The first element of a CpmlSegment is always a CPML_MOVE,
136 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
137 * so the origin is in the second data item */
138 primitive->org = segment->data + 1;
140 /* Also, the segment APIs ensure that @segment is prepended by
141 * only one CPML_MOVE */
142 primitive->data = segment->data + segment->data->header.length;
143 } else {
144 primitive->org = NULL;
145 primitive->data = NULL;
150 * cpml_primitive_copy:
151 * @primitive: the destination #CpmlPrimitive
152 * @src: (allow-none): the source #CpmlPrimitive
154 * Copies @src in @primitive. This is a shallow copy: the internal fields
155 * of @primitive refer to the same memory as the original @src primitive.
157 * Since: 1.0
159 void
160 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
162 if (src != NULL) {
163 memcpy(primitive, src, sizeof(CpmlPrimitive));
168 * cpml_primitive_reset:
169 * @primitive: a #CpmlPrimitive
171 * Resets @primitive so it refers to the first primitive of the
172 * source segment.
174 * Since: 1.0
176 void
177 cpml_primitive_reset(CpmlPrimitive *primitive)
179 cpml_primitive_from_segment(primitive, primitive->segment);
183 * cpml_primitive_next:
184 * @primitive: a #CpmlPrimitive
186 * Changes @primitive so it refers to the next primitive on the
187 * source segment. If there are no more primitives, @primitive is
188 * not changed and 0 is returned.
190 * Returns: (type boolean): %1 on success, %0 if no next primitive found or errors.
192 * Since: 1.0
195 cpml_primitive_next(CpmlPrimitive *primitive)
197 cairo_path_data_t *new_data;
198 const cairo_path_data_t *end_data;
200 new_data = primitive->data + primitive->data->header.length;
201 end_data = primitive->segment->data + primitive->segment->num_data;
203 if (new_data >= end_data)
204 return 0;
206 primitive->org = _cpml_get_point(primitive, -1);
207 primitive->data = new_data;
209 return 1;
213 * cpml_primitive_get_n_points:
214 * @primitive: a #CpmlPrimitive
216 * Gets the number of points required to identify @primitive.
217 * It is similar to cpml_primitive_type_get_n_points() but using
218 * a @primitive instance instead of a type.
220 * Returns: the number of points or %0 on errors.
222 * <!-- Virtual: n_points -->
224 * Since: 1.0
226 size_t
227 cpml_primitive_get_n_points(const CpmlPrimitive *primitive)
229 return cpml_primitive_type_get_n_points(primitive->data->header.type);
233 * cpml_primitive_get_length:
234 * @primitive: a #CpmlPrimitive
236 * Abstracts the length() family functions by providing a common
237 * way to access the underlying primitive-specific implementation.
238 * The function returns the length of @primitive.
240 * Returns: the requested length or 0 on errors
242 * <!-- Virtual: get_length -->
244 * Since: 1.0
246 double
247 cpml_primitive_get_length(const CpmlPrimitive *primitive)
249 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
251 if (class_data == NULL || class_data->get_length == NULL)
252 return 0;
254 return class_data->get_length(primitive);
258 * cpml_primitive_put_extents:
259 * @primitive: a #CpmlPrimitive
260 * @extents: (out caller-allocates): where to store the extents
262 * Abstracts the extents() family functions by providing a common
263 * way to access the underlying primitive-specific implementation.
265 * This function stores in @extents the bounding box of @primitive.
267 * On errors, that is if the extents cannot be calculated for some
268 * reason, this function does nothing.
270 * <!-- Virtual: put_extents -->
272 * Since: 1.0
274 void
275 cpml_primitive_put_extents(const CpmlPrimitive *primitive,
276 CpmlExtents *extents)
278 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
280 if (class_data == NULL || class_data->put_extents == NULL)
281 return;
283 class_data->put_extents(primitive, extents);
287 * cpml_primitive_set_point:
288 * @primitive: a #CpmlPrimitive
289 * @n_point: the index of the point to retrieve
290 * @pair: (allow-none): the source #CpmlPair
292 * Sets the specified @n_point of @primitive to @pair. The @n_point
293 * index starts from 0: if @n_point is 0, the start point (the origin)
294 * is changed, 1 for the second point and so on. If @n_point is
295 * negative, it is considered as a negative index from the end, so
296 * that -1 is the end point, -2 the point before the end point and
297 * so on.
299 * #CPML_CLOSE is managed in a special way: if @n_point
300 * is -1 or 1 and @primitive is a close-path, this function cycles
301 * the source #CpmlSegment and returns the first point. This is
302 * needed because requesting the end point (or the second point)
303 * of a close path is a valid operation and must returns the origin
304 * of the segment.
306 * Returns: (type gboolean): %1 if the point to be set is existent, %0 otherwise.
308 * Since: 1.0
311 cpml_primitive_set_point(CpmlPrimitive *primitive,
312 int n_point, const CpmlPair *pair)
314 cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
316 if (point == NULL)
317 return 0;
319 if (pair != NULL)
320 cpml_pair_to_cairo(pair, point);
322 return 1;
326 * cpml_primitive_put_point:
327 * @primitive: a #CpmlPrimitive
328 * @n_point: the index of the point to retrieve
329 * @pair: (out caller-allocates) (allow-none): the destination #CpmlPair
331 * Gets the specified @n_point from @primitive and stores it into
332 * @pair. The @n_point index is subject to the same rules explained
333 * in the cpml_primitive_set_point() function.
335 * Returns: (type gboolean): %1 if the point is found, %0 otherwise.
337 * Since: 1.0
340 cpml_primitive_put_point(const CpmlPrimitive *primitive,
341 int n_point, CpmlPair *pair)
343 const cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
345 if (point == NULL)
346 return 0;
348 if (pair != NULL)
349 cpml_pair_from_cairo(pair, point);
351 return 1;
355 * cpml_primitive_put_pair_at:
356 * @primitive: a #CpmlPrimitive
357 * @pos: the position value
358 * @pair: (out caller-allocates): the destination #CpmlPair
360 * Abstracts the put_pair_at() family functions by providing a common
361 * way to access the underlying primitive-specific implementation.
363 * It gets the coordinates of the point lying on @primitive
364 * at position @pos. @pos is an homogeneous factor where 0 is the
365 * start point, 1 the end point, 0.5 the mid point and so on.
366 * @pos can be less than 0 or greater than %1, in which case the
367 * coordinates of @pair are interpolated.
369 * On errors, that is if the coordinates cannot be calculated for
370 * some reason, this function does nothing.
372 * <!-- Virtual: put_pair_at -->
374 * Since: 1.0
376 void
377 cpml_primitive_put_pair_at(const CpmlPrimitive *primitive,
378 double pos, CpmlPair *pair)
380 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
382 if (class_data == NULL || class_data->put_pair_at == NULL)
383 return;
385 class_data->put_pair_at(primitive, pos, pair);
389 * cpml_primitive_put_vector_at:
390 * @primitive: a #CpmlPrimitive
391 * @pos: the position value
392 * @vector: (out caller-allocates): the destination #CpmlVector
394 * Abstracts the put_vector_at() family functions by providing a common
395 * way to access the underlying primitive-specific implementation.
397 * It gets the steepness of the point at position @pos on @primitive.
398 * @pos is an homogeneous factor where 0 is the start point, 1 the
399 * end point, 0.5 the mid point and so on.
400 * @pos can be less than 0 or greater than %1, in which case the
401 * coordinates of @pair are interpolated.
403 * On errors, that is if the steepness cannot be calculated for
404 * some reason, this function does nothing.
406 * <!-- Virtual: put_vector_at -->
408 * Since: 1.0
410 void
411 cpml_primitive_put_vector_at(const CpmlPrimitive *primitive,
412 double pos, CpmlVector *vector)
414 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
416 if (class_data == NULL || class_data->put_vector_at == NULL)
417 return;
419 class_data->put_vector_at(primitive, pos, vector);
423 * cpml_primitive_get_closest_pos:
424 * @primitive: a #CpmlPrimitive
425 * @pair: (allow-none): the coordinates of the subject point
427 * Returns the pos value of the point on @primitive nearest to @pair.
428 * The returned value is always clamped between %0 and %1.
430 * Returns: the requested pos value between %0 and %1, or %-1 on errors.
432 * <!-- Virtual: get_closest_pos -->
434 * Since: 1.0
436 double
437 cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive,
438 const CpmlPair *pair)
440 const _CpmlPrimitiveClass *class_data;
442 if (pair == NULL)
443 return -1;
445 class_data = _cpml_class_from_obj(primitive);
446 if (class_data == NULL || class_data->get_closest_pos == NULL)
447 return -1;
449 return class_data->get_closest_pos(primitive, pair);
453 * cpml_primitive_put_intersections:
454 * @primitive: the first #CpmlPrimitive
455 * @primitive2: (allow-none) the second #CpmlPrimitive
456 * @n_dest: maximum number of intersections to return
457 * @dest: (out caller-allocates) (array length=n_dest) (allow-none): the destination buffer that can contain @n_dest #CpmlPair
459 * Finds the intersection points between the given primitives and
460 * returns the result in @dest. The size of @dest should be enough
461 * to store @n_dest #CpmlPair. The maximum number of intersections
462 * is dependent on the type of the primitive involved in the
463 * operation. If there are at least one Bézier curve involved, up to
464 * %4 intersections could be returned. Otherwise, if there is an arc
465 * the intersections will be %2 at maximum. For line primitives, there
466 * is only %1 point (or %0 if the lines are parallel).
468 * <note>
469 * <para>
470 * The convention used by the CPML library is that a primitive should
471 * implement only the intersection algorithms with lower degree
472 * primitives. This is required to avoid code duplication: intersection
473 * between arc and Bézier curves must be implemented by #CPML_CURVE and
474 * intersection between lines and arcs must be implemented by #CPML_ARC.
475 * cpml_primitive_put_intersections() will take care of swapping the
476 * arguments if they are not properly ordered.
477 * </para>
478 * </note>
480 * Returns: the number of intersection points found or 0 if the
481 * primitives do not intersect or on errors.
483 * <!-- Virtual: put_intersections -->
485 * Since: 1.0
487 size_t
488 cpml_primitive_put_intersections(const CpmlPrimitive *primitive,
489 const CpmlPrimitive *primitive2,
490 size_t n_dest, CpmlPair *dest)
492 const _CpmlPrimitiveClass *class_data;
493 size_t n_points, n_points2;
495 if (primitive2 == NULL || dest == NULL)
496 return 0;
498 class_data = _cpml_class_from_obj(primitive);
499 if (class_data == NULL || class_data->put_intersections == NULL)
500 return 0;
502 n_points = cpml_primitive_get_n_points(primitive);
503 n_points2 = cpml_primitive_get_n_points(primitive2);
505 if (n_points == 0 || n_points2 == 0)
506 return 0;
508 /* Primitives reordering: the first must be the more complex one */
509 if (n_points < n_points2) {
510 const CpmlPrimitive *old_primitive2 = primitive2;
511 primitive2 = primitive;
512 primitive = old_primitive2;
515 return class_data->put_intersections(primitive, primitive2, n_dest, dest);
519 * cpml_primitive_put_intersections_with_segment:
520 * @primitive: a #CpmlPrimitive
521 * @segment: (allow-none): a #CpmlSegment
522 * @n_dest: maximum number of intersections to return
523 * @dest: (out caller-allocates) (array length=n_dest) (allow-none): the destination buffer that can contain @n_dest #CpmlPair
525 * Computes the intersections between @segment and @primitive by
526 * sequentially scanning the primitives in @segment and looking
527 * for their intersections with @primitive.
529 * If the intersections are more than @n_dest, only the first
530 * @n_dest pairs are stored.
532 * Returns: the number of intersection points found or 0 if the
533 * items do not intersect or on errors.
535 * Since: 1.0
537 size_t
538 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
539 const CpmlSegment *segment,
540 size_t n_dest, CpmlPair *dest)
542 CpmlPrimitive portion;
543 size_t found;
545 if (segment == NULL || dest == NULL)
546 return 0;
548 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
549 found = 0;
551 while (found < n_dest) {
552 found += cpml_primitive_put_intersections(&portion, primitive,
553 n_dest-found, dest+found);
554 if (!cpml_primitive_next(&portion))
555 break;
558 return found;
562 * cpml_primitive_offset:
563 * @primitive: a #CpmlPrimitive
564 * @offset: distance for the computed offset primitive
566 * Given a primitive, computes the same (or approximated) parallel
567 * primitive distant @offset from the original one and returns
568 * the result by changing @primitive.
570 * On errors, that is if the offset primitive cannot be calculated
571 * for some reason, this function does nothing.
573 * <!-- Virtual: offset -->
575 * Since: 1.0
577 void
578 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
580 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
582 if (class_data == NULL || class_data->offset == NULL)
583 return;
585 class_data->offset(primitive, offset);
589 * cpml_primitive_join:
590 * @primitive: the first #CpmlPrimitive
591 * @primitive2: (inout) (transfer none): the second #CpmlPrimitive
593 * Joins two primitive modifying the end point of @primitive and the
594 * start point of @primitive2 so that the resulting points will overlap.
596 * <important>
597 * <title>TODO</title>
598 * <itemizedlist>
599 * <listitem>Actually, the join is done by extending the end vector
600 * of @primitive and the start vector of @primitive2 and
601 * interpolating the intersection: this means no primitive
602 * dependent code is needed. Anyway, it is likely to change
603 * in the future because this approach is quite naive when
604 * curves are involved.</listitem>
605 * </itemizedlist>
606 * </important>
608 * Returns: (type boolean): %1 on success, %0 if the primitives cannot be joint.
610 * <!-- Virtual: join -->
612 * Since: 1.0
615 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
617 cairo_path_data_t *end1, *start2;
618 CpmlPrimitive line1, line2;
619 cairo_path_data_t data1[2], data2[2];
620 CpmlPair joint;
622 end1 = _cpml_get_point(primitive, -1);
623 start2 = _cpml_get_point(primitive2, 0);
625 /* Check if the primitives are yet connected */
626 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
627 return 1;
629 line1.org = _cpml_get_point(primitive, -2);
630 line1.data = data1;
631 data1[0].header.type = CPML_LINE;
632 data1[1] = *end1;
634 line2.org = start2;
635 line2.data = data2;
636 data2[0].header.type = CPML_LINE;
637 data2[1] = *_cpml_get_point(primitive2, 1);
639 if (!cpml_primitive_put_intersections(&line1, &line2, 1, &joint))
640 return 0;
642 cpml_pair_to_cairo(&joint, end1);
643 cpml_pair_to_cairo(&joint, start2);
645 return 1;
649 * cpml_primitive_to_cairo:
650 * @primitive: a #CpmlPrimitive
651 * @cr: (inout) (transfer none): the destination cairo context
653 * Renders a single @primitive to the @cr cairo context.
654 * As a special case, if the primitive is a #CPML_CLOSE, an
655 * equivalent line is rendered, because a close path left alone
656 * is not renderable.
658 * Also a #CPML_ARC primitive is treated specially, as it is not
659 * natively supported by cairo and has its own rendering API.
661 * Since: 1.0
663 void
664 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
666 CpmlPrimitiveType type;
667 cairo_path_t path;
668 cairo_path_data_t *path_data;
670 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
672 type = primitive->data->header.type;
674 if (type == CPML_CLOSE) {
675 path_data = _cpml_get_point(primitive, -1);
676 cairo_line_to(cr, path_data->point.x, path_data->point.y);
677 } else if (type == CPML_ARC) {
678 cpml_arc_to_cairo(primitive, cr);
679 } else {
680 path.status = CAIRO_STATUS_SUCCESS;
681 path.data = primitive->data;
682 path.num_data = primitive->data->header.length;
683 cairo_append_path(cr, &path);
688 * cpml_primitive_dump:
689 * @primitive: a #CpmlPrimitive
690 * @org_also: whether to output also the origin coordinates
692 * Dumps info on the specified @primitive to stdout: useful for
693 * debugging purposes. If @org_also is 1, a #CPML_MOVE to the
694 * origin is prepended to the data otherwise the
695 * <structfield>org</structfield> field is not used.
697 * Since: 1.0
699 void
700 cpml_primitive_dump(const CpmlPrimitive *primitive, int org_also)
702 const cairo_path_data_t *data;
703 int type;
704 const _CpmlPrimitiveClass *class_data;
705 size_t n, n_points;
707 data = primitive->data;
708 type = data->header.type;
709 class_data = _cpml_class_from_type(type);
711 if (class_data == NULL) {
712 printf("Unknown primitive type (%d)\n", type);
713 return;
716 /* Dump the origin, if requested */
717 if (org_also) {
718 printf("move to ");
719 _cpml_dump_point(primitive->org);
720 printf("\n");
723 printf("%s ", class_data->name);
725 n_points = cpml_primitive_get_n_points(primitive);
726 for (n = 1; n < n_points; ++n)
727 _cpml_dump_point(_cpml_get_point(primitive, n));
729 printf("\n");
733 static const _CpmlPrimitiveClass *
734 _cpml_class_from_type(CpmlPrimitiveType type)
736 if (type == CPML_LINE)
737 return _cpml_line_get_class();
738 else if (type == CPML_ARC)
739 return _cpml_arc_get_class();
740 else if (type == CPML_CURVE)
741 return _cpml_curve_get_class();
742 else if (type == CPML_CLOSE)
743 return _cpml_close_get_class();
745 return NULL;
748 static const _CpmlPrimitiveClass *
749 _cpml_class_from_obj(const CpmlPrimitive *primitive)
751 return _cpml_class_from_type(primitive->data->header.type);
755 * _cpml_get_point:
756 * @primitive: a #CpmlPrimitive
757 * @n_point: the index of the point to retrieve
759 * Gets the specified @n_point from @primitive. The index starts
760 * at 0: if @n_point is 0, the start point (the origin) is
761 * returned, 1 for the second point and so on. If @n_point is
762 * negative, it is considered as a negative index from the end,
763 * so that -1 is the end point, -2 the point before the end point
764 * and so on.
766 * #CPML_CLOSE is managed in a special way: if @n_point
767 * is -1 or 1 and @primitive is a close-path, this function cycles
768 * the source #CpmlSegment and returns the first point. This is
769 * needed because requesting the end point (or the second point)
770 * of a close path is a valid operation and must returns the start
771 * of the segment.
773 * Returns: a pointer to the requested point (in cairo format)
774 * or %NULL if the point is outside the valid range
776 * Since: 1.0
778 static cairo_path_data_t *
779 _cpml_get_point(const CpmlPrimitive *primitive, int n_point)
781 size_t n_points;
783 /* For a start point request, simply return the origin
784 * without further checking */
785 if (n_point == 0)
786 return primitive->org;
788 /* The CPML_CLOSE special case */
789 if (primitive->data->header.type == CAIRO_PATH_CLOSE_PATH &&
790 (n_point == 1 || n_point == -1))
791 return &primitive->segment->data[1];
793 n_points = cpml_primitive_get_n_points(primitive);
794 if (n_points == 0)
795 return NULL;
797 /* If n_point is negative, consider it as a negative index from the end */
798 if (n_point < 0)
799 n_point = n_points + n_point;
801 /* Out of range condition */
802 if (n_point < 0 || n_point >= n_points)
803 return NULL;
805 return n_point == 0 ? primitive->org : &primitive->data[n_point];
808 static void
809 _cpml_dump_point(const cairo_path_data_t *path_data)
811 printf("(%g %g) ", path_data->point.x, path_data->point.y);