[AdgArrowStyle] Hidden private struct
[adg.git] / cpml / cpml-primitive.c
bloba552d3019cba4fe1eda6c666fc19821245837b4a
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.
20 /**
21 * SECTION:primitive
22 * @title: CpmlPrimitive
23 * @short_description: Basic component of segments
25 * A primitive is an atomic geometric element found inside #CpmlSegment.
26 * The available primitives are the same defined by #cairo_path_data_type_t
27 * with the additional %CAIRO_PATH_ARC_TO type (check #CpmlPrimitiveType
28 * for further information) and without %CAIRO_PATH_MOVE_TO, as the latter
29 * is not considered a valid primitive and it is managed in different way
30 * (the move to primitives are only used to define the origin of a segment).
31 **/
33 /**
34 * CpmlPrimitiveType:
36 * This is another name for #cairo_path_data_type_t type. Although
37 * phisically they are the same struct, #CpmlPrimitiveType conceptually
38 * embodies an important difference: it can be used to specify the
39 * special %CAIRO_PATH_ARC_TO primitive. This is not a native cairo
40 * primitive and having two different types is a good way to make clear
41 * when a function expect or not embedded arc-to primitives.
42 **/
44 /**
45 * CpmlPrimitive:
46 * @segment: the source #CpmlSegment
47 * @org: a pointer to the first point of the primitive
48 * @data: the array of the path data, prepended by the header
50 * As for #CpmlSegment, also the primitive is unobtrusive. This
51 * means CpmlPrimitive does not include any coordinates but instead
52 * keeps pointers to the original segment (and, by transition, to
53 * the underlying #CpmlPath struct).
54 **/
56 #include "cpml-primitive.h"
57 #include "cpml-line.h"
58 #include "cpml-arc.h"
59 #include "cpml-curve.h"
60 #include "cpml-close.h"
62 #include <stdlib.h>
63 #include <string.h>
64 #include <stdio.h>
67 static void dump_cairo_point (const cairo_path_data_t *path_data);
70 /**
71 * cpml_primitive_copy:
72 * @primitive: the destination #CpmlPrimitive
73 * @src: the source #CpmlPrimitive
75 * Copies @src in @primitive. This is a shallow copy: the internal fields
76 * of @primitive refer to the same memory as the original @src primitive.
78 * Return value: @primitive
79 **/
80 CpmlPrimitive *
81 cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src)
83 return memcpy(primitive, src, sizeof(CpmlPrimitive));
86 /**
87 * cpml_primitive_from_segment:
88 * @primitive: the destination #CpmlPrimitive struct
89 * @segment: the source segment
91 * Initializes @primitive to the first primitive of @segment.
93 * Return value: @primitive
94 **/
95 CpmlPrimitive *
96 cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment)
98 primitive->segment = segment;
100 /* The first element of a CpmlSegment is always a CAIRO_PATH_MOVE_TO,
101 * as ensured by cpml_segment_from_cairo() and by the browsing APIs,
102 * so the origin is in the second data item */
103 primitive->org = &segment->data[1];
105 /* Also, the segment APIs ensure that @segment is prepended by
106 * only one CAIRO_PATH_MOVE_TO */
107 primitive->data = segment->data + 2;
109 return primitive;
113 * cpml_primitive_reset:
114 * @primitive: a #CpmlPrimitive
116 * Resets @primitive so it refers to the first primitive of the
117 * source segment.
119 void
120 cpml_primitive_reset(CpmlPrimitive *primitive)
122 primitive->org = &primitive->segment->data[1];
123 primitive->data = primitive->segment->data + 2;
127 * cpml_primitive_next:
128 * @primitive: a #CpmlPrimitive
131 * Changes @primitive so it refers to the next primitive on the
132 * source segment. If there are no more primitives, @primitive is
133 * not changed and 0 is returned.
135 * Return value: 1 on success, 0 if no next primitive found or errors
137 cairo_bool_t
138 cpml_primitive_next(CpmlPrimitive *primitive)
140 cairo_path_data_t *new_data;
142 new_data = primitive->data + primitive->data->header.length;
143 if (new_data - primitive->segment->data >= primitive->segment->num_data)
144 return 0;
146 primitive->org = cpml_primitive_get_point(primitive, -1);
147 primitive->data = new_data;
149 return 1;
153 * cpml_primitive_get_npoints:
154 * @primitive: a #CpmlPrimitive
156 * Gets the number of points required to identify @primitive.
157 * It is similar to cpml_primitive_type_get_npoints() but using
158 * a @primitive instance instead of a type.
160 * Return value: the number of points or -1 on errors
163 cpml_primitive_get_npoints(const CpmlPrimitive *primitive)
165 return cpml_primitive_type_get_npoints(primitive->data->header.type);
169 * cpml_primitive_get_point:
170 * @primitive: a #CpmlPrimitive
171 * @npoint: the index of the point to retrieve
173 * Gets the specified @npoint from @primitive. The index starts
174 * at 0: if @npoint is 0, the start point (the origin) is
175 * returned, 1 for the second point and so on. If @npoint is
176 * negative, it is considered as a negative index from the end,
177 * so that -1 is the end point, -2 the point before the end point
178 * and so on.
180 * %CAIRO_PATH_CLOSE_PATH is managed in a special way: if @npoint
181 * is -1 or 1 and @primitive is a close-path, this function cycles
182 * the source #CpmlSegment and returns the first point. This is
183 * needed because requesting the end point (or the second point)
184 * of a close path is a valid operation and must returns the start
185 * of the segment.
187 * Return value: a pointer to the requested point (in cairo format)
188 * or %NULL if the point is outside the valid range
190 cairo_path_data_t *
191 cpml_primitive_get_point(const CpmlPrimitive *primitive, int npoint)
193 int npoints;
195 /* For a start point request, simply return the origin
196 * without further checking */
197 if (npoint == 0)
198 return primitive->org;
200 /* The CAIRO_PATH_CLOSE_PATH special case */
201 if (primitive->data->header.type == CAIRO_PATH_CLOSE_PATH &&
202 (npoint == 1 || npoint == -1))
203 return &primitive->segment->data[1];
205 npoints = cpml_primitive_get_npoints(primitive);
206 if (npoints < 0)
207 return NULL;
209 /* If npoint is negative, consider it as a negative index from the end */
210 if (npoint < 0)
211 npoint = npoints + npoint;
213 /* Out of range condition */
214 if (npoint < 0 || npoint >= npoints)
215 return NULL;
217 return npoint == 0 ? primitive->org : &primitive->data[npoint];
221 * cpml_primitive_to_cairo:
222 * @primitive: a #CpmlPrimitive
223 * @cr: the destination cairo context
225 * Renders a single @primitive to the @cr cairo context.
226 * As a special case, if the primitive is a #CAIRO_PATH_CLOSE_PATH,
227 * an equivalent line is rendered, because a close path left alone
228 * is not renderable.
230 * Also a #CAIRO_PATH_ARC_TO primitive is treated specially, as it
231 * is not natively supported by cairo and has its own rendering API.
233 void
234 cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr)
236 cairo_path_t path;
237 cairo_path_data_t *path_data;
239 cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y);
241 switch (primitive->data->header.type) {
243 case CAIRO_PATH_CLOSE_PATH:
244 path_data = cpml_primitive_get_point(primitive, -1);
245 cairo_line_to(cr, path_data->point.x, path_data->point.y);
246 break;
248 case CAIRO_PATH_ARC_TO:
249 cpml_arc_to_cairo(primitive, cr);
250 break;
252 default:
253 path.status = CAIRO_STATUS_SUCCESS;
254 path.data = primitive->data;
255 path.num_data = primitive->data->header.length;
256 cairo_append_path(cr, &path);
257 break;
262 * cpml_primitive_dump:
263 * @primitive: a #CpmlPrimitive
264 * @org_also: whether to output also the origin coordinates
266 * Dumps info on the specified @primitive to stdout: useful for
267 * debugging purposes. If @org_also is 1, a %CAIRO_PATH_MOVE_TO
268 * to the origin is prepended to the data otherwise the
269 * <structfield>org</structfield> field is not used.
271 void
272 cpml_primitive_dump(const CpmlPrimitive *primitive, cairo_bool_t org_also)
274 const cairo_path_data_t *data;
275 int type, n, npoints;
277 data = primitive->data;
278 type = data->header.type;
279 npoints = cpml_primitive_get_npoints(primitive);
280 if (npoints < 0) {
281 printf("Unhandled primitive type (%d)\n", type);
282 return;
285 /* Dump the origin movement, if requested */
286 if (org_also) {
287 printf("Move to ");
288 dump_cairo_point(primitive->org);
289 printf("\n");
292 switch (type) {
294 case CAIRO_PATH_LINE_TO:
295 printf("Line to ");
296 break;
298 case CAIRO_PATH_ARC_TO:
299 printf("Arc to ");
300 break;
302 case CAIRO_PATH_CURVE_TO:
303 printf("Curve to ");
304 break;
306 case CAIRO_PATH_CLOSE_PATH:
307 printf("Path close");
308 break;
310 default:
311 printf("Unknown primitive (type = %d)", type);
312 break;
315 for (n = 1; n < npoints; ++n)
316 dump_cairo_point(cpml_primitive_get_point(primitive, n));
318 printf("\n");
322 * cpml_primitive_intersection_with_segment:
323 * @primitive: a #CpmlPrimitive
324 * @segment: a #CpmlSegment
325 * @dest: the destination vector of #CpmlPair
326 * @max: maximum number of intersections to return
328 * Computes the intersections between @segment and @primitive by
329 * sequentially scanning the primitives in @segment and looking
330 * for intersections with @primitive.
331 * If the intersections are more than @max, only the first @max pairs
332 * are stored in @dest.
334 * Return value: the number of intersections found
337 cpml_primitive_intersection_with_segment(const CpmlPrimitive *primitive,
338 const CpmlSegment *segment,
339 CpmlPair *dest, int max)
341 CpmlPrimitive portion;
342 int found;
344 cpml_primitive_from_segment(&portion, (CpmlSegment *) segment);
345 found = 0;
347 while (found < max) {
348 found += cpml_primitive_intersection(&portion, primitive,
349 dest+found, max-found);
350 if (!cpml_primitive_next(&portion))
351 break;
354 return found;
359 * cpml_primitive_type_get_npoints:
360 * @type: a primitive type
362 * Gets the number of points required to identify the @type primitive.
364 * <note><para>
365 * This function is primitive dependent, that is every primitive has
366 * its own implementation.
367 * </para></note>
369 * Return value: the number of points or -1 on errors
372 cpml_primitive_type_get_npoints(CpmlPrimitiveType type)
374 switch (type) {
376 case CAIRO_PATH_LINE_TO:
377 return cpml_line_type_get_npoints();
379 case CAIRO_PATH_ARC_TO:
380 return cpml_arc_type_get_npoints();
382 case CAIRO_PATH_CURVE_TO:
383 return cpml_curve_type_get_npoints();
385 case CAIRO_PATH_CLOSE_PATH:
386 return cpml_close_type_get_npoints();
388 default:
389 break;
392 return -1;
396 * cpml_primitive_length:
397 * @primitive: a #CpmlPrimitive
399 * Abstracts the length() family functions by providing a common
400 * way to access the underlying primitive-specific implementation.
401 * The function returns the length of @primitive.
403 * <note><para>
404 * This function is primitive dependent, that is every primitive has
405 * its own implementation.
406 * </para></note>
408 * Return value: the requested length or 0 on errors
410 double
411 cpml_primitive_length(const CpmlPrimitive *primitive)
413 switch (primitive->data->header.type) {
415 case CAIRO_PATH_LINE_TO:
416 case CAIRO_PATH_CLOSE_PATH:
417 return cpml_line_length(primitive);
419 case CAIRO_PATH_ARC_TO:
420 return cpml_arc_length(primitive);
422 case CAIRO_PATH_CURVE_TO:
423 return cpml_curve_length(primitive);
425 default:
426 break;
429 return 0.;
433 * cpml_primitive_pair_at:
434 * @primitive: a #CpmlPrimitive
435 * @pair: the destination #CpmlPair
436 * @pos: the position value
438 * Abstracts the pair_at() family functions by providing a common
439 * way to access the underlying primitive-specific implementation.
441 * It gets the coordinates of the point lying on @primitive
442 * at position @pos. @pos is an homogeneous factor where 0 is the
443 * start point, 1 the end point, 0.5 the mid point and so on.
444 * The relation 0 < @pos < 1 should be satisfied, although some
445 * primitives accept value outside this range.
447 * <note><para>
448 * This function is primitive dependent, that is every primitive has
449 * its own implementation.
450 * </para></note>
452 void
453 cpml_primitive_pair_at(const CpmlPrimitive *primitive,
454 CpmlPair *pair, double pos)
456 switch (primitive->data->header.type) {
458 case CAIRO_PATH_LINE_TO:
459 cpml_line_pair_at(primitive, pair, pos);
460 break;
462 case CAIRO_PATH_ARC_TO:
463 cpml_arc_pair_at(primitive, pair, pos);
464 break;
466 case CAIRO_PATH_CURVE_TO:
467 cpml_curve_pair_at(primitive, pair, pos);
468 break;
470 case CAIRO_PATH_CLOSE_PATH:
471 cpml_close_pair_at(primitive, pair, pos);
472 break;
474 default:
475 break;
480 * cpml_primitive_vector_at:
481 * @primitive: a #CpmlPrimitive
482 * @vector: the destination #CpmlVector
483 * @pos: the position value
485 * Abstracts the vector_at() family functions by providing a common
486 * way to access the underlying primitive-specific implementation.
488 * It gets the steepness of the point at position @pos on @primitive.
489 * @pos is an homogeneous factor where 0 is the start point, 1 the
490 * end point, 0.5 the mid point and so on.
491 * The relation 0 < @pos < 1 should be satisfied, although some
492 * primitives accept value outside this range.
494 * <note><para>
495 * This function is primitive dependent, that is every primitive has
496 * its own implementation.
497 * </para></note>
499 void
500 cpml_primitive_vector_at(const CpmlPrimitive *primitive,
501 CpmlVector *vector, double pos)
503 switch (primitive->data->header.type) {
505 case CAIRO_PATH_LINE_TO:
506 cpml_line_vector_at(primitive, vector, pos);
507 break;
509 case CAIRO_PATH_ARC_TO:
510 cpml_arc_vector_at(primitive, vector, pos);
511 break;
513 case CAIRO_PATH_CURVE_TO:
514 cpml_curve_vector_at(primitive, vector, pos);
515 break;
517 case CAIRO_PATH_CLOSE_PATH:
518 cpml_close_vector_at(primitive, vector, pos);
519 break;
521 default:
522 break;
527 * cpml_primitive_near_pos:
528 * @primitive: a #CpmlPrimitive
529 * @pair: the coordinates of the subject point
531 * Returns the pos value of the point on @primitive nearest to @pair.
532 * The returned value is always between 0 and 1 or -1 in case of errors.
534 * <note><para>
535 * This function is primitive dependent, that is every primitive has
536 * its own implementation.
537 * </para></note>
539 * Return value: the requested pos value between 0 and 1 or -1 on errors
541 double
542 cpml_primitive_near_pos(const CpmlPrimitive *primitive, const CpmlPair *pair)
544 switch (primitive->data->header.type) {
546 case CAIRO_PATH_LINE_TO:
547 return cpml_line_near_pos(primitive, pair);
549 case CAIRO_PATH_ARC_TO:
550 return cpml_arc_near_pos(primitive, pair);
552 case CAIRO_PATH_CURVE_TO:
553 return cpml_curve_near_pos(primitive, pair);
555 case CAIRO_PATH_CLOSE_PATH:
556 return cpml_close_near_pos(primitive, pair);
558 default:
559 break;
562 return -1.;
566 * cpml_primitive_join:
567 * @primitive: the first #CpmlPrimitive
568 * @primitive2: the second #CpmlPrimitive
570 * Joins two primitive modifying the end point of @primitive and the
571 * start point of @primitive2 so that the resulting points will overlap.
573 * <important>
574 * <title>TODO</title>
575 * <itemizedlist>
576 * <listitem>Actually, the join is done by extending the end vector
577 * of @primitive and the start vector of @primitive2 and
578 * interpolating the intersection: this means no primitive
579 * dependent code is needed. Anyway, it is likely to change
580 * in the future because this approach is quite naive when
581 * curves are involved.</listitem>
582 * </itemizedlist>
583 * </important>
585 * Return value: 1 on success, 0 if the end vector of @primitive
586 * and the start vector of @primitive2 are parallel
588 cairo_bool_t
589 cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2)
591 cairo_path_data_t *end1, *start2;
592 CpmlPrimitive line1, line2;
593 cairo_path_data_t data1[2], data2[2];
594 CpmlPair joint;
596 end1 = cpml_primitive_get_point(primitive, -1);
597 start2 = cpml_primitive_get_point(primitive2, 0);
599 /* Check if the primitives are yet connected */
600 if (end1->point.x == start2->point.x && end1->point.y == start2->point.y)
601 return 1;
603 line1.org = cpml_primitive_get_point(primitive, -2);
604 line1.data = data1;
605 data1[0].header.type = CAIRO_PATH_LINE_TO;
606 data1[1] = *end1;
608 line2.org = start2;
609 line2.data = data2;
610 data2[0].header.type = CAIRO_PATH_LINE_TO;
611 data2[1] = *cpml_primitive_get_point(primitive2, 1);
613 if (!cpml_line_intersection(&line1, &line2, &joint, 1))
614 return 0;
616 cpml_pair_to_cairo(&joint, end1);
617 cpml_pair_to_cairo(&joint, start2);
619 return 1;
623 * cpml_primitive_intersection:
624 * @primitive: the first #CpmlPrimitive
625 * @primitive2: the second #CpmlPrimitive
626 * @dest: the destination #CpmlPair (or a vector of #CpmlPair)
627 * @max: maximum number of intersections to return
629 * Finds the intersection points between the given primitives and
630 * returns the result in @dest. The size of @dest should be enough
631 * to store @max #CpmlPair. The absoulte max number of intersections
632 * is dependent from the type of the primitive involved in the
633 * operation. If there are at least one Bézier curve involved, up to
634 * 4 intersections could be returned. Otherwise, if there is an arc
635 * the intersections will be 2 at maximum. For line line primitives,
636 * there is only 1 point (or obviously 0 if the lines do not intersect).
638 * <note><para>
639 * This function is primitive dependent: every new primitive must
640 * expose API to get intersections with any other primitive type
641 * (excluding %CAIRO_PATH_CLOSE_PATH, as it is converted to a line
642 * primitive).</para>
643 * <para>The convention used by CPML is that a primitive should
644 * expose only intersection APIs dealing with lower complexity
645 * primitives. This is required to avoid double functions:
646 * you will have only a cpml_curve_intersection_with_line() function,
647 * not a cpml_line_intersection_with_curve(), as the latter is
648 * easily reproduced by calling the former with @primitive2
649 * and @primitive swapped.
650 * </para></note>
652 * Return value: the number of intersection points found or 0 if the
653 * primitives do not intersect
656 cpml_primitive_intersection(const CpmlPrimitive *primitive,
657 const CpmlPrimitive *primitive2,
658 CpmlPair *dest, int max)
660 CpmlPrimitiveType type1, type2;
662 type1 = primitive->data->header.type;
663 type2 = primitive->data->header.type;
665 /* Close path primitives are treated as line-to */
666 if (type1 == CAIRO_PATH_CLOSE_PATH)
667 type1 = CAIRO_PATH_LINE_TO;
668 if (type2 == CAIRO_PATH_CLOSE_PATH)
669 type2 = CAIRO_PATH_LINE_TO;
671 /* Order the two primitives in ascending complexity, to facilitate
672 * the dispatcher logic */
673 if (cpml_primitive_type_get_npoints(type1) > cpml_primitive_type_get_npoints(type2)) {
674 const CpmlPrimitive *tmp_primitive;
675 CpmlPrimitiveType tmp_type;
677 tmp_type = type1;
678 tmp_primitive = primitive;
680 type1 = type2;
681 primitive = primitive2;
683 type2 = tmp_type;
684 primitive2 = tmp_primitive;
687 /* Dispatcher: probably there's a smarter way to do this */
688 switch (type1) {
690 case CAIRO_PATH_LINE_TO:
691 if (type2 == CAIRO_PATH_LINE_TO)
692 return cpml_line_intersection(primitive2, primitive,
693 dest, max);
694 else if (type2 == CAIRO_PATH_ARC_TO)
695 return cpml_arc_intersection_with_line(primitive2, primitive,
696 dest, max);
697 else if (type2 == CAIRO_PATH_CURVE_TO)
698 return cpml_curve_intersection_with_line(primitive2, primitive,
699 dest, max);
700 break;
702 case CAIRO_PATH_ARC_TO:
703 if (type2 == CAIRO_PATH_ARC_TO)
704 return cpml_arc_intersection(primitive2, primitive,
705 dest, max);
706 else if (type2 == CAIRO_PATH_CURVE_TO)
707 return cpml_curve_intersection_with_arc(primitive2, primitive,
708 dest, max);
709 break;
711 case CAIRO_PATH_CURVE_TO:
712 if (type2 == CAIRO_PATH_CURVE_TO)
713 return cpml_curve_intersection(primitive2, primitive,
714 dest, max);
715 break;
717 default:
718 break;
721 /* Primitive combination not found */
722 return 0;
726 * cpml_primitive_offset:
727 * @primitive: a #CpmlPrimitive
728 * @offset: distance for the computed offset primitive
730 * Given a primitive, computes the same (or approximated) parallel
731 * primitive distant @offset from the original one and returns
732 * the result by changing @primitive.
734 * <note><para>
735 * This function is primitive dependent, that is every primitive has
736 * its own implementation.
737 * </para></note>
739 void
740 cpml_primitive_offset(CpmlPrimitive *primitive, double offset)
742 switch (primitive->data->header.type) {
744 case CAIRO_PATH_LINE_TO:
745 cpml_line_offset(primitive, offset);
746 break;
748 case CAIRO_PATH_ARC_TO:
749 cpml_arc_offset(primitive, offset);
750 break;
752 case CAIRO_PATH_CURVE_TO:
753 cpml_curve_offset(primitive, offset);
754 break;
756 case CAIRO_PATH_CLOSE_PATH:
757 cpml_close_offset(primitive, offset);
758 break;
760 default:
761 break;
765 static void
766 dump_cairo_point(const cairo_path_data_t *path_data)
768 printf("(%g %g) ", path_data->point.x, path_data->point.y);