[docs] Added missing definitions
[adg.git] / src / cpml / cpml-primitive.c
blob82c0c80882e01d4bd6f91b48ef60aecb524d6129
1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2008,2009,2010,2011 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.
33 **/
35 /**
36 * CpmlPrimitiveType:
38 * This is a type compatible with #cairo_path_data_type_t type. It is
39 * basically the same enum but it embodies an important difference:
40 * it can be used to specify the special #CPML_ARC primitive. This is
41 * not a native cairo primitive and having two different types is a
42 * good way to make clear when a function expect or not embedded
43 * #CPML_ARC 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 **/
58 /**
59 * CPML_MOVE:
61 * A constant that resolves to %CAIRO_PATH_MOVE_TO. It is wrapped by
62 * the CPML library for consistency with #CPML_ARC.
63 **/
65 /**
66 * CPML_LINE:
68 * A constant that resolves to %CAIRO_PATH_LINE_TO. It is wrapped by
69 * the CPML library for consistency with #CPML_ARC.
70 **/
72 /**
73 * CPML_CURVE:
75 * A constant that resolves to %CAIRO_PATH_CURVE_TO. It is wrapped by
76 * the CPML library for consistency with #CPML_ARC.
77 **/
79 /**
80 * CPML_CLOSE:
82 * A constant that resolves to %CAIRO_PATH_CLOSE_PATH. It is wrapped by
83 * the CPML library for consistency with #CPML_ARC.
84 **/
86 /**
87 * CPML_ARC:
89 * Actually, at least up to version 1.10.2, the cairo library does not
90 * support arc primitives natively. Furthermore, there is no plan they
91 * will be ever supported.
93 * Arcs are used extensively in technical drawing: some operations are
94 * trivials with arcs and a nightmare with cubic Bézier curves. The
95 * CPML library provides native arc support, converting them to curves
96 * when the #CpmlSegment is returned to the cairo context, for instance
97 * when using cpml_segment_to_cairo().
98 **/
101 #include "cpml-internal.h"
102 #include "cpml-extents.h"
103 #include "cpml-segment.h"
104 #include "cpml-primitive.h"
105 #include "cpml-primitive-private.h"
106 #include "cpml-arc.h"
107 #include "cpml-curve.h"
108 #include <string.h>
109 #include <stdio.h>
112 static const _CpmlPrimitiveClass *
113 get_class_from_type (CpmlPrimitiveType type);
114 static const _CpmlPrimitiveClass *
115 get_class (const CpmlPrimitive *primitive);
116 static void dump_cairo_point (const cairo_path_data_t *path_data);
120 * cpml_primitive_type_get_n_points:
121 * @type: a primitive type
123 * Gets the number of points required to identify the @type primitive.
125 * Returns: the number of points or %0 on errors
127 size_t
128 cpml_primitive_type_get_n_points(CpmlPrimitiveType type)
130 const _CpmlPrimitiveClass *class_data = get_class_from_type(type);
132 if (class_data == NULL)
133 return 0;
135 return class_data->n_points;
139 * cpml_primitive_from_segment:
140 * @primitive: the destination #CpmlPrimitive struct
141 * @segment: the source segment
143 * Initializes @primitive to the first primitive of @segment.
145 void
146 cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment)
148 primitive->segment = segment;
150 /* The first element of a CpmlSegment is always a CPML_MOVE,
151 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
152 * so the origin is in the second data item */
153 primitive->org = segment->data + 1;
155 /* Also, the segment APIs ensure that @segment is prepended by
156 * only one CPML_MOVE */
157 primitive->data = segment->data + segment->data->header.length;
161 * cpml_primitive_copy:
162 * @primitive: the destination #CpmlPrimitive
163 * @src: the source #CpmlPrimitive
165 * Copies @src in @primitive. This is a shallow copy: the internal fields
166 * of @primitive refer to the same memory as the original @src primitive.
168 void
169 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
171 memcpy(primitive, src, sizeof(CpmlPrimitive));
175 * cpml_primitive_reset:
176 * @primitive: a #CpmlPrimitive
178 * Resets @primitive so it refers to the first primitive of the
179 * source segment.
181 void
182 cpml_primitive_reset(CpmlPrimitive *primitive)
184 cpml_primitive_from_segment(primitive, primitive->segment);
188 * cpml_primitive_next:
189 * @primitive: a #CpmlPrimitive
192 * Changes @primitive so it refers to the next primitive on the
193 * source segment. If there are no more primitives, @primitive is
194 * not changed and 0 is returned.
196 * Returns: 1 on success, 0 if no next primitive found or errors
198 cairo_bool_t
199 cpml_primitive_next(CpmlPrimitive *primitive)
201 cairo_path_data_t *new_data;
202 const cairo_path_data_t *end_data;
204 new_data = primitive->data + primitive->data->header.length;
205 end_data = primitive->segment->data + primitive->segment->num_data;
207 if (new_data >= end_data)
208 return 0;
210 primitive->org = cpml_primitive_get_point(primitive, -1);
211 primitive->data = new_data;
213 return 1;
217 * cpml_primitive_get_n_points:
218 * @primitive: a #CpmlPrimitive
220 * Gets the number of points required to identify @primitive.
221 * It is similar to cpml_primitive_type_get_n_points() but using
222 * a @primitive instance instead of a type.
224 * Returns: the number of points or %0 on errors
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_point:
234 * @primitive: a #CpmlPrimitive
235 * @n_point: the index of the point to retrieve
237 * Gets the specified @n_point from @primitive. The index starts
238 * at 0: if @n_point is 0, the start point (the origin) is
239 * returned, 1 for the second point and so on. If @n_point is
240 * negative, it is considered as a negative index from the end,
241 * so that -1 is the end point, -2 the point before the end point
242 * and so on.
244 * #CPML_CLOSE is managed in a special way: if @n_point
245 * is -1 or 1 and @primitive is a close-path, this function cycles
246 * the source #CpmlSegment and returns the first point. This is
247 * needed because requesting the end point (or the second point)
248 * of a close path is a valid operation and must returns the start
249 * of the segment.
251 * Returns: a pointer to the requested point (in cairo format)
252 * or %NULL if the point is outside the valid range
254 cairo_path_data_t *
255 cpml_primitive_get_point(const CpmlPrimitive *primitive, int n_point)
257 size_t n_points;
259 /* For a start point request, simply return the origin
260 * without further checking */
261 if (n_point == 0)
262 return primitive->org;
264 /* The CPML_CLOSE special case */
265 if (primitive->data->header.type == CAIRO_PATH_CLOSE_PATH &&
266 (n_point == 1 || n_point == -1))
267 return &primitive->segment->data[1];
269 n_points = cpml_primitive_get_n_points(primitive);
270 if (n_points == 0)
271 return NULL;
273 /* If n_point is negative, consider it as a negative index from the end */
274 if (n_point < 0)
275 n_point = n_points + n_point;
277 /* Out of range condition */
278 if (n_point < 0 || n_point >= n_points)
279 return NULL;
281 return n_point == 0 ? primitive->org : &primitive->data[n_point];
285 * cpml_primitive_get_length:
286 * @primitive: a #CpmlPrimitive
288 * Abstracts the length() family functions by providing a common
289 * way to access the underlying primitive-specific implementation.
290 * The function returns the length of @primitive.
292 * Returns: the requested length or 0 on errors
294 double
295 cpml_primitive_get_length(const CpmlPrimitive *primitive)
297 const _CpmlPrimitiveClass *class_data = get_class(primitive);
299 if (class_data == NULL || class_data->get_length == NULL)
300 return 0;
302 return class_data->get_length(primitive);
306 * cpml_primitive_put_extents:
307 * @primitive: a #CpmlPrimitive
308 * @extents: where to store the extents
310 * Abstracts the extents() family functions by providing a common
311 * way to access the underlying primitive-specific implementation.
313 * This function stores in @extents the bounding box of @primitive.
315 * On errors, that is if the extents cannot be calculated for some
316 * reason, this function does nothing.
318 void
319 cpml_primitive_put_extents(const CpmlPrimitive *primitive,
320 CpmlExtents *extents)
322 const _CpmlPrimitiveClass *class_data = get_class(primitive);
324 if (class_data == NULL || class_data->put_extents == NULL)
325 return;
327 class_data->put_extents(primitive, extents);
331 * cpml_primitive_put_pair_at:
332 * @primitive: a #CpmlPrimitive
333 * @pos: the position value
334 * @pair: the destination #CpmlPair
336 * Abstracts the put_pair_at() family functions by providing a common
337 * way to access the underlying primitive-specific implementation.
339 * It gets the coordinates of the point lying on @primitive
340 * at position @pos. @pos is an homogeneous factor where 0 is the
341 * start point, 1 the end point, 0.5 the mid point and so on.
342 * @pos can be less than 0 or greater than %1, in which case the
343 * coordinates of @pair are interpolated.
345 * On errors, that is if the coordinates cannot be calculated for
346 * some reason, this function does nothing.
348 void
349 cpml_primitive_put_pair_at(const CpmlPrimitive *primitive, double pos,
350 CpmlPair *pair)
352 const _CpmlPrimitiveClass *class_data = get_class(primitive);
354 if (class_data == NULL || class_data->put_pair_at == NULL)
355 return;
357 class_data->put_pair_at(primitive, pos, pair);
361 * cpml_primitive_put_vector_at:
362 * @primitive: a #CpmlPrimitive
363 * @pos: the position value
364 * @vector: the destination #CpmlVector
366 * Abstracts the put_vector_at() family functions by providing a common
367 * way to access the underlying primitive-specific implementation.
369 * It gets the steepness of the point at position @pos on @primitive.
370 * @pos is an homogeneous factor where 0 is the start point, 1 the
371 * end point, 0.5 the mid point and so on.
372 * @pos can be less than 0 or greater than %1, in which case the
373 * coordinates of @pair are interpolated.
375 * On errors, that is if the steepness cannot be calculated for
376 * some reason, this function does nothing.
378 void
379 cpml_primitive_put_vector_at(const CpmlPrimitive *primitive, double pos,
380 CpmlVector *vector)
382 const _CpmlPrimitiveClass *class_data = get_class(primitive);
384 if (class_data == NULL || class_data->put_vector_at == NULL)
385 return;
387 class_data->put_vector_at(primitive, pos, vector);
391 * cpml_primitive_get_closest_pos:
392 * @primitive: a #CpmlPrimitive
393 * @pair: the coordinates of the subject point
395 * Returns the pos value of the point on @primitive nearest to @pair.
396 * The returned value is always clamped between %0 and %1.
398 * Returns: the requested pos value between %0 and %1, or %-1 on errors
400 double
401 cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive,
402 const CpmlPair *pair)
404 const _CpmlPrimitiveClass *class_data = get_class(primitive);
406 if (class_data == NULL || class_data->get_closest_pos == NULL)
407 return -1;
409 return class_data->get_closest_pos(primitive, pair);
413 * cpml_primitive_put_intersections:
414 * @primitive: the first #CpmlPrimitive
415 * @primitive2: the second #CpmlPrimitive
416 * @n_dest: maximum number of intersections to return
417 * @dest: the destination buffer that can contain @n_dest #CpmlPair
419 * Finds the intersection points between the given primitives and
420 * returns the result in @dest. The size of @dest should be enough
421 * to store @n_dest #CpmlPair. The maximum number of intersections
422 * is dependent on the type of the primitive involved in the
423 * operation. If there are at least one Bézier curve involved, up to
424 * %4 intersections could be returned. Otherwise, if there is an arc
425 * the intersections will be %2 at maximum. For line primitives, there
426 * is only %1 point (or %0 if the lines are parallel).
428 * <note>
429 * <para>
430 * The convention used by the CPML library is that a primitive should
431 * implement only the intersection algorithms with lower degree
432 * primitives. This is required to avoid code duplication: intersection
433 * between arc and Bézier curves must be implemented by #CPML_CURVE and
434 * intersection between lines and arcs must be implemented by #CPML_ARC.
435 * cpml_primitive_put_intersections() will take care of swapping the
436 * arguments if they are not properly ordered.
437 * </para>
438 * </note>
440 * Returns: the number of intersection points found or 0 if the
441 * primitives do not intersect or on errors
443 size_t
444 cpml_primitive_put_intersections(const CpmlPrimitive *primitive,
445 const CpmlPrimitive *primitive2,
446 size_t n_dest, CpmlPair *dest)
448 const _CpmlPrimitiveClass *class_data;
449 size_t n_points, n_points2;
451 class_data = get_class(primitive);
453 if (class_data == NULL || class_data->put_intersections == NULL)
454 return 0;
456 n_points = cpml_primitive_get_n_points(primitive);
457 n_points2 = cpml_primitive_get_n_points(primitive2);
459 if (n_points == 0 || n_points2 == 0)
460 return 0;
462 /* Primitives reordering: the first must be the more complex one */
463 if (n_points < n_points2) {
464 const CpmlPrimitive *old_primitive2 = primitive2;
465 primitive2 = primitive;
466 primitive = old_primitive2;
469 return class_data->put_intersections(primitive, primitive2, n_dest, dest);
473 * cpml_primitive_put_intersections_with_segment:
474 * @primitive: a #CpmlPrimitive
475 * @segment: a #CpmlSegment
476 * @n_dest: maximum number of intersection pairs to return
477 * @dest: the destination buffer of #CpmlPair
479 * Computes the intersections between @segment and @primitive by
480 * sequentially scanning the primitives in @segment and looking
481 * for their intersections with @primitive.
483 * If the intersections are more than @n_dest, only the first
484 * @n_dest pairs are stored.
486 * Returns: the number of intersections found
488 size_t
489 cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive,
490 const CpmlSegment *segment,
491 size_t n_dest, CpmlPair *dest)
493 CpmlPrimitive portion;
494 size_t found;
496 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
497 found = 0;
499 while (found < n_dest) {
500 found += cpml_primitive_put_intersections(&portion, primitive,
501 n_dest-found, dest+found);
502 if (!cpml_primitive_next(&portion))
503 break;
506 return found;
510 * cpml_primitive_offset:
511 * @primitive: a #CpmlPrimitive
512 * @offset: distance for the computed offset primitive
514 * Given a primitive, computes the same (or approximated) parallel
515 * primitive distant @offset from the original one and returns
516 * the result by changing @primitive.
518 * On errors, that is if the offset primitive cannot be calculated
519 * for some reason, this function does nothing.
521 void
522 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
524 const _CpmlPrimitiveClass *class_data = get_class(primitive);
526 if (class_data == NULL || class_data->offset == NULL)
527 return;
529 class_data->offset(primitive, offset);
533 * cpml_primitive_join:
534 * @primitive: the first #CpmlPrimitive
535 * @primitive2: the second #CpmlPrimitive
537 * Joins two primitive modifying the end point of @primitive and the
538 * start point of @primitive2 so that the resulting points will overlap.
540 * <important>
541 * <title>TODO</title>
542 * <itemizedlist>
543 * <listitem>Actually, the join is done by extending the end vector
544 * of @primitive and the start vector of @primitive2 and
545 * interpolating the intersection: this means no primitive
546 * dependent code is needed. Anyway, it is likely to change
547 * in the future because this approach is quite naive when
548 * curves are involved.</listitem>
549 * </itemizedlist>
550 * </important>
552 * Returns: 1 on success, 0 if the end vector of @primitive
553 * and the start vector of @primitive2 are parallel
555 cairo_bool_t
556 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
558 cairo_path_data_t *end1, *start2;
559 CpmlPrimitive line1, line2;
560 cairo_path_data_t data1[2], data2[2];
561 CpmlPair joint;
563 end1 = cpml_primitive_get_point(primitive, -1);
564 start2 = cpml_primitive_get_point(primitive2, 0);
566 /* Check if the primitives are yet connected */
567 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
568 return 1;
570 line1.org = cpml_primitive_get_point(primitive, -2);
571 line1.data = data1;
572 data1[0].header.type = CPML_LINE;
573 data1[1] = *end1;
575 line2.org = start2;
576 line2.data = data2;
577 data2[0].header.type = CPML_LINE;
578 data2[1] = *cpml_primitive_get_point(primitive2, 1);
580 if (!cpml_primitive_put_intersections(&line1, &line2, 1, &joint))
581 return 0;
583 cpml_pair_to_cairo(&joint, end1);
584 cpml_pair_to_cairo(&joint, start2);
586 return 1;
590 * cpml_primitive_to_cairo:
591 * @primitive: a #CpmlPrimitive
592 * @cr: the destination cairo context
594 * Renders a single @primitive to the @cr cairo context.
595 * As a special case, if the primitive is a #CPML_CLOSE, an
596 * equivalent line is rendered, because a close path left alone
597 * is not renderable.
599 * Also a #CPML_ARC primitive is treated specially, as it is not
600 * natively supported by cairo and has its own rendering API.
602 void
603 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
605 CpmlPrimitiveType type;
606 cairo_path_t path;
607 cairo_path_data_t *path_data;
609 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
611 type = primitive->data->header.type;
613 if (type == CPML_CLOSE) {
614 path_data = cpml_primitive_get_point(primitive, -1);
615 cairo_line_to(cr, path_data->point.x, path_data->point.y);
616 } else if (type == CPML_ARC) {
617 cpml_arc_to_cairo(primitive, cr);
618 } else {
619 path.status = CAIRO_STATUS_SUCCESS;
620 path.data = primitive->data;
621 path.num_data = primitive->data->header.length;
622 cairo_append_path(cr, &path);
627 * cpml_primitive_dump:
628 * @primitive: a #CpmlPrimitive
629 * @org_also: whether to output also the origin coordinates
631 * Dumps info on the specified @primitive to stdout: useful for
632 * debugging purposes. If @org_also is 1, a #CPML_MOVE to the
633 * origin is prepended to the data otherwise the
634 * <structfield>org</structfield> field is not used.
636 void
637 cpml_primitive_dump(const CpmlPrimitive *primitive, cairo_bool_t org_also)
639 const cairo_path_data_t *data;
640 int type;
641 const _CpmlPrimitiveClass *class_data;
642 size_t n, n_points;
644 data = primitive->data;
645 type = data->header.type;
646 class_data = get_class_from_type(type);
648 if (class_data == NULL) {
649 printf("Unknown primitive type (%d)\n", type);
650 return;
653 /* Dump the origin, if requested */
654 if (org_also) {
655 printf("move to ");
656 dump_cairo_point(primitive->org);
657 printf("\n");
660 printf("%s ", class_data->name);
662 n_points = cpml_primitive_get_n_points(primitive);
663 for (n = 1; n < n_points; ++n)
664 dump_cairo_point(cpml_primitive_get_point(primitive, n));
666 printf("\n");
670 static const _CpmlPrimitiveClass *
671 get_class_from_type(CpmlPrimitiveType type)
673 if (type == CPML_LINE)
674 return _cpml_line_get_class();
675 else if (type == CPML_ARC)
676 return _cpml_arc_get_class();
677 else if (type == CPML_CURVE)
678 return _cpml_curve_get_class();
679 else if (type == CPML_CLOSE)
680 return _cpml_close_get_class();
682 return NULL;
685 static const _CpmlPrimitiveClass *
686 get_class(const CpmlPrimitive *primitive)
688 return get_class_from_type(primitive->data->header.type);
691 static void
692 dump_cairo_point(const cairo_path_data_t *path_data)
694 printf("(%g %g) ", path_data->point.x, path_data->point.y);