doc: update copyright line for 2019
[adg.git] / src / cpml / cpml-primitive.c
blob37e356f1d00348a8593b50542bb3682da0620329
1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2007-2019 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>
88 /* This is the best way I found to get the maximum value
89 * of a bunch of constant by using only the preprocessor */
90 #define CPML_MAX (CPML_LINE | CPML_ARC | CPML_CURVE | CPML_CLOSE)
93 static const _CpmlPrimitiveClass *
94 _cpml_class_from_type (CpmlPrimitiveType type);
95 static const _CpmlPrimitiveClass *
96 _cpml_class_from_obj (const CpmlPrimitive *primitive);
97 static cairo_path_data_t *
98 _cpml_get_point (const CpmlPrimitive *primitive,
99 int n_point);
100 static void _cpml_dump_point (const cairo_path_data_t *path_data);
104 * cpml_primitive_type_get_n_points:
105 * @type: a primitive type
107 * Gets the number of points required to identify the @type primitive.
109 * Returns: the number of points or 0 on errors
111 * Since: 1.0
113 size_t
114 cpml_primitive_type_get_n_points(CpmlPrimitiveType type)
116 const _CpmlPrimitiveClass *class_data = _cpml_class_from_type(type);
118 if (class_data == NULL)
119 return 0;
121 return class_data->n_points;
125 * cpml_primitive_type:
126 * @primitive: a #CpmlPrimitive struct
128 * Returns the type of @primitive, i.e. %CPML_LINE for lines,
129 * %CPML_ARC for arcs or circles, %CPML_CURVE for cubic Bézier
130 * curves and %CPML_CLOSE for closing lines. The %CPML_MOVE is
131 * used extensively *between* segments but it is not considered
132 * by itself a primitive, so it should never been returned.
134 * Returns: the type of the primitive.
136 * Since: 1.0
138 CpmlPrimitiveType
139 cpml_primitive_type(const CpmlPrimitive *primitive)
141 return primitive->data[0].header.type;
145 * cpml_primitive_from_segment:
146 * @primitive: (out): the destination #CpmlPrimitive struct
147 * @segment: (in): the source segment
149 * Initializes @primitive to the first primitive of @segment.
151 * Since: 1.0
153 void
154 cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment)
156 primitive->segment = segment;
158 /* The first element of a CpmlSegment is always a CPML_MOVE,
159 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
160 * so the origin is in the second data item */
161 primitive->org = segment->data + 1;
163 /* Also, the segment APIs ensure that @segment is prepended by
164 * only one CPML_MOVE */
165 primitive->data = segment->data + segment->data->header.length;
169 * cpml_primitive_copy:
170 * @primitive: (out): the destination #CpmlPrimitive
171 * @src: (in): the source #CpmlPrimitive
173 * Copies @src in @primitive. This is a shallow copy: the internal fields
174 * of @primitive refer to the same memory as the original @src primitive.
176 * Since: 1.0
178 void
179 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
181 memcpy(primitive, src, sizeof(CpmlPrimitive));
185 * cpml_primitive_copy_data:
186 * @primitive: the destination #CpmlPrimitive
187 * @src: the source primitive to copy
189 * Copies the memory referenced by the <structfield>org</structfield>
190 * and <structfield>data</structfield> fields from @src to @primitive.
191 * For a shallow copy, check out cpml_primitive_copy().
193 * This could seem a somewhat unusual operation because @primitive
194 * should contain the same primitive as @src (i.e., the
195 * <structfield>data->header</structfield> field must be the same)
196 * but it can be convenient in some situation, such as when restoring
197 * the original data from a backup primitive, e.g.:
199 * <informalexample><programlisting language="C">
200 * CpmlPrimitive *backup;
202 * backup = cpml_primitive_deep_dup(&primitive);
203 * // Now &primitive points can be freely modified
204 * ...
205 * // Let's restore &primitive original points
206 * cpml_primitive_copy_data(&primitive, backup);
207 * g_free(backup);
208 * </programlisting></informalexample>
210 * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors.
212 * Since: 1.0
215 cpml_primitive_copy_data(CpmlPrimitive *primitive, const CpmlPrimitive *src)
217 if (primitive->data[0].header.type != src->data[0].header.type ||
218 primitive->data[0].header.length != src->data[0].header.length)
219 return 0;
221 memcpy(primitive->org, src->org, sizeof(cairo_path_data_t));
222 memcpy(primitive->data, src->data,
223 sizeof(cairo_path_data_t) * src->data[0].header.length);
224 return 1;
228 * cpml_primitive_reset:
229 * @primitive: (inout): a #CpmlPrimitive
231 * Resets @primitive so it refers to the first primitive of the
232 * source segment.
234 * Since: 1.0
236 void
237 cpml_primitive_reset(CpmlPrimitive *primitive)
239 cpml_primitive_from_segment(primitive, primitive->segment);
243 * cpml_primitive_next:
244 * @primitive: (inout): a #CpmlPrimitive
246 * Changes @primitive so it refers to the next primitive on the
247 * source segment. If there are no more primitives, @primitive is
248 * not changed and 0 is returned.
250 * Returns: (type boolean): 1 on success, 0 if no next primitive found or errors.
252 * Since: 1.0
255 cpml_primitive_next(CpmlPrimitive *primitive)
257 cairo_path_data_t *new_data;
258 const cairo_path_data_t *end_data;
260 new_data = primitive->data + primitive->data->header.length;
261 end_data = primitive->segment->data + primitive->segment->num_data;
263 if (new_data >= end_data)
264 return 0;
266 primitive->org = _cpml_get_point(primitive, -1);
267 primitive->data = new_data;
269 return 1;
273 * cpml_primitive_get_n_points:
274 * @primitive: a #CpmlPrimitive
276 * Gets the number of points required to identify @primitive.
277 * It is similar to cpml_primitive_type_get_n_points() but using
278 * a @primitive instance instead of a type.
280 * Returns: the number of points or 0 on errors.
282 * <!-- Virtual: n_points -->
284 * Since: 1.0
286 size_t
287 cpml_primitive_get_n_points(const CpmlPrimitive *primitive)
289 return cpml_primitive_type_get_n_points(primitive->data->header.type);
293 * cpml_primitive_get_length:
294 * @primitive: a #CpmlPrimitive
296 * Abstracts the length() family functions by providing a common
297 * way to access the underlying primitive-specific implementation.
298 * The function returns the length of @primitive.
300 * Returns: the requested length or 0 on errors
302 * <!-- Virtual: get_length -->
304 * Since: 1.0
306 double
307 cpml_primitive_get_length(const CpmlPrimitive *primitive)
309 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
311 if (class_data == NULL || class_data->get_length == NULL)
312 return 0;
314 return class_data->get_length(primitive);
318 * cpml_primitive_put_extents:
319 * @primitive: (in): a #CpmlPrimitive
320 * @extents: (out): where to store the extents
322 * Abstracts the extents() family functions by providing a common
323 * way to access the underlying primitive-specific implementation.
325 * This function stores in @extents the bounding box of @primitive.
327 * On errors, that is if the extents cannot be calculated for some
328 * reason, this function does nothing.
330 * <!-- Virtual: put_extents -->
332 * Since: 1.0
334 void
335 cpml_primitive_put_extents(const CpmlPrimitive *primitive,
336 CpmlExtents *extents)
338 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
340 if (class_data == NULL || class_data->put_extents == NULL)
341 return;
343 class_data->put_extents(primitive, extents);
347 * cpml_primitive_set_point:
348 * @primitive: a #CpmlPrimitive
349 * @n_point: the index of the point to retrieve
350 * @pair: the source #CpmlPair
352 * Sets the specified @n_point of @primitive to @pair. The @n_point
353 * index starts from 0: if @n_point is 0, the start point (the origin)
354 * is changed, 1 for the second point and so on. If @n_point is
355 * negative, it is considered as a negative index from the end, so
356 * that -1 is the end point, -2 the point before the end point and
357 * so on.
359 * #CPML_CLOSE is managed in a special way: if @n_point
360 * is -1 or 1 and @primitive is a close-path, this function cycles
361 * the source #CpmlSegment and returns the first point. This is
362 * needed because requesting the end point (or the second point)
363 * of a close path is a valid operation and must returns the origin
364 * of the segment.
366 * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise.
368 * Since: 1.0
371 cpml_primitive_set_point(CpmlPrimitive *primitive,
372 int n_point, const CpmlPair *pair)
374 cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
376 if (point == NULL)
377 return 0;
379 cpml_pair_to_cairo(pair, point);
380 return 1;
384 * cpml_primitive_put_point:
385 * @primitive: a #CpmlPrimitive
386 * @n_point: the index of the point to retrieve
387 * @pair: (out caller-allocates): the destination #CpmlPair
389 * Gets the specified @n_point from @primitive and stores it into
390 * @pair. The @n_point index is subject to the same rules explained
391 * in the cpml_primitive_set_point() function.
393 * Returns: (type gboolean): 1 if the point is found, 0 otherwise.
395 * Since: 1.0
398 cpml_primitive_put_point(const CpmlPrimitive *primitive,
399 int n_point, CpmlPair *pair)
401 const cairo_path_data_t *point = _cpml_get_point(primitive, n_point);
403 if (point == NULL)
404 return 0;
406 cpml_pair_from_cairo(pair, point);
407 return 1;
411 * cpml_primitive_put_pair_at:
412 * @primitive: (in): a #CpmlPrimitive
413 * @pos: (in): the position value
414 * @pair: (out): the destination #CpmlPair
416 * Abstracts the <function>put_pair_at</function> family functions by
417 * providing a common way to access the underlying primitive-specific
418 * implementation.
420 * It gets the coordinates of the point lying on @primitive
421 * at position @pos. @pos is an homogeneous factor where 0 is the
422 * start point, 1 the end point, 0.5 the mid point and so on.
423 * @pos can be less than 0 or greater than 1, in which case the
424 * coordinates of @pair are interpolated.
426 * On errors, that is if the coordinates cannot be calculated for
427 * some reason, this function does nothing.
429 * <!-- Virtual: put_pair_at -->
431 * Since: 1.0
433 void
434 cpml_primitive_put_pair_at(const CpmlPrimitive *primitive,
435 double pos, CpmlPair *pair)
437 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
439 if (class_data == NULL || class_data->put_pair_at == NULL)
440 return;
442 class_data->put_pair_at(primitive, pos, pair);
446 * cpml_primitive_put_vector_at:
447 * @primitive: (in): a #CpmlPrimitive
448 * @pos: (in): the position value
449 * @vector: (out): the destination #CpmlVector
451 * Abstracts the <function>put_vector_at</function> family functions by
452 * providing a common way to access the underlying primitive-specific
453 * implementation.
455 * It gets the steepness of the point at position @pos on @primitive.
456 * @pos is an homogeneous factor where 0 is the start point, 1 the
457 * end point, 0.5 the mid point and so on.
458 * @pos can be less than 0 or greater than 1, in which case the
459 * coordinates of @pair are interpolated.
461 * On errors, that is if the steepness cannot be calculated for
462 * some reason, this function does nothing.
464 * <!-- Virtual: put_vector_at -->
466 * Since: 1.0
468 void
469 cpml_primitive_put_vector_at(const CpmlPrimitive *primitive,
470 double pos, CpmlVector *vector)
472 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
474 if (class_data == NULL || class_data->put_vector_at == NULL)
475 return;
477 class_data->put_vector_at(primitive, pos, vector);
481 * cpml_primitive_is_inside:
482 * @primitive: a #CpmlPrimitive
483 * @pair: the coordinates of the subject point
485 * Checks if @pair is inside the bounding box of @primitive. This
486 * can be useful e.g. to verify if an intersection point is a real
487 * intersection or an hypothetical one.
489 * Returns: 1 if @pair is inside the bounding box of @primitive, 0 otherwise.
491 * Since: 1.0
494 cpml_primitive_is_inside(const CpmlPrimitive *primitive, const CpmlPair *pair)
496 CpmlExtents extents = { 0 };
497 cpml_primitive_put_extents(primitive, &extents);
498 return cpml_extents_pair_is_inside(&extents, pair);
502 * cpml_primitive_get_closest_pos:
503 * @primitive: a #CpmlPrimitive
504 * @pair: the coordinates of the subject point
506 * Returns the pos value of the point on @primitive nearest to @pair.
507 * The returned value is always clamped between 0 and 1.
509 * Returns: the requested pos value between 0 and 1, or -1 on errors.
511 * <!-- Virtual: get_closest_pos -->
513 * Since: 1.0
515 double
516 cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive,
517 const CpmlPair *pair)
519 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
521 if (class_data == NULL || class_data->get_closest_pos == NULL)
522 return -1;
524 return class_data->get_closest_pos(primitive, pair);
528 * cpml_primitive_put_intersections:
529 * @primitive: the first #CpmlPrimitive
530 * @primitive2: the second #CpmlPrimitive
531 * @n_dest: maximum number of intersections to return
532 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
534 * Finds the intersection points between the given primitives and
535 * returns the result in @dest. The size of @dest should be enough
536 * to store @n_dest #CpmlPair. The maximum number of intersections
537 * is dependent on the type of the primitive involved in the
538 * operation. If there are at least one Bézier curve involved, up to
539 * 4 intersections could be returned. Otherwise, if there is an arc
540 * the intersections will be 2 at maximum. For line primitives, there
541 * is only 1 point (or 0 if the lines are parallel).
543 * Also hypothetical intersections are returned, that is intersections
544 * made by extending the primitives outside their bounds. This means e.g.
545 * two lines always return one intersection if they are not parallel. To
546 * discriminate real intersections you should check the returned points
547 * with cpml_primitive_is_inside(), for example:
549 * <informalexample><programlisting language="C">
550 * if (cpml_primitive_put_intersections(line1, line2, 1, &pair) == 0) {
551 * // line1 and line2 are parallels
552 * } else if (cpml_primitive_is_inside(line1, &pair) &&
553 * cpml_primitive_is_inside(line2, &pair)) {
554 * // This is a real intersection
555 * } else {
556 * // This is an hypothetical intersection
558 * </programlisting></informalexample>
560 * <note>
561 * <para>
562 * The convention used by the CPML library is that a primitive should
563 * implement only the intersection algorithms with lower degree
564 * primitives. This is required to avoid code duplication: intersection
565 * between arc and Bézier curves must be implemented by #CPML_CURVE and
566 * intersection between lines and arcs must be implemented by #CPML_ARC.
567 * cpml_primitive_put_intersections() will take care of swapping the
568 * arguments if they are not properly ordered.
569 * </para>
570 * </note>
572 * Returns: the number of intersection points found or 0 if the
573 * primitives do not intersect or on errors
575 * <!-- Virtual: put_intersections -->
577 * Since: 1.0
579 size_t
580 cpml_primitive_put_intersections(const CpmlPrimitive *primitive,
581 const CpmlPrimitive *primitive2,
582 size_t n_dest, CpmlPair *dest)
584 const _CpmlPrimitiveClass *class_data;
585 size_t n_points, n_points2;
587 if (n_dest == 0)
588 return 0;
590 n_points = cpml_primitive_get_n_points(primitive);
591 n_points2 = cpml_primitive_get_n_points(primitive2);
593 /* Check if the primitives can intersect */
594 if (n_points == 0 || n_points2 == 0)
595 return 0;
597 /* Primitives reordering: the first must be the more complex one */
598 if (n_points < n_points2) {
599 return cpml_primitive_put_intersections(primitive2, primitive,
600 n_dest, dest);
603 /* Check if put_intersections is implemented */
604 class_data = _cpml_class_from_obj(primitive);
605 if (class_data == NULL || class_data->put_intersections == NULL)
606 return 0;
608 return class_data->put_intersections(primitive, primitive2, n_dest, dest);
612 * cpml_primitive_put_intersections_with_segment:
613 * @primitive: a #CpmlPrimitive
614 * @segment: a #CpmlSegment
615 * @n_dest: maximum number of intersections to return
616 * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair
618 * Computes the intersections between @segment and @primitive by
619 * sequentially scanning the primitives in @segment and looking
620 * for their intersections with @primitive.
622 * If the intersections are more than @n_dest, only the first
623 * @n_dest pairs are stored.
625 * Beware: this function returns only real intersections. This is in contrast
626 * to cpml_primitive_put_intersections() that, instead, returns all
627 * intersections. Check that function documentation to know what virtual and
628 * real intersections are.
630 * Returns: the number of real intersections found.
632 * Since: 1.0
634 size_t
635 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
636 const CpmlSegment *segment,
637 size_t n_dest, CpmlPair *dest)
639 CpmlPrimitive portion;
640 CpmlPair partial[5];
641 const CpmlPair *pair;
642 size_t found, total;
644 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
645 total = 0;
647 while (total < n_dest) {
648 found = cpml_primitive_put_intersections(&portion, primitive, 5, partial);
650 /* Store only real intersections */
651 for (pair = partial; found; -- found, ++ pair) {
652 if (cpml_primitive_is_inside(&portion, pair) &&
653 cpml_primitive_is_inside(primitive, pair)) {
654 cpml_pair_copy(dest+total, pair);
655 ++ total;
659 if (!cpml_primitive_next(&portion))
660 break;
663 return total;
667 * cpml_primitive_offset:
668 * @primitive: (inout): a #CpmlPrimitive
669 * @offset: (in): distance for the computed offset primitive
671 * Given a primitive, computes the same (or approximated) parallel
672 * primitive distant @offset from the original one and returns
673 * the result by changing @primitive.
675 * On errors, that is if the offset primitive cannot be calculated
676 * for some reason, this function does nothing.
678 * <!-- Virtual: offset -->
680 * Since: 1.0
682 void
683 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
685 const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive);
687 if (class_data == NULL || class_data->offset == NULL)
688 return;
690 class_data->offset(primitive, offset);
694 * cpml_primitive_join:
695 * @primitive: (inout): the first #CpmlPrimitive
696 * @primitive2: (inout): the second #CpmlPrimitive
698 * Joins two primitive modifying the end point of @primitive and the
699 * start point of @primitive2 so that the resulting points will overlap.
701 * <important>
702 * <title>TODO</title>
703 * <itemizedlist>
704 * <listitem>Actually, the join is done by extending the end vector
705 * of @primitive and the start vector of @primitive2 and
706 * interpolating the intersection: this means no primitive
707 * dependent code is needed. Anyway, it is likely to change
708 * in the future because this approach is quite naive when
709 * curves are involved.</listitem>
710 * </itemizedlist>
711 * </important>
713 * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined.
715 * <!-- Virtual: join -->
717 * Since: 1.0
720 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
722 cairo_path_data_t *end1, *start2;
723 CpmlPrimitive line1, line2;
724 cairo_path_data_t data1[2], data2[2];
725 CpmlPair joint;
727 end1 = _cpml_get_point(primitive, -1);
728 start2 = _cpml_get_point(primitive2, 0);
730 /* Check if the primitives are yet connected */
731 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
732 return 1;
734 line1.org = _cpml_get_point(primitive, -2);
735 line1.data = data1;
736 data1[0].header.type = CPML_LINE;
737 data1[1] = *end1;
739 line2.org = start2;
740 line2.data = data2;
741 data2[0].header.type = CPML_LINE;
742 data2[1] = *_cpml_get_point(primitive2, 1);
744 if (!cpml_primitive_put_intersections(&line1, &line2, 1, &joint))
745 return 0;
747 cpml_pair_to_cairo(&joint, end1);
748 cpml_pair_to_cairo(&joint, start2);
750 return 1;
754 * cpml_primitive_to_cairo:
755 * @primitive: (in): a #CpmlPrimitive
756 * @cr: (inout): the destination cairo context
758 * Renders a single @primitive to the @cr cairo context.
759 * As a special case, if the primitive is a #CPML_CLOSE, an
760 * equivalent line is rendered, because a close path left alone
761 * is not renderable.
763 * Also a #CPML_ARC primitive is treated specially, as it is not
764 * natively supported by cairo and has its own rendering API.
766 * Since: 1.0
768 void
769 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
771 CpmlPrimitiveType type;
772 cairo_path_t path;
773 cairo_path_data_t *path_data;
775 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
777 type = primitive->data->header.type;
779 if (type == CPML_CLOSE) {
780 path_data = _cpml_get_point(primitive, -1);
781 cairo_line_to(cr, path_data->point.x, path_data->point.y);
782 } else if (type == CPML_ARC) {
783 cpml_arc_to_cairo(primitive, cr);
784 } else {
785 path.status = CAIRO_STATUS_SUCCESS;
786 path.data = primitive->data;
787 path.num_data = primitive->data->header.length;
788 cairo_append_path(cr, &path);
793 * cpml_primitive_dump:
794 * @primitive: a #CpmlPrimitive
795 * @org_also: (type boolean): whether to output also the origin coordinates
797 * Dumps info on the specified @primitive to stdout: useful for
798 * debugging purposes. If @org_also is 1, a #CPML_MOVE to the
799 * origin is prepended to the data otherwise the
800 * <structfield>org</structfield> field is not used.
802 * Since: 1.0
804 void
805 cpml_primitive_dump(const CpmlPrimitive *primitive, int org_also)
807 const cairo_path_data_t *data;
808 int type;
809 const _CpmlPrimitiveClass *class_data;
810 size_t n, n_points;
812 data = primitive->data;
813 type = data->header.type;
814 class_data = _cpml_class_from_type(type);
816 if (class_data == NULL) {
817 printf("Unknown primitive type (%d)\n", type);
818 return;
821 /* Dump the origin, if requested */
822 if (org_also) {
823 printf("move to ");
824 _cpml_dump_point(primitive->org);
825 printf("\n");
828 printf("%s ", class_data->name);
830 n_points = cpml_primitive_get_n_points(primitive);
831 for (n = 1; n < n_points; ++n)
832 _cpml_dump_point(_cpml_get_point(primitive, n));
834 printf("\n");
837 static const _CpmlPrimitiveClass *
838 _cpml_class_from_type(CpmlPrimitiveType type)
840 static const _CpmlPrimitiveClass *table[CPML_MAX + 1] = { NULL };
842 if (table[CPML_LINE] == NULL) {
843 table[CPML_LINE] = _cpml_line_get_class();
844 table[CPML_ARC] = _cpml_arc_get_class();
845 table[CPML_CURVE] = _cpml_curve_get_class();
846 table[CPML_CLOSE] = _cpml_close_get_class();
849 return (int) type <= (int) CPML_MAX ? table[type] : NULL;
852 static const _CpmlPrimitiveClass *
853 _cpml_class_from_obj(const CpmlPrimitive *primitive)
855 return _cpml_class_from_type(primitive->data->header.type);
859 * _cpml_get_point:
860 * @primitive: a #CpmlPrimitive
861 * @n_point: the index of the point to retrieve
863 * Gets the specified @n_point from @primitive. The index starts
864 * at 0: if @n_point is 0, the start point (the origin) is
865 * returned, 1 for the second point and so on. If @n_point is
866 * negative, it is considered as a negative index from the end,
867 * so that -1 is the end point, -2 the point before the end point
868 * and so on.
870 * #CPML_CLOSE is managed in a special way: if @n_point
871 * is -1 or 1 and @primitive is a close-path, this function cycles
872 * the source #CpmlSegment and returns the first point. This is
873 * needed because requesting the end point (or the second point)
874 * of a close path is a valid operation and must returns the start
875 * of the segment.
877 * Returns: a pointer to the requested point (in cairo format)
878 * or <constant>NULL</constant> if the point is outside
879 * the valid range.
881 * Since: 1.0
883 static cairo_path_data_t *
884 _cpml_get_point(const CpmlPrimitive *primitive, int n_point)
886 size_t n_points;
888 /* For a start point request, simply return the origin
889 * without further checking */
890 if (n_point == 0)
891 return primitive->org;
893 /* The CPML_CLOSE special case */
894 if (primitive->data->header.type == CPML_CLOSE &&
895 (n_point == 1 || n_point == -1))
896 return &primitive->segment->data[1];
898 n_points = cpml_primitive_get_n_points(primitive);
899 if (n_points == 0)
900 return NULL;
902 /* If n_point is negative, consider it as a negative index from the end */
903 if (n_point < 0)
904 n_point = n_points + n_point;
906 /* Out of range condition */
907 if (n_point < 0 || n_point >= n_points)
908 return NULL;
910 return n_point == 0 ? primitive->org : &primitive->data[n_point];
913 static void
914 _cpml_dump_point(const cairo_path_data_t *path_data)
916 printf("(%g %g) ", path_data->point.x, path_data->point.y);