[CPML] Changed n_points type from int to size_t
[adg.git] / cpml / cpml-primitive.c
blob8cb410c6b8af832dadf1ca03ed794030c1a65c14
1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2008, 2009 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 %CAIRO_PATH_ARC_TO type (check #CpmlPrimitiveType
30 * for further information) and without %CAIRO_PATH_MOVE_TO 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.
33 **/
35 /**
36 * CpmlPrimitiveType:
38 * This is another name for #cairo_path_data_type_t type. Although
39 * phisically they are the same struct, #CpmlPrimitiveType conceptually
40 * embodies an important difference: it can be used to specify the
41 * special %CAIRO_PATH_ARC_TO primitive. This is not a native cairo
42 * primitive and having two different types is a good way to make clear
43 * when a function expect or not embedded arc-to primitives.
44 **/
46 /**
47 * CpmlPrimitive:
48 * @segment: the source #CpmlSegment
49 * @org: a pointer to the first point of the primitive
50 * @data: the array of the path data, prepended by the header
52 * As for #CpmlSegment, also the primitive is unobtrusive. This
53 * means CpmlPrimitive does not include any coordinates but instead
54 * keeps pointers to the original segment (and, by transition, to
55 * the underlying #CpmlPath struct).
56 **/
59 #include "cpml-internal.h"
60 #include "cpml-extents.h"
61 #include "cpml-segment.h"
62 #include "cpml-primitive.h"
63 #include "cpml-primitive-private.h"
64 #include "cpml-line.h"
65 #include "cpml-arc.h"
66 #include "cpml-curve.h"
67 #include "cpml-close.h"
68 #include <stdlib.h>
69 #include <string.h>
70 #include <stdio.h>
73 static const _CpmlPrimitiveClass *
74 get_class_from_type (CpmlPrimitiveType type);
75 static const _CpmlPrimitiveClass *
76 get_class (const CpmlPrimitive *primitive);
77 static void dump_cairo_point (const cairo_path_data_t *path_data);
80 /**
81 * cpml_primitive_copy:
82 * @primitive: the destination #CpmlPrimitive
83 * @src: the source #CpmlPrimitive
85 * Copies @src in @primitive. This is a shallow copy: the internal fields
86 * of @primitive refer to the same memory as the original @src primitive.
88 * Returns: @primitive
89 **/
90 CpmlPrimitive *
91 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
93 return memcpy(primitive, src, sizeof(CpmlPrimitive));
96 /**
97 * cpml_primitive_from_segment:
98 * @primitive: the destination #CpmlPrimitive struct
99 * @segment: the source segment
101 * Initializes @primitive to the first primitive of @segment.
103 * Returns: @primitive
105 CpmlPrimitive *
106 cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment)
108 primitive->segment = segment;
110 /* The first element of a CpmlSegment is always a CAIRO_PATH_MOVE_TO,
111 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
112 * so the origin is in the second data item */
113 primitive->org = &segment->data[1];
115 /* Also, the segment APIs ensure that @segment is prepended by
116 * only one CAIRO_PATH_MOVE_TO */
117 primitive->data = segment->data + segment->data[0].header.length;
119 return primitive;
123 * cpml_primitive_reset:
124 * @primitive: a #CpmlPrimitive
126 * Resets @primitive so it refers to the first primitive of the
127 * source segment.
129 void
130 cpml_primitive_reset(CpmlPrimitive *primitive)
132 cpml_primitive_from_segment(primitive, primitive->segment);
136 * cpml_primitive_next:
137 * @primitive: a #CpmlPrimitive
140 * Changes @primitive so it refers to the next primitive on the
141 * source segment. If there are no more primitives, @primitive is
142 * not changed and 0 is returned.
144 * Returns: 1 on success, 0 if no next primitive found or errors
146 cairo_bool_t
147 cpml_primitive_next(CpmlPrimitive *primitive)
149 cairo_path_data_t *new_data;
151 new_data = primitive->data + primitive->data->header.length;
152 if (new_data - primitive->segment->data >= primitive->segment->num_data)
153 return 0;
155 /* TODO: this is a temporary workaround to be removed as soon as
156 * the issue #21 will be resolved */
157 if (new_data->header.type == CAIRO_PATH_MOVE_TO)
158 return 0;
160 primitive->org = cpml_primitive_get_point(primitive, -1);
161 primitive->data = new_data;
163 return 1;
167 * cpml_primitive_get_n_points:
168 * @primitive: a #CpmlPrimitive
170 * Gets the number of points required to identify @primitive.
171 * It is similar to cpml_primitive_type_get_n_points() but using
172 * a @primitive instance instead of a type.
174 * Returns: the number of points or %0 on errors
176 size_t
177 cpml_primitive_get_n_points(const CpmlPrimitive *primitive)
179 return cpml_primitive_type_get_n_points(primitive->data->header.type);
183 * cpml_primitive_get_point:
184 * @primitive: a #CpmlPrimitive
185 * @n_point: the index of the point to retrieve
187 * Gets the specified @n_point from @primitive. The index starts
188 * at 0: if @n_point is 0, the start point (the origin) is
189 * returned, 1 for the second point and so on. If @n_point is
190 * negative, it is considered as a negative index from the end,
191 * so that -1 is the end point, -2 the point before the end point
192 * and so on.
194 * %CAIRO_PATH_CLOSE_PATH is managed in a special way: if @n_point
195 * is -1 or 1 and @primitive is a close-path, this function cycles
196 * the source #CpmlSegment and returns the first point. This is
197 * needed because requesting the end point (or the second point)
198 * of a close path is a valid operation and must returns the start
199 * of the segment.
201 * Returns: a pointer to the requested point (in cairo format)
202 * or %NULL if the point is outside the valid range
204 cairo_path_data_t *
205 cpml_primitive_get_point(const CpmlPrimitive *primitive, int n_point)
207 size_t n_points;
209 /* For a start point request, simply return the origin
210 * without further checking */
211 if (n_point == 0)
212 return primitive->org;
214 /* The CAIRO_PATH_CLOSE_PATH special case */
215 if (primitive->data->header.type == CAIRO_PATH_CLOSE_PATH &&
216 (n_point == 1 || n_point == -1))
217 return &primitive->segment->data[1];
219 n_points = cpml_primitive_get_n_points(primitive);
220 if (n_points == 0)
221 return NULL;
223 /* If n_point is negative, consider it as a negative index from the end */
224 if (n_point < 0)
225 n_point = n_points + n_point;
227 /* Out of range condition */
228 if (n_point < 0 || n_point >= n_points)
229 return NULL;
231 return n_point == 0 ? primitive->org : &primitive->data[n_point];
235 * cpml_primitive_to_cairo:
236 * @primitive: a #CpmlPrimitive
237 * @cr: the destination cairo context
239 * Renders a single @primitive to the @cr cairo context.
240 * As a special case, if the primitive is a #CAIRO_PATH_CLOSE_PATH,
241 * an equivalent line is rendered, because a close path left alone
242 * is not renderable.
244 * Also a #CAIRO_PATH_ARC_TO primitive is treated specially, as it
245 * is not natively supported by cairo and has its own rendering API.
247 void
248 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
250 cairo_path_t path;
251 cairo_path_data_t *path_data;
253 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
255 switch (primitive->data->header.type) {
257 case CAIRO_PATH_CLOSE_PATH:
258 path_data = cpml_primitive_get_point(primitive, -1);
259 cairo_line_to(cr, path_data->point.x, path_data->point.y);
260 break;
262 case CAIRO_PATH_ARC_TO:
263 cpml_arc_to_cairo(primitive, cr);
264 break;
266 default:
267 path.status = CAIRO_STATUS_SUCCESS;
268 path.data = primitive->data;
269 path.num_data = primitive->data->header.length;
270 cairo_append_path(cr, &path);
271 break;
276 * cpml_primitive_dump:
277 * @primitive: a #CpmlPrimitive
278 * @org_also: whether to output also the origin coordinates
280 * Dumps info on the specified @primitive to stdout: useful for
281 * debugging purposes. If @org_also is 1, a %CAIRO_PATH_MOVE_TO
282 * to the origin is prepended to the data otherwise the
283 * <structfield>org</structfield> field is not used.
285 void
286 cpml_primitive_dump(const CpmlPrimitive *primitive, cairo_bool_t org_also)
288 const cairo_path_data_t *data;
289 int type;
290 size_t n, n_points;
292 data = primitive->data;
293 type = data->header.type;
294 n_points = cpml_primitive_get_n_points(primitive);
295 if (n_points == 0) {
296 printf("Unhandled primitive type (%d)\n", type);
297 return;
300 /* Dump the origin movement, if requested */
301 if (org_also) {
302 printf("Move to ");
303 dump_cairo_point(primitive->org);
304 printf("\n");
307 switch (type) {
309 case CAIRO_PATH_LINE_TO:
310 printf("Line to ");
311 break;
313 case CAIRO_PATH_ARC_TO:
314 printf("Arc to ");
315 break;
317 case CAIRO_PATH_CURVE_TO:
318 printf("Curve to ");
319 break;
321 case CAIRO_PATH_CLOSE_PATH:
322 printf("Path close");
323 break;
325 default:
326 printf("Unknown primitive (type = %d)", type);
327 break;
330 for (n = 1; n < n_points; ++n)
331 dump_cairo_point(cpml_primitive_get_point(primitive, n));
333 printf("\n");
337 * cpml_primitive_put_intersections_with_segment:
338 * @primitive: a #CpmlPrimitive
339 * @segment: a #CpmlSegment
340 * @dest: the destination vector of #CpmlPair
341 * @max: maximum number of intersections to return
343 * Computes the intersections between @segment and @primitive by
344 * sequentially scanning the primitives in @segment and looking
345 * for intersections with @primitive.
346 * If the intersections are more than @max, only the first @max pairs
347 * are stored in @dest.
349 * Returns: the number of intersections found
352 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
353 const CpmlSegment *segment,
354 CpmlPair *dest, int max)
356 CpmlPrimitive portion;
357 int found;
359 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
360 found = 0;
362 while (found < max) {
363 found += cpml_primitive_put_intersections(&portion, primitive,
364 max-found, dest+found);
365 if (!cpml_primitive_next(&portion))
366 break;
369 return found;
374 * cpml_primitive_type_get_n_points:
375 * @type: a primitive type
377 * Gets the number of points required to identify the @type primitive.
379 * <note><para>
380 * This function is primitive dependent, that is every primitive has
381 * its own implementation.
382 * </para></note>
384 * Returns: the number of points or %0 on errors
386 size_t
387 cpml_primitive_type_get_n_points(CpmlPrimitiveType type)
389 const _CpmlPrimitiveClass *class_data = get_class_from_type(type);
391 if (class_data == NULL)
392 return 0;
394 return class_data->n_points;
398 * cpml_primitive_get_length:
399 * @primitive: a #CpmlPrimitive
401 * Abstracts the length() family functions by providing a common
402 * way to access the underlying primitive-specific implementation.
403 * The function returns the length of @primitive.
405 * Returns: the requested length or 0 on errors
407 double
408 cpml_primitive_get_length(const CpmlPrimitive *primitive)
410 const _CpmlPrimitiveClass *class_data = get_class(primitive);
412 if (class_data == NULL || class_data->get_length == NULL)
413 return 0;
415 return class_data->get_length(primitive);
419 * cpml_primitive_put_extents:
420 * @primitive: a #CpmlPrimitive
421 * @extents: where to store the extents
423 * Abstracts the extents() family functions by providing a common
424 * way to access the underlying primitive-specific implementation.
426 * This function stores in @extents the bounding box of @primitive.
428 * On errors, that is if the extents cannot be calculated for some
429 * reason, this function does nothing.
431 void
432 cpml_primitive_put_extents(const CpmlPrimitive *primitive,
433 CpmlExtents *extents)
435 const _CpmlPrimitiveClass *class_data = get_class(primitive);
437 if (class_data == NULL || class_data->put_extents == NULL)
438 return;
440 class_data->put_extents(primitive, extents);
444 * cpml_primitive_put_pair_at:
445 * @primitive: a #CpmlPrimitive
446 * @pos: the position value
447 * @pair: the destination #CpmlPair
449 * Abstracts the put_pair_at() family functions by providing a common
450 * way to access the underlying primitive-specific implementation.
452 * It gets the coordinates of the point lying on @primitive
453 * at position @pos. @pos is an homogeneous factor where 0 is the
454 * start point, 1 the end point, 0.5 the mid point and so on.
455 * @pos can be less than 0 or greater than %1, in which case the
456 * coordinates of @pair are interpolated.
458 * On errors, that is if the coordinates cannot be calculated for
459 * some reason, this function does nothing.
461 void
462 cpml_primitive_put_pair_at(const CpmlPrimitive *primitive, double pos,
463 CpmlPair *pair)
465 const _CpmlPrimitiveClass *class_data = get_class(primitive);
467 if (class_data == NULL || class_data->put_pair_at == NULL)
468 return;
470 class_data->put_pair_at(primitive, pos, pair);
474 * cpml_primitive_put_vector_at:
475 * @primitive: a #CpmlPrimitive
476 * @pos: the position value
477 * @vector: the destination #CpmlVector
479 * Abstracts the put_vector_at() family functions by providing a common
480 * way to access the underlying primitive-specific implementation.
482 * It gets the steepness of the point at position @pos on @primitive.
483 * @pos is an homogeneous factor where 0 is the start point, 1 the
484 * end point, 0.5 the mid point and so on.
485 * @pos can be less than 0 or greater than %1, in which case the
486 * coordinates of @pair are interpolated.
488 * On errors, that is if the steepness cannot be calculated for
489 * some reason, this function does nothing.
491 void
492 cpml_primitive_put_vector_at(const CpmlPrimitive *primitive, double pos,
493 CpmlVector *vector)
495 const _CpmlPrimitiveClass *class_data = get_class(primitive);
497 if (class_data == NULL || class_data->put_vector_at == NULL)
498 return;
500 class_data->put_vector_at(primitive, pos, vector);
504 * cpml_primitive_get_closest_pos:
505 * @primitive: a #CpmlPrimitive
506 * @pair: the coordinates of the subject point
508 * Returns the pos value of the point on @primitive nearest to @pair.
509 * The returned value is always clamped between %0 and %1.
511 * Returns: the requested pos value between %0 and %1, or %-1 on errors
513 double
514 cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive,
515 const CpmlPair *pair)
517 const _CpmlPrimitiveClass *class_data = get_class(primitive);
519 if (class_data == NULL || class_data->get_closest_pos == NULL)
520 return -1;
522 return class_data->get_closest_pos(primitive, pair);
526 * cpml_primitive_join:
527 * @primitive: the first #CpmlPrimitive
528 * @primitive2: the second #CpmlPrimitive
530 * Joins two primitive modifying the end point of @primitive and the
531 * start point of @primitive2 so that the resulting points will overlap.
533 * <important>
534 * <title>TODO</title>
535 * <itemizedlist>
536 * <listitem>Actually, the join is done by extending the end vector
537 * of @primitive and the start vector of @primitive2 and
538 * interpolating the intersection: this means no primitive
539 * dependent code is needed. Anyway, it is likely to change
540 * in the future because this approach is quite naive when
541 * curves are involved.</listitem>
542 * </itemizedlist>
543 * </important>
545 * Returns: 1 on success, 0 if the end vector of @primitive
546 * and the start vector of @primitive2 are parallel
548 cairo_bool_t
549 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
551 cairo_path_data_t *end1, *start2;
552 CpmlPrimitive line1, line2;
553 cairo_path_data_t data1[2], data2[2];
554 CpmlPair joint;
556 end1 = cpml_primitive_get_point(primitive, -1);
557 start2 = cpml_primitive_get_point(primitive2, 0);
559 /* Check if the primitives are yet connected */
560 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
561 return 1;
563 line1.org = cpml_primitive_get_point(primitive, -2);
564 line1.data = data1;
565 data1[0].header.type = CAIRO_PATH_LINE_TO;
566 data1[1] = *end1;
568 line2.org = start2;
569 line2.data = data2;
570 data2[0].header.type = CAIRO_PATH_LINE_TO;
571 data2[1] = *cpml_primitive_get_point(primitive2, 1);
573 if (!cpml_line_put_intersections(&line1, &line2, 1, &joint))
574 return 0;
576 cpml_pair_to_cairo(&joint, end1);
577 cpml_pair_to_cairo(&joint, start2);
579 return 1;
583 * cpml_primitive_put_intersections:
584 * @primitive: the first #CpmlPrimitive
585 * @primitive2: the second #CpmlPrimitive
586 * @max: maximum number of intersections to return
587 * @dest: the destination buffer that can contain @max #CpmlPair
589 * Finds the intersection points between the given primitives and
590 * returns the result in @dest. The size of @dest should be enough
591 * to store @max #CpmlPair. The absoulte max number of intersections
592 * is dependent from the type of the primitive involved in the
593 * operation. If there are at least one Bézier curve involved, up to
594 * 4 intersections could be returned. Otherwise, if there is an arc
595 * the intersections will be 2 at maximum. For line line primitives,
596 * there is only 1 point (or obviously 0 if the lines do not intersect).
598 * <note><para>
599 * This function is primitive dependent: every new primitive must
600 * expose API to get intersections with any other primitive type
601 * (excluding %CAIRO_PATH_CLOSE_PATH, as it is converted to a line
602 * primitive).</para>
603 * <para>The convention used by CPML is that a primitive should
604 * expose only intersection APIs dealing with lower complexity
605 * primitives. This is required to avoid double functions:
606 * you will have only a cpml_curve_put_intersections_with_line()
607 * function, not a cpml_line_put_intersections_with_curve(), as
608 * the latter is easily reproduced by calling the former with
609 * @primitive2 and @primitive swapped.
610 * </para></note>
612 * Returns: the number of intersection points found or 0 if the
613 * primitives do not intersect
616 cpml_primitive_put_intersections(const CpmlPrimitive *primitive,
617 const CpmlPrimitive *primitive2,
618 int max, CpmlPair *dest)
620 CpmlPrimitiveType type1, type2;
622 type1 = primitive->data->header.type;
623 type2 = primitive->data->header.type;
625 /* Close path primitives are treated as line-to */
626 if (type1 == CAIRO_PATH_CLOSE_PATH)
627 type1 = CAIRO_PATH_LINE_TO;
628 if (type2 == CAIRO_PATH_CLOSE_PATH)
629 type2 = CAIRO_PATH_LINE_TO;
631 /* Order the two primitives in ascending complexity, to facilitate
632 * the dispatcher logic */
633 if (cpml_primitive_type_get_n_points(type1) > cpml_primitive_type_get_n_points(type2)) {
634 const CpmlPrimitive *tmp_primitive;
635 CpmlPrimitiveType tmp_type;
637 tmp_type = type1;
638 tmp_primitive = primitive;
640 type1 = type2;
641 primitive = primitive2;
643 type2 = tmp_type;
644 primitive2 = tmp_primitive;
647 /* Dispatcher: probably there's a smarter way to do this */
648 switch (type1) {
650 case CAIRO_PATH_LINE_TO:
651 if (type2 == CAIRO_PATH_LINE_TO)
652 return cpml_line_put_intersections(primitive2, primitive,
653 max, dest);
654 else if (type2 == CAIRO_PATH_ARC_TO)
655 return cpml_arc_put_intersections_with_line(primitive2, primitive,
656 max, dest);
657 else if (type2 == CAIRO_PATH_CURVE_TO)
658 return cpml_curve_put_intersections_with_line(primitive2, primitive,
659 max, dest);
660 break;
662 case CAIRO_PATH_ARC_TO:
663 if (type2 == CAIRO_PATH_ARC_TO)
664 return cpml_arc_put_intersections(primitive2, primitive,
665 max, dest);
666 else if (type2 == CAIRO_PATH_CURVE_TO)
667 return cpml_curve_put_intersections_with_arc(primitive2, primitive,
668 max, dest);
669 break;
671 case CAIRO_PATH_CURVE_TO:
672 if (type2 == CAIRO_PATH_CURVE_TO)
673 return cpml_curve_put_intersections(primitive2, primitive,
674 max, dest);
675 break;
677 default:
678 break;
681 /* Primitive combination not found */
682 return 0;
686 * cpml_primitive_offset:
687 * @primitive: a #CpmlPrimitive
688 * @offset: distance for the computed offset primitive
690 * Given a primitive, computes the same (or approximated) parallel
691 * primitive distant @offset from the original one and returns
692 * the result by changing @primitive.
694 * <note><para>
695 * This function is primitive dependent, that is every primitive has
696 * its own implementation.
697 * </para></note>
699 void
700 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
702 switch (primitive->data->header.type) {
704 case CAIRO_PATH_LINE_TO:
705 cpml_line_offset(primitive, offset);
706 break;
708 case CAIRO_PATH_ARC_TO:
709 cpml_arc_offset(primitive, offset);
710 break;
712 case CAIRO_PATH_CURVE_TO:
713 cpml_curve_offset(primitive, offset);
714 break;
716 case CAIRO_PATH_CLOSE_PATH:
717 cpml_close_offset(primitive, offset);
718 break;
720 default:
721 break;
726 static const _CpmlPrimitiveClass *
727 get_class_from_type(CpmlPrimitiveType type)
729 switch (type) {
730 case CAIRO_PATH_LINE_TO:
731 return _cpml_line_get_class();
732 case CAIRO_PATH_ARC_TO:
733 return _cpml_arc_get_class();
734 case CAIRO_PATH_CURVE_TO:
735 return _cpml_curve_get_class();
736 case CAIRO_PATH_CLOSE_PATH:
737 return _cpml_close_get_class();
738 default:
739 break;
742 return NULL;
745 static const _CpmlPrimitiveClass *
746 get_class(const CpmlPrimitive *primitive)
748 return get_class_from_type(primitive->data->header.type);
751 static void
752 dump_cairo_point(const cairo_path_data_t *path_data)
754 printf("(%g %g) ", path_data->point.x, path_data->point.y);