[CPML] s/cpml_(.*?)_length/cpml_\1_get_length/
[adg.git] / adg / adg-path.c
blob82999e2b3d1bddd537f4352cb82d2d498499d8c3
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,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:adg-path
23 * @short_description: The basic model representing a generic path
25 * The #AdgPath model represents a virtual #CpmlPath: this class
26 * implements methods to create the path and provides additional
27 * operations specific to technical drawings.
29 * #AdgPath overrides the get_cpml_path() method of the parent
30 * #AdgTrail class, avoiding the need of an #AdgTrailCallback.
31 * The path is constructed programmaticaly: keep in mind any
32 * method that modifies the path will invalidate the #CpmlPath
33 * returned by adg_trail_get_cpml_path().
35 * Although some of the provided methods are clearly based on the
36 * original cairo path manipulation API, their behavior could be
37 * sligthly different. This is intentional, because the ADG provides
38 * additional path manipulation algorithms, sometime quite complex,
39 * and a more restrictive filter on the path quality is required.
40 * Also, the ADG is designed to be used by technicians while cairo
41 * targets a broader range of developers.
43 * As an example, following the rule of the less surprise, some
44 * cairo functions guess the current point when it is not defined,
45 * while the #AdgPath methods trigger a warning without other effect.
46 * Furthermore, after cairo_path_close_path() a %CAIRO_PATH_MOVE_TO
47 * primitive to the starting point of the segment is automatically
48 * added by cairo; in ADG, after an adg_path_close() the current
49 * point is simply unset.
50 **/
52 /**
53 * AdgPath:
55 * All fields are private and should not be used directly.
56 * Use its public methods instead.
57 **/
60 #include "adg-internal.h"
61 #include "adg-path.h"
62 #include "adg-path-private.h"
63 #include "adg-primitive.h"
64 #include <math.h>
66 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_path_parent_class)
67 #define PARENT_MODEL_CLASS ((AdgModelClass *) adg_path_parent_class)
70 static void finalize (GObject *object);
71 static void clear (AdgModel *model);
72 static void clear_parent (AdgModel *model);
73 static void changed (AdgModel *model);
74 static CpmlPath * get_cpml_path (AdgTrail *trail);
75 static CpmlPath * read_cpml_path (AdgPath *path);
76 static void append_primitive (AdgPath *path,
77 AdgPrimitive *primitive);
78 static gint needed_pairs (CpmlPrimitiveType type);
79 static void clear_operation (AdgPath *path);
80 static gboolean append_operation (AdgPath *path,
81 AdgAction action,
82 ...);
83 static void do_operation (AdgPath *path,
84 cairo_path_data_t
85 *path_data);
86 static void do_action (AdgPath *path,
87 AdgAction action,
88 AdgPrimitive *primitive);
89 static void do_chamfer (AdgPath *path,
90 AdgPrimitive *current);
91 static void do_fillet (AdgPath *path,
92 AdgPrimitive *current);
93 static gboolean is_convex (const AdgPrimitive
94 *primitive1,
95 const AdgPrimitive
96 *primitive2);
97 static void dup_reversed_pair (const gchar *name,
98 AdgPair *pair,
99 gpointer user_data);
100 static const gchar * action_name (AdgAction action);
103 G_DEFINE_TYPE(AdgPath, adg_path, ADG_TYPE_TRAIL);
106 static void
107 adg_path_class_init(AdgPathClass *klass)
109 GObjectClass *gobject_class;
110 AdgModelClass *model_class;
111 AdgTrailClass *trail_class;
113 gobject_class = (GObjectClass *) klass;
114 model_class = (AdgModelClass *) klass;
115 trail_class = (AdgTrailClass *) klass;
117 g_type_class_add_private(klass, sizeof(AdgPathPrivate));
119 gobject_class->finalize = finalize;
121 model_class->clear = clear;
122 model_class->changed = changed;
124 trail_class->get_cpml_path = get_cpml_path;
127 static void
128 adg_path_init(AdgPath *path)
130 AdgPathPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(path, ADG_TYPE_PATH,
131 AdgPathPrivate);
133 data->cp_is_valid = FALSE;
134 data->cpml.array = g_array_new(FALSE, FALSE, sizeof(cairo_path_data_t));
135 data->operation.action = ADG_ACTION_NONE;
137 path->data = data;
140 static void
141 finalize(GObject *object)
143 AdgPath *path;
144 AdgPathPrivate *data;
146 path = (AdgPath *) object;
147 data = path->data;
149 g_array_free(data->cpml.array, TRUE);
150 clear_operation(path);
152 if (PARENT_OBJECT_CLASS->finalize)
153 PARENT_OBJECT_CLASS->finalize(object);
158 * adg_path_new:
160 * Creates a new path model. The path should be constructed
161 * programmatically by using the methods provided by #AdgPath.
163 * Returns: the newly created path model
165 AdgPath *
166 adg_path_new(void)
168 return g_object_new(ADG_TYPE_PATH, NULL);
172 * adg_path_get_current_point:
173 * @path: an #AdgPath
175 * Gets the current point of @path, which is conceptually the
176 * final point reached by the path so far.
178 * If there is no defined current point, %NULL is returned.
179 * It is possible to check this in advance with
180 * adg_path_has_current_point().
182 * Most #AdgPath methods alter the current point and most of them
183 * expect a current point to be defined otherwise will fail triggering
184 * a warning. Check the description of every method for specific details.
186 * Returns: the current point or %NULL on no current point set or errors
188 const AdgPair *
189 adg_path_get_current_point(AdgPath *path)
191 AdgPathPrivate *data;
193 g_return_val_if_fail(ADG_IS_PATH(path), NULL);
195 data = path->data;
197 if (!data->cp_is_valid)
198 return NULL;
200 return &data->cp;
204 * adg_path_has_current_point:
205 * @path: an #AdgPath
207 * Returns whether a current point is defined on @path.
208 * See adg_path_get_current_point() for details on the current point.
210 * Returns: whether a current point is defined
212 gboolean
213 adg_path_has_current_point(AdgPath *path)
215 AdgPathPrivate *data;
217 g_return_val_if_fail(ADG_IS_PATH(path), FALSE);
219 data = path->data;
221 return data->cp_is_valid;
225 * adg_path_last_primitive:
226 * @path: an #AdgPath
228 * Gets the last primitive appended to @path. The returned struct
229 * is owned by @path and should not be freed or modified.
231 * Returns: a pointer to the last appended primitive or %NULL on errors
233 const AdgPrimitive *
234 adg_path_last_primitive(AdgPath *path)
236 AdgPathPrivate *data;
238 g_return_val_if_fail(ADG_IS_PATH(path), NULL);
240 data = path->data;
242 return &data->last;
246 * adg_path_over_primitive:
247 * @path: an #AdgPath
249 * Gets the primitive before the last one appended to @path. The
250 * "over" term comes from forth, where the %OVER operator works
251 * on the stack in the same way as adg_path_over_primitive() works
252 * on @path. The returned struct is owned by @path and should not
253 * be freed or modified.
255 * Returns: a pointer to the primitive before the last appended one
256 * or %NULL on errors
258 const AdgPrimitive *
259 adg_path_over_primitive(AdgPath *path)
261 AdgPathPrivate *data;
263 g_return_val_if_fail(ADG_IS_PATH(path), NULL);
265 data = path->data;
267 return &data->over;
271 * adg_path_append:
272 * @path: an #AdgPath
273 * @type: a #cairo_data_type_t value
274 * @...: point data, specified as #AdgPair pointers
276 * Generic method to append a primitive to @path. The number of #AdgPair
277 * structs depends on @type: there is no way with this function to
278 * reserve more cairo_path_data_t structs than what is needed by the
279 * primitive.
281 * This function accepts also the special %CAIRO_PATH_ARC_TO primitive.
283 * If @path has no current point while the requested primitive needs it,
284 * a warning message will be triggered without other effect.
286 void
287 adg_path_append(AdgPath *path, CpmlPrimitiveType type, ...)
289 va_list var_args;
291 va_start(var_args, type);
292 adg_path_append_valist(path, type, var_args);
293 va_end(var_args);
297 * adg_path_append_valist:
298 * @path: an #AdgPath
299 * @type: a #cairo_data_type_t value
300 * @var_args: point data, specified as #AdgPair pointers
302 * va_list version of adg_path_append().
304 void
305 adg_path_append_valist(AdgPath *path, CpmlPrimitiveType type, va_list var_args)
307 AdgPathPrivate *data;
308 AdgPrimitive primitive;
309 gint length, cnt;
310 cairo_path_data_t org;
311 cairo_path_data_t *path_data;
312 const AdgPair *pair;
314 g_return_if_fail(ADG_IS_PATH(path));
316 data = path->data;
317 length = needed_pairs(type);
318 if (length == 0)
319 return;
321 /* Set a copy of the current point as the primitive origin */
322 cpml_pair_to_cairo(&data->cp, &org);
323 primitive.org = &org;
325 /* Build the cairo_path_data_t array */
326 primitive.data = path_data = g_new(cairo_path_data_t, length);
328 path_data->header.type = type;
329 path_data->header.length = length;
331 for (cnt = 1; cnt < length; ++ cnt) {
332 pair = va_arg(var_args, AdgPair *);
333 if (pair == NULL) {
334 g_free(primitive.data);
335 g_warning(_("%s: null pair caught while parsing arguments"),
336 G_STRLOC);
337 return;
340 ++ path_data;
341 cpml_pair_to_cairo(pair, path_data);
344 /* Terminate the creation of the temporary primitive */
345 primitive.segment = NULL;
347 /* Append this primitive to @path */
348 append_primitive(path, &primitive);
350 g_free(primitive.data);
354 * adg_path_append_primitive:
355 * @path: an #AdgPath
356 * @primitive: the #AdgPrimitive to append
358 * Appends @primitive to @path. The primitive to add is considered the
359 * continuation of the current path so the <structfield>org</structfield>
360 * component of @primitive is not used. Anyway the current point is
361 * checked against it: they must be equal or the function will fail
362 * without further processing.
364 void
365 adg_path_append_primitive(AdgPath *path, const AdgPrimitive *primitive)
367 AdgPathPrivate *data;
368 AdgPrimitive *primitive_dup;
370 g_return_if_fail(ADG_IS_PATH(path));
371 g_return_if_fail(primitive != NULL);
372 g_return_if_fail(primitive->org != NULL);
374 data = path->data;
376 g_return_if_fail(primitive->org->point.x == data->cp.x &&
377 primitive->org->point.y == data->cp.y);
379 /* The primitive data could be modified by pending operations:
380 * work on a copy */
381 primitive_dup = adg_primitive_deep_dup(primitive);
383 append_primitive(path, primitive_dup);
385 g_free(primitive_dup);
389 * adg_path_append_segment:
390 * @path: an #AdgPath
391 * @segment: the #AdgSegment to append
393 * Appends @segment to @path.
395 void
396 adg_path_append_segment(AdgPath *path, const AdgSegment *segment)
398 AdgPathPrivate *data;
400 g_return_if_fail(ADG_IS_PATH(path));
401 g_return_if_fail(segment != NULL);
403 data = path->data;
405 clear_parent((AdgModel *) path);
406 data->cpml.array = g_array_append_vals(data->cpml.array,
407 segment->data, segment->num_data);
411 * adg_path_append_cpml_path:
412 * @path: an #AdgPath
413 * @cpml_path: the #cairo_path_t path to append
415 * Appends a whole #CpmlPath to @path. #CpmlPath is a superset of
416 * #cairo_path_t, so this function can be feeded with both.
418 void
419 adg_path_append_cpml_path(AdgPath *path, const CpmlPath *cpml_path)
421 AdgPathPrivate *data;
423 g_return_if_fail(ADG_IS_PATH(path));
424 g_return_if_fail(cpml_path != NULL);
426 data = path->data;
428 clear_parent((AdgModel *) path);
429 data->cpml.array = g_array_append_vals(data->cpml.array,
430 cpml_path->data,
431 cpml_path->num_data);
435 * adg_path_move_to:
436 * @path: an #AdgPath
437 * @pair: the destination coordinates
439 * Begins a new segment. After this call the current point will be @pair.
441 void
442 adg_path_move_to(AdgPath *path, const AdgPair *pair)
444 adg_path_append(path, CAIRO_PATH_MOVE_TO, pair);
448 * adg_path_move_to_explicit:
449 * @path: an #AdgPath
450 * @x: the new x coordinate
451 * @y: the new y coordinate
453 * Convenient function to call adg_path_move_to() using explicit
454 * coordinates instead of #AdgPair.
456 void
457 adg_path_move_to_explicit(AdgPath *path, gdouble x, gdouble y)
459 AdgPair p;
461 p.x = x;
462 p.y = y;
464 adg_path_append(path, CAIRO_PATH_MOVE_TO, &p);
468 * adg_path_line_to:
469 * @path: an #AdgPath
470 * @pair: the destination coordinates
472 * Adds a line to @path from the current point to @pair. After this
473 * call the current point will be @pair.
475 * If @path has no current point before this call, this function will
476 * trigger a warning without other effect.
478 void
479 adg_path_line_to(AdgPath *path, const AdgPair *pair)
481 adg_path_append(path, CAIRO_PATH_LINE_TO, pair);
485 * adg_path_line_to_explicit:
486 * @path: an #AdgPath
487 * @x: the new x coordinate
488 * @y: the new y coordinate
490 * Convenient function to call adg_path_line_to() using explicit
491 * coordinates instead of #AdgPair.
493 void
494 adg_path_line_to_explicit(AdgPath *path, gdouble x, gdouble y)
496 AdgPair p;
498 p.x = x;
499 p.y = y;
501 adg_path_append(path, CAIRO_PATH_LINE_TO, &p);
505 * adg_path_arc_to:
506 * @path: an #AdgPath
507 * @throught: an arbitrary point on the arc
508 * @pair: the destination coordinates
510 * Adds an arc to the path from the current point to @pair, passing
511 * throught @throught. After this call the current point will be @pair.
513 * If @path has no current point before this call, this function will
514 * trigger a warning without other effect.
516 void
517 adg_path_arc_to(AdgPath *path, const AdgPair *throught, const AdgPair *pair)
519 adg_path_append(path, CAIRO_PATH_ARC_TO, throught, pair);
523 * adg_path_arc_to_explicit:
524 * @path: an #AdgPath
525 * @x1: the x coordinate of an intermediate point
526 * @y1: the y coordinate of an intermediate point
527 * @x2: the x coordinate of the end of the arc
528 * @y2: the y coordinate of the end of the arc
530 * Convenient function to call adg_path_arc_to() using explicit
531 * coordinates instead of #AdgPair.
533 void
534 adg_path_arc_to_explicit(AdgPath *path, gdouble x1, gdouble y1,
535 gdouble x2, gdouble y2)
537 AdgPair p[2];
539 p[0].x = x1;
540 p[0].y = y1;
541 p[1].x = x2;
542 p[1].y = y2;
544 adg_path_append(path, CAIRO_PATH_ARC_TO, &p[0], &p[1]);
548 * adg_path_curve_to:
549 * @path: an #AdgPath
550 * @control1: the first control point of the curve
551 * @control2: the second control point of the curve
552 * @pair: the destination coordinates
554 * Adds a cubic Bézier curve to the path from the current point to
555 * position @pair, using @control1 and @control2 as control points.
556 * After this call the current point will be @pair.
558 * If @path has no current point before this call, this function will
559 * trigger a warning without other effect.
561 void
562 adg_path_curve_to(AdgPath *path, const AdgPair *control1,
563 const AdgPair *control2, const AdgPair *pair)
565 adg_path_append(path, CAIRO_PATH_CURVE_TO, control1, control2, pair);
569 * adg_path_curve_to_explicit:
570 * @path: an #AdgPath
571 * @x1: the x coordinate of the first control point
572 * @y1: the y coordinate of the first control point
573 * @x2: the x coordinate of the second control point
574 * @y2: the y coordinate of the second control point
575 * @x3: the x coordinate of the end of the curve
576 * @y3: the y coordinate of the end of the curve
578 * Convenient function to call adg_path_curve_to() using explicit
579 * coordinates instead of #AdgPair.
581 void
582 adg_path_curve_to_explicit(AdgPath *path, gdouble x1, gdouble y1,
583 gdouble x2, gdouble y2, gdouble x3, gdouble y3)
585 AdgPair p[3];
587 p[0].x = x1;
588 p[0].y = y1;
589 p[1].x = x2;
590 p[1].y = y2;
591 p[2].x = x3;
592 p[2].y = y3;
594 adg_path_append(path, CAIRO_PATH_CURVE_TO, &p[0], &p[1], &p[2]);
598 * adg_path_close:
599 * @path: an #AdgPath
601 * Adds a line segment to the path from the current point to the
602 * beginning of the current segment, (the most recent point passed
603 * to an adg_path_move_to()), and closes this segment.
604 * After this call the current point will be unset.
606 * The behavior of adg_path_close() is distinct from simply calling
607 * adg_line_to() with the coordinates of the segment starting point.
608 * When a closed segment is stroked, there are no caps on the ends.
609 * Instead, there is a line join connecting the final and initial
610 * primitive of the segment.
612 * If @path has no current point before this call, this function will
613 * trigger a warning without other effect.
615 void
616 adg_path_close(AdgPath *path)
618 adg_path_append(path, CAIRO_PATH_CLOSE_PATH);
622 * adg_path_arc:
623 * @path: an #AdgPath
624 * @center: coordinates of the center of the arc
625 * @r: the radius of the arc
626 * @start: the start angle, in radians
627 * @end: the end angle, in radians
629 * A more usual way to add an arc to @path. After this call, the current
630 * point will be the computed end point of the arc. The arc will be
631 * rendered in increasing angle, accordling to @start and @end. This means
632 * if @start is less than @end, the arc will be rendered in clockwise
633 * direction (accordling to the default cairo coordinate system) while if
634 * @start is greather than @end, the arc will be rendered in couterclockwise
635 * direction.
637 * By explicitely setting the whole arc data, the start point could be
638 * different from the current point. In this case, if @path has no
639 * current point before the call a %CAIRO_PATH_MOVE_TO to the start
640 * point of the arc will be automatically prepended to the arc.
641 * If @path has a current point, a %CAIRO_PATH_LINE_TO to the start
642 * point of the arc will be used instead of the "move to" primitive.
644 void
645 adg_path_arc(AdgPath *path, const AdgPair *center, gdouble r,
646 gdouble start, gdouble end)
648 AdgPathPrivate *data;
649 AdgPair p[3];
651 g_return_if_fail(ADG_IS_PATH(path));
652 g_return_if_fail(center != NULL);
654 data = path->data;
655 cpml_vector_from_angle(&p[0], start);
656 cpml_vector_from_angle(&p[1], (end-start) / 2);
657 cpml_vector_from_angle(&p[2], end);
659 cpml_vector_set_length(&p[0], r);
660 cpml_vector_set_length(&p[1], r);
661 cpml_vector_set_length(&p[2], r);
663 cpml_pair_add(&p[0], center);
664 cpml_pair_add(&p[1], center);
665 cpml_pair_add(&p[2], center);
667 if (!data->cp_is_valid)
668 adg_path_append(path, CAIRO_PATH_MOVE_TO, &p[0]);
669 else if (p[0].x != data->cp.x || p[0].y != data->cp.y)
670 adg_path_append(path, CAIRO_PATH_LINE_TO, &p[0]);
672 adg_path_append(path, CAIRO_PATH_ARC_TO, &p[1], &p[2]);
676 * adg_path_arc_explicit:
677 * @path: an #AdgPath
678 * @xc: x position of the center of the arc
679 * @yc: y position of the center of the arc
680 * @r: the radius of the arc
681 * @start: the start angle, in radians
682 * @end: the end angle, in radians
684 * Convenient function to call adg_path_arc() using explicit
685 * coordinates instead of #AdgPair.
687 void
688 adg_path_arc_explicit(AdgPath *path, gdouble xc, gdouble yc, gdouble r,
689 gdouble start, gdouble end)
691 AdgPair center;
693 center.x = xc;
694 center.y = yc;
696 adg_path_arc(path, &center, r, start, end);
700 * adg_path_chamfer
701 * @path: an #AdgPath
702 * @delta1: the distance from the intersection point of the current primitive
703 * @delta2: the distance from the intersection point of the next primitive
705 * A binary action that generates a chamfer between two primitives.
706 * The first primitive involved is the current primitive, the second will
707 * be the next primitive appended to @path after this call. The second
708 * primitive is required: if the chamfer operation is not properly
709 * terminated (by not providing the second primitive), any API accessing
710 * the path in reading mode will raise a warning.
712 * An exception is a chamfer after a %CAIRO_PATH_CLOSE_PATH primitive.
713 * In this case the second primitive is not required: the current close
714 * path is used as first operand while the first primitive of the
715 * current segment is used as second operand.
717 * The chamfer operation requires two lengths: @delta1 specifies the
718 * "quantity" to trim on the first primitive while @delta2 is the same
719 * applied on the second primitive. The term "quantity" means the length
720 * of the portion to cut out from the original primitive (that is the
721 * primitive as would be without the chamfer).
723 void
724 adg_path_chamfer(AdgPath *path, gdouble delta1, gdouble delta2)
726 g_return_if_fail(ADG_IS_PATH(path));
728 if (!append_operation(path, ADG_ACTION_CHAMFER, delta1, delta2))
729 return;
733 * adg_path_fillet:
734 * @path: an #AdgPath
735 * @radius: the radius of the fillet
737 * A binary action that joins to primitives with an arc.
738 * The first primitive involved is the current primitive, the second will
739 * be the next primitive appended to @path after this call. The second
740 * primitive is required: if the fillet operation is not properly
741 * terminated (by not providing the second primitive), any API accessing
742 * the path in reading mode will raise a warning.
744 * An exception is a fillet after a %CAIRO_PATH_CLOSE_PATH primitive.
745 * In this case the second primitive is not required: the current close
746 * path is used as first operand while the first primitive of the
747 * current segment is used as second operand.
749 void
750 adg_path_fillet(AdgPath *path, gdouble radius)
752 g_return_if_fail(ADG_IS_PATH(path));
754 if (!append_operation(path, ADG_ACTION_FILLET, radius))
755 return;
759 * adg_path_reflect:
760 * @path: an #AdgPath
761 * @vector: the slope of the axis
763 * Reflects the first segment or @path around the axis passing
764 * throught (0, 0) and with a @vector slope. The internal segment
765 * is duplicated and the proper transformation (computed from
766 * @vector) to mirror the segment is applied on all its points.
767 * The result is then reversed with cpml_segment_reverse() and
768 * appended to the original path with adg_path_append_segment().
770 * For convenience, if @vector is %NULL the path is reversed
771 * around the x axis (y=0).
773 void
774 adg_path_reflect(AdgPath *path, const CpmlVector *vector)
776 AdgNamedPairData data;
777 AdgSegment segment, *dup_segment;
779 g_return_if_fail(ADG_IS_PATH(path));
780 g_return_if_fail(vector == NULL || vector->x != 0 || vector->y != 0);
782 data.model = (AdgModel *) path;
784 if (vector == NULL) {
785 cairo_matrix_init_scale(&data.matrix, 1, -1);
786 } else {
787 CpmlVector slope;
788 gdouble cos2angle, sin2angle;
790 cpml_pair_copy(&slope, vector);
791 cpml_vector_set_length(&slope, 1);
793 if (slope.x == 0 && slope.y == 0) {
794 g_warning(_("%s: the axis of the reflection is not known"),
795 G_STRLOC);
796 return;
799 sin2angle = 2. * vector->x * vector->y;
800 cos2angle = 2. * vector->x * vector->x - 1;
802 cairo_matrix_init(&data.matrix, cos2angle, sin2angle,
803 sin2angle, -cos2angle, 0, 0);
806 if (!adg_trail_put_segment((AdgTrail *) path, 1, &segment))
807 return;
809 /* No need to reverse an empty segment */
810 if (segment.num_data == 0 || segment.num_data == 0)
811 return;
813 dup_segment = adg_segment_deep_dup(&segment);
814 if (dup_segment == NULL)
815 return;
817 cpml_segment_reverse(dup_segment);
818 cpml_segment_transform(dup_segment, &data.matrix);
819 dup_segment->data[0].header.type = CAIRO_PATH_LINE_TO;
821 adg_path_append_segment(path, dup_segment);
823 g_free(dup_segment);
825 adg_model_foreach_named_pair(data.model, dup_reversed_pair, &data);
829 static void
830 clear(AdgModel *model)
832 AdgPath *path;
833 AdgPathPrivate *data;
835 path = (AdgPath *) model;
836 data = path->data;
838 g_array_set_size(data->cpml.array, 0);
839 clear_operation(path);
840 clear_parent(model);
843 static void
844 clear_parent(AdgModel *model)
846 if (PARENT_MODEL_CLASS->clear)
847 PARENT_MODEL_CLASS->clear(model);
850 static void
851 changed(AdgModel *model)
853 clear_parent(model);
855 if (PARENT_MODEL_CLASS->changed)
856 PARENT_MODEL_CLASS->changed(model);
859 static CpmlPath *
860 get_cpml_path(AdgTrail *trail)
862 clear_parent((AdgModel *) trail);
863 return read_cpml_path((AdgPath *) trail);
866 static CpmlPath *
867 read_cpml_path(AdgPath *path)
869 AdgPathPrivate *data = path->data;
871 /* Always regenerate the CpmlPath as it is a trivial operation */
872 data->cpml.path.status = CAIRO_STATUS_SUCCESS;
873 data->cpml.path.data = (cairo_path_data_t *) (data->cpml.array)->data;
874 data->cpml.path.num_data = (data->cpml.array)->len;
876 return &data->cpml.path;
879 static void
880 append_primitive(AdgPath *path, AdgPrimitive *current)
882 AdgPathPrivate *data;
883 cairo_path_data_t *path_data;
884 int length;
886 data = path->data;
887 path_data = current->data;
888 length = path_data[0].header.length;
890 /* Execute any pending operation */
891 do_operation(path, path_data);
893 /* Append the path data to the internal path array */
894 data->cpml.array = g_array_append_vals(data->cpml.array,
895 path_data, length);
897 /* Set path data to point to the recently appended cairo_path_data_t
898 * primitive: the first struct is the header */
899 path_data = (cairo_path_data_t *) (data->cpml.array)->data +
900 (data->cpml.array)->len - length;
902 /* Store the over primitive */
903 memcpy(&data->over, &data->last, sizeof(AdgPrimitive));
905 /* Set the last primitive for subsequent binary operations */
906 data->last.org = data->cp_is_valid ? path_data - 1 : NULL;
907 data->last.segment = NULL;
908 data->last.data = path_data;
910 /* Save the last point as the current point, if applicable */
911 data->cp_is_valid = length > 1;
912 if (length > 1)
913 cpml_pair_from_cairo(&data->cp, &path_data[length-1]);
915 /* Invalidate cairo_path: should be recomputed */
916 clear_parent((AdgModel *) path);
919 static gint
920 needed_pairs(CpmlPrimitiveType type)
922 switch (type) {
923 case CAIRO_PATH_CLOSE_PATH:
924 return 1;
925 case CAIRO_PATH_MOVE_TO:
926 return 2;
927 case CAIRO_PATH_LINE_TO:
928 return 2;
929 case CAIRO_PATH_ARC_TO:
930 return 3;
931 case CAIRO_PATH_CURVE_TO:
932 return 4;
933 default:
934 g_return_val_if_reached(0);
937 return 0;
940 static void
941 clear_operation(AdgPath *path)
943 AdgPathPrivate *data;
944 AdgOperation *operation;
946 data = path->data;
947 operation = &data->operation;
949 if (operation->action != ADG_ACTION_NONE) {
950 g_warning(_("%s: a `%s' operation is still active while clearing the path"),
951 G_STRLOC, action_name(operation->action));
952 operation->action = ADG_ACTION_NONE;
956 static gboolean
957 append_operation(AdgPath *path, AdgAction action, ...)
959 AdgPathPrivate *data;
960 AdgOperation *operation;
961 va_list var_args;
963 data = path->data;
965 if (data->last.data == NULL) {
966 g_warning(_("%s: requested a `%s' operation on a path without current primitive"),
967 G_STRLOC, action_name(action));
968 return FALSE;
971 operation = &data->operation;
972 if (operation->action != ADG_ACTION_NONE) {
973 g_warning(_("%s: requested a `%s' operation while a `%s' operation is active"),
974 G_STRLOC, action_name(action), action_name(operation->action));
975 ADG_MESSAGE("TODO: this is a rude simplification, as a lot of "
976 "actions could be chained up. As an example, a fillet "
977 "followed by a polar chamfer is quite common.");
978 return FALSE;
981 va_start(var_args, action);
983 switch (action) {
985 case ADG_ACTION_CHAMFER:
986 operation->data.chamfer.delta1 = va_arg(var_args, double);
987 operation->data.chamfer.delta2 = va_arg(var_args, double);
988 break;
990 case ADG_ACTION_FILLET:
991 operation->data.fillet.radius = va_arg(var_args, double);
992 break;
994 case ADG_ACTION_NONE:
995 va_end(var_args);
996 return TRUE;
998 default:
999 g_warning(_("%s: `%d' operation not recognized"), G_STRLOC, action);
1000 va_end(var_args);
1001 return FALSE;
1004 operation->action = action;
1005 va_end(var_args);
1007 if (data->last.data[0].header.type == CAIRO_PATH_CLOSE_PATH) {
1008 /* Special case: an action with the close primitive should
1009 * be resolved now by changing the close primitive to a
1010 * line-to and using it as second operand and use the first
1011 * primitive of the current segment as first operand */
1012 guint length;
1013 cairo_path_data_t *path_data;
1014 CpmlSegment segment;
1015 CpmlPrimitive current;
1017 length = data->cpml.array->len;
1019 /* Ensure the close path primitive is not the only data */
1020 g_return_val_if_fail(length > 1, FALSE);
1022 /* Allocate one more item once for all to accept the
1023 * conversion from a close to line-to primitive */
1024 data->cpml.array = g_array_set_size(data->cpml.array, length + 1);
1025 path_data = (cairo_path_data_t *) data->cpml.array->data;
1026 --data->cpml.array->len;
1028 /* Set segment and current (the first primitive of segment) */
1029 cpml_segment_from_cairo(&segment, read_cpml_path(path));
1030 while (cpml_segment_next(&segment))
1032 cpml_primitive_from_segment(&current, &segment);
1034 /* Convert close path to a line-to primitive */
1035 ++data->cpml.array->len;
1036 path_data[length - 1].header.type = CAIRO_PATH_LINE_TO;
1037 path_data[length - 1].header.length = 2;
1038 path_data[length] = *current.org;
1040 data->last.segment = &segment;
1041 data->last.org = &path_data[length - 2];
1042 data->last.data = &path_data[length - 1];
1044 do_action(path, action, &current);
1048 return TRUE;
1051 static void
1052 do_operation(AdgPath *path, cairo_path_data_t *path_data)
1054 AdgPathPrivate *data;
1055 AdgAction action;
1056 AdgSegment segment;
1057 AdgPrimitive current;
1058 cairo_path_data_t current_org;
1060 data = path->data;
1061 action = data->operation.action;
1062 cpml_segment_from_cairo(&segment, read_cpml_path(path));
1064 /* Construct the current primitive, that is the primitive to be
1065 * mixed with the last primitive with the specified operation.
1066 * Its org is a copy of the end point of the last primitive: it can be
1067 * modified without affecting anything else. It is expected the operation
1068 * functions will add to @path the primitives required but NOT to add
1069 * @current, as this one will be inserted automatically. */
1070 current.segment = &segment;
1071 current.org = &current_org;
1072 current.data = path_data;
1073 cpml_pair_to_cairo(&data->cp, &current_org);
1075 do_action(path, action, &current);
1078 static void
1079 do_action(AdgPath *path, AdgAction action, AdgPrimitive *primitive)
1081 switch (action) {
1082 case ADG_ACTION_NONE:
1083 return;
1084 case ADG_ACTION_CHAMFER:
1085 do_chamfer(path, primitive);
1086 break;
1087 case ADG_ACTION_FILLET:
1088 do_fillet(path, primitive);
1089 break;
1090 default:
1091 g_return_if_reached();
1095 static void
1096 do_chamfer(AdgPath *path, AdgPrimitive *current)
1098 AdgPathPrivate *data;
1099 AdgPrimitive *last;
1100 gdouble delta1, delta2;
1101 gdouble len1, len2;
1102 AdgPair pair;
1104 data = path->data;
1105 last = &data->last;
1106 delta1 = data->operation.data.chamfer.delta1;
1107 len1 = cpml_primitive_get_length(last);
1109 if (delta1 >= len1) {
1110 g_warning(_("%s: first chamfer delta of `%lf' is greather than the available `%lf' length"),
1111 G_STRLOC, delta1, len1);
1112 return;
1115 delta2 = data->operation.data.chamfer.delta2;
1116 len2 = cpml_primitive_get_length(current);
1118 if (delta2 >= len2) {
1119 g_warning(_("%s: second chamfer delta of `%lf' is greather than the available `%lf' length"),
1120 G_STRLOC, delta1, len1);
1121 return;
1124 /* Change the end point of the last primitive */
1125 cpml_primitive_pair_at(last, &pair, 1. - delta1 / len1);
1126 cpml_pair_to_cairo(&pair, cpml_primitive_get_point(last, -1));
1128 /* Change the start point of the current primitive */
1129 cpml_primitive_pair_at(current, &pair, delta2 / len2);
1130 cpml_pair_to_cairo(&pair, cpml_primitive_get_point(current, 0));
1132 /* Add the chamfer line */
1133 data->operation.action = ADG_ACTION_NONE;
1134 adg_path_append(path, CAIRO_PATH_LINE_TO, &pair);
1137 static void
1138 do_fillet(AdgPath *path, AdgPrimitive *current)
1140 AdgPathPrivate *data;
1141 AdgPrimitive *last, *current_dup, *last_dup;
1142 gdouble radius, offset, pos;
1143 AdgPair center, vector, p[3];
1145 data = path->data;
1146 last = &data->last;
1147 current_dup = adg_primitive_deep_dup(current);
1148 last_dup = adg_primitive_deep_dup(last);
1149 radius = data->operation.data.fillet.radius;
1150 offset = is_convex(last_dup, current_dup) ? -radius : radius;
1152 /* Find the center of the fillet from the intersection between
1153 * the last and current primitives offseted by radius */
1154 cpml_primitive_offset(current_dup, offset);
1155 cpml_primitive_offset(last_dup, offset);
1156 if (cpml_primitive_intersection(current_dup, last_dup,
1157 &center, 1) == 0) {
1158 g_warning(_("%s: fillet with radius of `%lf' is not applicable here"),
1159 G_STRLOC, radius);
1160 g_free(current_dup);
1161 g_free(last_dup);
1162 return;
1165 /* Compute the start point of the fillet */
1166 pos = cpml_primitive_near_pos(last_dup, &center);
1167 cpml_primitive_vector_at(last_dup, &vector, pos);
1168 cpml_vector_set_length(&vector, offset);
1169 cpml_vector_normal(&vector);
1170 cpml_pair_copy(&p[0], &center);
1171 cpml_pair_sub(&p[0], &vector);
1173 /* Compute the mid point of the fillet */
1174 cpml_pair_from_cairo(&vector, current->org);
1175 cpml_pair_sub(&vector, &center);
1176 cpml_vector_set_length(&vector, radius);
1177 cpml_pair_copy(&p[1], &center);
1178 cpml_pair_add(&p[1], &vector);
1180 /* Compute the end point of the fillet */
1181 pos = cpml_primitive_near_pos(current_dup, &center);
1182 cpml_primitive_vector_at(current_dup, &vector, pos);
1183 cpml_vector_set_length(&vector, offset);
1184 cpml_vector_normal(&vector);
1185 cpml_pair_copy(&p[2], &center);
1186 cpml_pair_sub(&p[2], &vector);
1188 g_free(current_dup);
1189 g_free(last_dup);
1191 /* Change the end point of the last primitive */
1192 cpml_pair_to_cairo(&p[0], cpml_primitive_get_point(last, -1));
1194 /* Change the start point of the current primitive */
1195 cpml_pair_to_cairo(&p[2], cpml_primitive_get_point(current, 0));
1197 /* Add the fillet arc */
1198 data->operation.action = ADG_ACTION_NONE;
1199 adg_path_append(path, CAIRO_PATH_ARC_TO, &p[1], &p[2]);
1202 static gboolean
1203 is_convex(const AdgPrimitive *primitive1, const AdgPrimitive *primitive2)
1205 CpmlVector v1, v2;
1206 gdouble angle1, angle2;
1208 cpml_primitive_vector_at(primitive1, &v1, -1);
1209 cpml_primitive_vector_at(primitive2, &v2, 0);
1211 /* Probably there is a smarter way to get this without trygonometry */
1212 angle1 = cpml_vector_angle(&v1);
1213 angle2 = cpml_vector_angle(&v2);
1215 if (angle1 > angle2)
1216 angle1 -= M_PI*2;
1218 return angle2-angle1 > M_PI;
1221 static void
1222 dup_reversed_pair(const gchar *name, AdgPair *pair, gpointer user_data)
1224 AdgNamedPairData *data;
1225 gchar *new_name;
1226 AdgPair new_pair;
1228 data = (AdgNamedPairData *) user_data;
1229 new_name = g_strdup_printf("-%s", name);
1230 cpml_pair_copy(&new_pair, pair);
1232 cpml_pair_transform(&new_pair, &data->matrix);
1233 adg_model_set_named_pair(data->model, new_name, &new_pair);
1235 g_free(new_name);
1238 static const gchar *
1239 action_name(AdgAction action)
1241 switch (action) {
1242 case ADG_ACTION_NONE:
1243 return "NULL";
1244 case ADG_ACTION_CHAMFER:
1245 return "CHAMFER";
1246 case ADG_ACTION_FILLET:
1247 return "FILLET";
1250 return "undefined";