[AdgLDim] Using the new AdgEntity APIs
[adg.git] / cpml / cpml-primitive.c
bloba127d085db08122590327913a31c96dc49701372
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: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-primitive.h"
60 #include "cpml-line.h"
61 #include "cpml-arc.h"
62 #include "cpml-curve.h"
63 #include "cpml-close.h"
65 #include <stdlib.h>
66 #include <string.h>
67 #include <stdio.h>
70 static void dump_cairo_point (const cairo_path_data_t *path_data);
73 /**
74 * cpml_primitive_copy:
75 * @primitive: the destination #CpmlPrimitive
76 * @src: the source #CpmlPrimitive
78 * Copies @src in @primitive. This is a shallow copy: the internal fields
79 * of @primitive refer to the same memory as the original @src primitive.
81 * Return value: @primitive
82 **/
83 CpmlPrimitive *
84 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
86 return memcpy(primitive, src, sizeof(CpmlPrimitive));
89 /**
90 * cpml_primitive_from_segment:
91 * @primitive: the destination #CpmlPrimitive struct
92 * @segment: the source segment
94 * Initializes @primitive to the first primitive of @segment.
96 * Return value: @primitive
97 **/
98 CpmlPrimitive *
99 cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment)
101 primitive->segment = segment;
103 /* The first element of a CpmlSegment is always a CAIRO_PATH_MOVE_TO,
104 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
105 * so the origin is in the second data item */
106 primitive->org = &segment->data[1];
108 /* Also, the segment APIs ensure that @segment is prepended by
109 * only one CAIRO_PATH_MOVE_TO */
110 primitive->data = segment->data + 2;
112 return primitive;
116 * cpml_primitive_reset:
117 * @primitive: a #CpmlPrimitive
119 * Resets @primitive so it refers to the first primitive of the
120 * source segment.
122 void
123 cpml_primitive_reset(CpmlPrimitive *primitive)
125 primitive->org = &primitive->segment->data[1];
126 primitive->data = primitive->segment->data + 2;
130 * cpml_primitive_next:
131 * @primitive: a #CpmlPrimitive
134 * Changes @primitive so it refers to the next primitive on the
135 * source segment. If there are no more primitives, @primitive is
136 * not changed and 0 is returned.
138 * Return value: 1 on success, 0 if no next primitive found or errors
140 cairo_bool_t
141 cpml_primitive_next(CpmlPrimitive *primitive)
143 cairo_path_data_t *new_data;
145 new_data = primitive->data + primitive->data->header.length;
146 if (new_data - primitive->segment->data >= primitive->segment->num_data)
147 return 0;
149 primitive->org = cpml_primitive_get_point(primitive, -1);
150 primitive->data = new_data;
152 return 1;
156 * cpml_primitive_get_npoints:
157 * @primitive: a #CpmlPrimitive
159 * Gets the number of points required to identify @primitive.
160 * It is similar to cpml_primitive_type_get_npoints() but using
161 * a @primitive instance instead of a type.
163 * Return value: the number of points or -1 on errors
166 cpml_primitive_get_npoints(const CpmlPrimitive *primitive)
168 return cpml_primitive_type_get_npoints(primitive->data->header.type);
172 * cpml_primitive_get_point:
173 * @primitive: a #CpmlPrimitive
174 * @npoint: the index of the point to retrieve
176 * Gets the specified @npoint from @primitive. The index starts
177 * at 0: if @npoint is 0, the start point (the origin) is
178 * returned, 1 for the second point and so on. If @npoint is
179 * negative, it is considered as a negative index from the end,
180 * so that -1 is the end point, -2 the point before the end point
181 * and so on.
183 * %CAIRO_PATH_CLOSE_PATH is managed in a special way: if @npoint
184 * is -1 or 1 and @primitive is a close-path, this function cycles
185 * the source #CpmlSegment and returns the first point. This is
186 * needed because requesting the end point (or the second point)
187 * of a close path is a valid operation and must returns the start
188 * of the segment.
190 * Return value: a pointer to the requested point (in cairo format)
191 * or %NULL if the point is outside the valid range
193 cairo_path_data_t *
194 cpml_primitive_get_point(const CpmlPrimitive *primitive, int npoint)
196 int npoints;
198 /* For a start point request, simply return the origin
199 * without further checking */
200 if (npoint == 0)
201 return primitive->org;
203 /* The CAIRO_PATH_CLOSE_PATH special case */
204 if (primitive->data->header.type == CAIRO_PATH_CLOSE_PATH &&
205 (npoint == 1 || npoint == -1))
206 return &primitive->segment->data[1];
208 npoints = cpml_primitive_get_npoints(primitive);
209 if (npoints < 0)
210 return NULL;
212 /* If npoint is negative, consider it as a negative index from the end */
213 if (npoint < 0)
214 npoint = npoints + npoint;
216 /* Out of range condition */
217 if (npoint < 0 || npoint >= npoints)
218 return NULL;
220 return npoint == 0 ? primitive->org : &primitive->data[npoint];
224 * cpml_primitive_to_cairo:
225 * @primitive: a #CpmlPrimitive
226 * @cr: the destination cairo context
228 * Renders a single @primitive to the @cr cairo context.
229 * As a special case, if the primitive is a #CAIRO_PATH_CLOSE_PATH,
230 * an equivalent line is rendered, because a close path left alone
231 * is not renderable.
233 * Also a #CAIRO_PATH_ARC_TO primitive is treated specially, as it
234 * is not natively supported by cairo and has its own rendering API.
236 void
237 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
239 cairo_path_t path;
240 cairo_path_data_t *path_data;
242 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
244 switch (primitive->data->header.type) {
246 case CAIRO_PATH_CLOSE_PATH:
247 path_data = cpml_primitive_get_point(primitive, -1);
248 cairo_line_to(cr, path_data->point.x, path_data->point.y);
249 break;
251 case CAIRO_PATH_ARC_TO:
252 cpml_arc_to_cairo(primitive, cr);
253 break;
255 default:
256 path.status = CAIRO_STATUS_SUCCESS;
257 path.data = primitive->data;
258 path.num_data = primitive->data->header.length;
259 cairo_append_path(cr, &path);
260 break;
265 * cpml_primitive_dump:
266 * @primitive: a #CpmlPrimitive
267 * @org_also: whether to output also the origin coordinates
269 * Dumps info on the specified @primitive to stdout: useful for
270 * debugging purposes. If @org_also is 1, a %CAIRO_PATH_MOVE_TO
271 * to the origin is prepended to the data otherwise the
272 * <structfield>org</structfield> field is not used.
274 void
275 cpml_primitive_dump(const CpmlPrimitive *primitive, cairo_bool_t org_also)
277 const cairo_path_data_t *data;
278 int type, n, npoints;
280 data = primitive->data;
281 type = data->header.type;
282 npoints = cpml_primitive_get_npoints(primitive);
283 if (npoints < 0) {
284 printf("Unhandled primitive type (%d)\n", type);
285 return;
288 /* Dump the origin movement, if requested */
289 if (org_also) {
290 printf("Move to ");
291 dump_cairo_point(primitive->org);
292 printf("\n");
295 switch (type) {
297 case CAIRO_PATH_LINE_TO:
298 printf("Line to ");
299 break;
301 case CAIRO_PATH_ARC_TO:
302 printf("Arc to ");
303 break;
305 case CAIRO_PATH_CURVE_TO:
306 printf("Curve to ");
307 break;
309 case CAIRO_PATH_CLOSE_PATH:
310 printf("Path close");
311 break;
313 default:
314 printf("Unknown primitive (type = %d)", type);
315 break;
318 for (n = 1; n < npoints; ++n)
319 dump_cairo_point(cpml_primitive_get_point(primitive, n));
321 printf("\n");
325 * cpml_primitive_intersection_with_segment:
326 * @primitive: a #CpmlPrimitive
327 * @segment: a #CpmlSegment
328 * @dest: the destination vector of #CpmlPair
329 * @max: maximum number of intersections to return
331 * Computes the intersections between @segment and @primitive by
332 * sequentially scanning the primitives in @segment and looking
333 * for intersections with @primitive.
334 * If the intersections are more than @max, only the first @max pairs
335 * are stored in @dest.
337 * Return value: the number of intersections found
340 cpml_primitive_intersection_with_segment(const CpmlPrimitive *primitive,
341 const CpmlSegment *segment,
342 CpmlPair *dest, int max)
344 CpmlPrimitive portion;
345 int found;
347 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
348 found = 0;
350 while (found < max) {
351 found += cpml_primitive_intersection(&portion, primitive,
352 dest+found, max-found);
353 if (!cpml_primitive_next(&portion))
354 break;
357 return found;
362 * cpml_primitive_type_get_npoints:
363 * @type: a primitive type
365 * Gets the number of points required to identify the @type primitive.
367 * <note><para>
368 * This function is primitive dependent, that is every primitive has
369 * its own implementation.
370 * </para></note>
372 * Return value: the number of points or -1 on errors
375 cpml_primitive_type_get_npoints(CpmlPrimitiveType type)
377 switch (type) {
379 case CAIRO_PATH_LINE_TO:
380 return cpml_line_type_get_npoints();
382 case CAIRO_PATH_ARC_TO:
383 return cpml_arc_type_get_npoints();
385 case CAIRO_PATH_CURVE_TO:
386 return cpml_curve_type_get_npoints();
388 case CAIRO_PATH_CLOSE_PATH:
389 return cpml_close_type_get_npoints();
391 default:
392 break;
395 return -1;
399 * cpml_primitive_length:
400 * @primitive: a #CpmlPrimitive
402 * Abstracts the length() family functions by providing a common
403 * way to access the underlying primitive-specific implementation.
404 * The function returns the length of @primitive.
406 * <note><para>
407 * This function is primitive dependent, that is every primitive has
408 * its own implementation.
409 * </para></note>
411 * Return value: the requested length or 0 on errors
413 double
414 cpml_primitive_length(const CpmlPrimitive *primitive)
416 switch (primitive->data->header.type) {
418 case CAIRO_PATH_LINE_TO:
419 case CAIRO_PATH_CLOSE_PATH:
420 return cpml_line_length(primitive);
422 case CAIRO_PATH_ARC_TO:
423 return cpml_arc_length(primitive);
425 case CAIRO_PATH_CURVE_TO:
426 return cpml_curve_length(primitive);
428 default:
429 break;
432 return 0.;
436 * cpml_primitive_pair_at:
437 * @primitive: a #CpmlPrimitive
438 * @pair: the destination #CpmlPair
439 * @pos: the position value
441 * Abstracts the pair_at() family functions by providing a common
442 * way to access the underlying primitive-specific implementation.
444 * It gets the coordinates of the point lying on @primitive
445 * at position @pos. @pos is an homogeneous factor where 0 is the
446 * start point, 1 the end point, 0.5 the mid point and so on.
447 * The relation 0 < @pos < 1 should be satisfied, although some
448 * primitives accept value outside this range.
450 * <note><para>
451 * This function is primitive dependent, that is every primitive has
452 * its own implementation.
453 * </para></note>
455 void
456 cpml_primitive_pair_at(const CpmlPrimitive *primitive,
457 CpmlPair *pair, double pos)
459 switch (primitive->data->header.type) {
461 case CAIRO_PATH_LINE_TO:
462 cpml_line_pair_at(primitive, pair, pos);
463 break;
465 case CAIRO_PATH_ARC_TO:
466 cpml_arc_pair_at(primitive, pair, pos);
467 break;
469 case CAIRO_PATH_CURVE_TO:
470 cpml_curve_pair_at(primitive, pair, pos);
471 break;
473 case CAIRO_PATH_CLOSE_PATH:
474 cpml_close_pair_at(primitive, pair, pos);
475 break;
477 default:
478 break;
483 * cpml_primitive_vector_at:
484 * @primitive: a #CpmlPrimitive
485 * @vector: the destination #CpmlVector
486 * @pos: the position value
488 * Abstracts the vector_at() family functions by providing a common
489 * way to access the underlying primitive-specific 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 * The relation 0 < @pos < 1 should be satisfied, although some
495 * primitives accept value outside this range.
497 * <note><para>
498 * This function is primitive dependent, that is every primitive has
499 * its own implementation.
500 * </para></note>
502 void
503 cpml_primitive_vector_at(const CpmlPrimitive *primitive,
504 CpmlVector *vector, double pos)
506 switch (primitive->data->header.type) {
508 case CAIRO_PATH_LINE_TO:
509 cpml_line_vector_at(primitive, vector, pos);
510 break;
512 case CAIRO_PATH_ARC_TO:
513 cpml_arc_vector_at(primitive, vector, pos);
514 break;
516 case CAIRO_PATH_CURVE_TO:
517 cpml_curve_vector_at(primitive, vector, pos);
518 break;
520 case CAIRO_PATH_CLOSE_PATH:
521 cpml_close_vector_at(primitive, vector, pos);
522 break;
524 default:
525 break;
530 * cpml_primitive_near_pos:
531 * @primitive: a #CpmlPrimitive
532 * @pair: the coordinates of the subject point
534 * Returns the pos value of the point on @primitive nearest to @pair.
535 * The returned value is always between 0 and 1 or -1 in case of errors.
537 * <note><para>
538 * This function is primitive dependent, that is every primitive has
539 * its own implementation.
540 * </para></note>
542 * Return value: the requested pos value between 0 and 1 or -1 on errors
544 double
545 cpml_primitive_near_pos(const CpmlPrimitive *primitive, const CpmlPair *pair)
547 switch (primitive->data->header.type) {
549 case CAIRO_PATH_LINE_TO:
550 return cpml_line_near_pos(primitive, pair);
552 case CAIRO_PATH_ARC_TO:
553 return cpml_arc_near_pos(primitive, pair);
555 case CAIRO_PATH_CURVE_TO:
556 return cpml_curve_near_pos(primitive, pair);
558 case CAIRO_PATH_CLOSE_PATH:
559 return cpml_close_near_pos(primitive, pair);
561 default:
562 break;
565 return -1.;
569 * cpml_primitive_join:
570 * @primitive: the first #CpmlPrimitive
571 * @primitive2: the second #CpmlPrimitive
573 * Joins two primitive modifying the end point of @primitive and the
574 * start point of @primitive2 so that the resulting points will overlap.
576 * <important>
577 * <title>TODO</title>
578 * <itemizedlist>
579 * <listitem>Actually, the join is done by extending the end vector
580 * of @primitive and the start vector of @primitive2 and
581 * interpolating the intersection: this means no primitive
582 * dependent code is needed. Anyway, it is likely to change
583 * in the future because this approach is quite naive when
584 * curves are involved.</listitem>
585 * </itemizedlist>
586 * </important>
588 * Return value: 1 on success, 0 if the end vector of @primitive
589 * and the start vector of @primitive2 are parallel
591 cairo_bool_t
592 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
594 cairo_path_data_t *end1, *start2;
595 CpmlPrimitive line1, line2;
596 cairo_path_data_t data1[2], data2[2];
597 CpmlPair joint;
599 end1 = cpml_primitive_get_point(primitive, -1);
600 start2 = cpml_primitive_get_point(primitive2, 0);
602 /* Check if the primitives are yet connected */
603 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
604 return 1;
606 line1.org = cpml_primitive_get_point(primitive, -2);
607 line1.data = data1;
608 data1[0].header.type = CAIRO_PATH_LINE_TO;
609 data1[1] = *end1;
611 line2.org = start2;
612 line2.data = data2;
613 data2[0].header.type = CAIRO_PATH_LINE_TO;
614 data2[1] = *cpml_primitive_get_point(primitive2, 1);
616 if (!cpml_line_intersection(&line1, &line2, &joint, 1))
617 return 0;
619 cpml_pair_to_cairo(&joint, end1);
620 cpml_pair_to_cairo(&joint, start2);
622 return 1;
626 * cpml_primitive_intersection:
627 * @primitive: the first #CpmlPrimitive
628 * @primitive2: the second #CpmlPrimitive
629 * @dest: the destination #CpmlPair (or a vector of #CpmlPair)
630 * @max: maximum number of intersections to return
632 * Finds the intersection points between the given primitives and
633 * returns the result in @dest. The size of @dest should be enough
634 * to store @max #CpmlPair. The absoulte max number of intersections
635 * is dependent from the type of the primitive involved in the
636 * operation. If there are at least one Bézier curve involved, up to
637 * 4 intersections could be returned. Otherwise, if there is an arc
638 * the intersections will be 2 at maximum. For line line primitives,
639 * there is only 1 point (or obviously 0 if the lines do not intersect).
641 * <note><para>
642 * This function is primitive dependent: every new primitive must
643 * expose API to get intersections with any other primitive type
644 * (excluding %CAIRO_PATH_CLOSE_PATH, as it is converted to a line
645 * primitive).</para>
646 * <para>The convention used by CPML is that a primitive should
647 * expose only intersection APIs dealing with lower complexity
648 * primitives. This is required to avoid double functions:
649 * you will have only a cpml_curve_intersection_with_line() function,
650 * not a cpml_line_intersection_with_curve(), as the latter is
651 * easily reproduced by calling the former with @primitive2
652 * and @primitive swapped.
653 * </para></note>
655 * Return value: the number of intersection points found or 0 if the
656 * primitives do not intersect
659 cpml_primitive_intersection(const CpmlPrimitive *primitive,
660 const CpmlPrimitive *primitive2,
661 CpmlPair *dest, int max)
663 CpmlPrimitiveType type1, type2;
665 type1 = primitive->data->header.type;
666 type2 = primitive->data->header.type;
668 /* Close path primitives are treated as line-to */
669 if (type1 == CAIRO_PATH_CLOSE_PATH)
670 type1 = CAIRO_PATH_LINE_TO;
671 if (type2 == CAIRO_PATH_CLOSE_PATH)
672 type2 = CAIRO_PATH_LINE_TO;
674 /* Order the two primitives in ascending complexity, to facilitate
675 * the dispatcher logic */
676 if (cpml_primitive_type_get_npoints(type1) > cpml_primitive_type_get_npoints(type2)) {
677 const CpmlPrimitive *tmp_primitive;
678 CpmlPrimitiveType tmp_type;
680 tmp_type = type1;
681 tmp_primitive = primitive;
683 type1 = type2;
684 primitive = primitive2;
686 type2 = tmp_type;
687 primitive2 = tmp_primitive;
690 /* Dispatcher: probably there's a smarter way to do this */
691 switch (type1) {
693 case CAIRO_PATH_LINE_TO:
694 if (type2 == CAIRO_PATH_LINE_TO)
695 return cpml_line_intersection(primitive2, primitive,
696 dest, max);
697 else if (type2 == CAIRO_PATH_ARC_TO)
698 return cpml_arc_intersection_with_line(primitive2, primitive,
699 dest, max);
700 else if (type2 == CAIRO_PATH_CURVE_TO)
701 return cpml_curve_intersection_with_line(primitive2, primitive,
702 dest, max);
703 break;
705 case CAIRO_PATH_ARC_TO:
706 if (type2 == CAIRO_PATH_ARC_TO)
707 return cpml_arc_intersection(primitive2, primitive,
708 dest, max);
709 else if (type2 == CAIRO_PATH_CURVE_TO)
710 return cpml_curve_intersection_with_arc(primitive2, primitive,
711 dest, max);
712 break;
714 case CAIRO_PATH_CURVE_TO:
715 if (type2 == CAIRO_PATH_CURVE_TO)
716 return cpml_curve_intersection(primitive2, primitive,
717 dest, max);
718 break;
720 default:
721 break;
724 /* Primitive combination not found */
725 return 0;
729 * cpml_primitive_offset:
730 * @primitive: a #CpmlPrimitive
731 * @offset: distance for the computed offset primitive
733 * Given a primitive, computes the same (or approximated) parallel
734 * primitive distant @offset from the original one and returns
735 * the result by changing @primitive.
737 * <note><para>
738 * This function is primitive dependent, that is every primitive has
739 * its own implementation.
740 * </para></note>
742 void
743 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
745 switch (primitive->data->header.type) {
747 case CAIRO_PATH_LINE_TO:
748 cpml_line_offset(primitive, offset);
749 break;
751 case CAIRO_PATH_ARC_TO:
752 cpml_arc_offset(primitive, offset);
753 break;
755 case CAIRO_PATH_CURVE_TO:
756 cpml_curve_offset(primitive, offset);
757 break;
759 case CAIRO_PATH_CLOSE_PATH:
760 cpml_close_offset(primitive, offset);
761 break;
763 default:
764 break;
768 static void
769 dump_cairo_point(const cairo_path_data_t *path_data)
771 printf("(%g %g) ", path_data->point.x, path_data->point.y);