doc: update copyright line for 2021
[adg.git] / src / cpml / cpml-primitive.c
blob48b20ba3d386ed12cd878d5b19d1d197a436d0dc
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.
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:
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().
55 * Since: 1.0
56 **/
58 /**
59 * CPML_MOVE:
61 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_MOVE_TO.
63 * Since: 1.0
64 **/
66 /**
67 * CPML_LINE:
69 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_LINE_TO.
71 * Since: 1.0
72 **/
74 /**
75 * CPML_CURVE:
77 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CURVE_TO.
79 * Since: 1.0
80 **/
82 /**
83 * CPML_CLOSE:
85 * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CLOSE_PATH.
87 * Since: 1.0
88 **/
90 /**
91 * CPML_ARC:
93 * A #CpmlPrimitiveType value that represents an arc representation at
94 * CPML level.
96 * Since: 1.0
97 **/
99 /**
100 * CpmlPrimitive:
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).
110 * Since: 1.0
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"
121 #include <string.h>
122 #include <stdio.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,
135 int n_point);
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
147 * Since: 1.0
149 size_t
150 cpml_primitive_type_get_n_points(CpmlPrimitiveType type)
152 const _CpmlPrimitiveClass *class_data = _cpml_class_from_type(type);
154 if (class_data == NULL)
155 return 0;
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.
172 * Since: 1.0
174 CpmlPrimitiveType
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.
187 * Since: 1.0
189 void
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.
212 * Since: 1.0
214 void
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
240 * ...
241 * // Let's restore &primitive original points
242 * cpml_primitive_copy_data(&primitive, backup);
243 * g_free(backup);
244 * </programlisting></informalexample>
246 * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors.
248 * Since: 1.0
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)
255 return 0;
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);
260 return 1;
264 * cpml_primitive_reset:
265 * @primitive: (inout): a #CpmlPrimitive
267 * Resets @primitive so it refers to the first primitive of the
268 * source segment.
270 * Since: 1.0
272 void
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.
288 * Since: 1.0
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)
300 return 0;
302 primitive->org = _cpml_get_point(primitive, -1);
303 primitive->data = new_data;
305 return 1;
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 -->
320 * Since: 1.0
322 size_t
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 -->
340 * Since: 1.0
342 double
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)
348 return 0;
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 -->
368 * Since: 1.0
370 void
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)
377 return;
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
393 * so on.
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
400 * of the segment.
402 * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise.
404 * Since: 1.0
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);
412 if (point == NULL)
413 return 0;
415 cpml_pair_to_cairo(pair, point);
416 return 1;
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.
431 * Since: 1.0
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);
439 if (point == NULL)
440 return 0;
442 cpml_pair_from_cairo(pair, point);
443 return 1;
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
454 * implementation.
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 -->
467 * Since: 1.0
469 void
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)
476 return;
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
489 * implementation.
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 -->
502 * Since: 1.0
504 void
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)
511 return;
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.
527 * Since: 1.0
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 -->
549 * Since: 1.0
551 double
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)
558 return -1;
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
591 * } else {
592 * // This is an hypothetical intersection
594 * </programlisting></informalexample>
596 * <note>
597 * <para>
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.
605 * </para>
606 * </note>
608 * Returns: the number of intersection points found or 0 if the
609 * primitives do not intersect or on errors
611 * <!-- Virtual: put_intersections -->
613 * Since: 1.0
615 size_t
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;
623 if (n_dest == 0)
624 return 0;
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)
631 return 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,
636 n_dest, dest);
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)
642 return 0;
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.
668 * Since: 1.0
670 size_t
671 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
672 const CpmlSegment *segment,
673 size_t n_dest, CpmlPair *dest)
675 CpmlPrimitive portion;
676 CpmlPair partial[5];
677 const CpmlPair *pair;
678 size_t found, total;
680 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
681 total = 0;
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);
691 ++ total;
695 if (!cpml_primitive_next(&portion))
696 break;
699 return total;
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 -->
716 * Since: 1.0
718 void
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)
724 return;
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.
737 * <important>
738 * <title>TODO</title>
739 * <itemizedlist>
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>
746 * </itemizedlist>
747 * </important>
749 * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined.
751 * <!-- Virtual: join -->
753 * Since: 1.0
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];
761 CpmlPair joint;
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)
768 return 1;
770 line1.org = _cpml_get_point(primitive, -2);
771 line1.data = data1;
772 data1[0].header.type = CPML_LINE;
773 data1[1] = *end1;
775 line2.org = start2;
776 line2.data = data2;
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))
781 return 0;
783 cpml_pair_to_cairo(&joint, end1);
784 cpml_pair_to_cairo(&joint, start2);
786 return 1;
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
797 * is not renderable.
799 * Also a %CPML_ARC primitive is treated specially, as it is not
800 * natively supported by cairo and has its own rendering API.
802 * Since: 1.0
804 void
805 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
807 CpmlPrimitiveType type;
808 cairo_path_t path;
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);
820 } else {
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.
838 * Since: 1.0
840 void
841 cpml_primitive_dump(const CpmlPrimitive *primitive, int org_also)
843 const cairo_path_data_t *data;
844 int type;
845 const _CpmlPrimitiveClass *class_data;
846 size_t n, n_points;
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);
854 return;
857 /* Dump the origin, if requested */
858 if (org_also) {
859 printf("move to ");
860 _cpml_dump_point(primitive->org);
861 printf("\n");
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));
870 printf("\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);
895 * _cpml_get_point:
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
904 * and so on.
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
911 * of the segment.
913 * Returns: a pointer to the requested point (in cairo format)
914 * or <constant>NULL</constant> if the point is outside
915 * the valid range.
917 * Since: 1.0
919 static cairo_path_data_t *
920 _cpml_get_point(const CpmlPrimitive *primitive, int n_point)
922 size_t n_points;
924 /* For a start point request, simply return the origin
925 * without further checking */
926 if (n_point == 0)
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);
935 if (n_points == 0)
936 return NULL;
938 /* If n_point is negative, consider it as a negative index from the end */
939 if (n_point < 0)
940 n_point = n_points + n_point;
942 /* Out of range condition */
943 if (n_point < 0 || n_point >= n_points)
944 return NULL;
946 return n_point == 0 ? primitive->org : &primitive->data[n_point];
949 static void
950 _cpml_dump_point(const cairo_path_data_t *path_data)
952 printf("(%g %g) ", path_data->point.x, path_data->point.y);