[cairo-commit] goocanvas/src goocanvasellipse.c, 1.8, 1.9 goocanvasgroup.c, 1.17, 1.18 goocanvasimage.c, 1.11, 1.12 goocanvasitemsimple.c, 1.19, 1.20 goocanvasitemsimple.h, 1.16, 1.17 goocanvaspath.c, 1.9, 1.10 goocanvaspath.h, 1.7, 1.8 goocanvaspolyline.c, 1.10, 1.11 goocanvasrect.c, 1.8, 1.9 goocanvastext.c, 1.10, 1.11 goocanvasutils.c, 1.9, 1.10 goocanvasutils.h, 1.10, 1.11

Damon Chaplin commit at pdx.freedesktop.org
Thu Feb 15 06:18:56 PST 2007


Committed by: damon

Update of /cvs/cairo/goocanvas/src
In directory kemper:/tmp/cvs-serv30383/src

Modified Files:
	goocanvasellipse.c goocanvasgroup.c goocanvasimage.c 
	goocanvasitemsimple.c goocanvasitemsimple.h goocanvaspath.c 
	goocanvaspath.h goocanvaspolyline.c goocanvasrect.c 
	goocanvastext.c goocanvasutils.c goocanvasutils.h 
Log Message:
2007-02-15  Damon Chaplin  <damon at gnome.org>

	* src/goocanvasutils.c: 
	* src/goocanvaspath.c: moved data structures and code for parsing and
	creating SVG paths from goocanvaspath.[hc] to goocanvasutils.[hc].
	Added goo_canvas_parse_path_data() and goo_canvas_create_path() public
	functions.

	* src/goocanvasitemsimple.c: added "clip-path" and "clip-fill-rule"
	properties to specify the clip path and fill rule, and used these
	to calculate bounds, paint, and do hit testing.

	* src/goocanvasitemsimple.h (struct _GooCanvasItemSimpleData): added
	clip_path_commands and clip_fill_rule.

	* src/goocanvastext.c (goo_canvas_text_set_model): 
	* src/goocanvasrect.c (goo_canvas_rect_set_model): 
	* src/goocanvaspolyline.c (goo_canvas_polyline_set_model): 
	* src/goocanvaspath.c (goo_canvas_path_set_model): 
	* src/goocanvasimage.c (goo_canvas_image_set_model): 
	* src/goocanvasgroup.c (goo_canvas_group_set_model): 
	* src/goocanvasellipse.c (goo_canvas_ellipse_set_model): use
	goo_canvas_item_simple_set_model() rather than getting parent iface.
	(For GooCanvasTable this actually caused a crash as when it called
	the GooCanvasGroup function that got the parent iface which pointed to
	itself so it got into an infinite loop.)

	* demo/mv-demo-clipping.c: 
	* demo/demo-clipping.c: new files for clipping demo pages.

	* demo/Makefile.am: 
	* demo/mv-demo.c: 
	* demo/demo.c: added clipping pages.



Index: goocanvasellipse.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasellipse.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- goocanvasellipse.c	2 Feb 2007 21:54:00 -0000	1.8
+++ goocanvasellipse.c	15 Feb 2007 14:18:50 -0000	1.9
@@ -312,8 +312,6 @@
 goo_canvas_ellipse_set_model    (GooCanvasItem      *item,
 				 GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasEllipse *ellipse = (GooCanvasEllipse*) item;
   GooCanvasEllipseModel *emodel = (GooCanvasEllipseModel*) model;
@@ -326,7 +324,7 @@
   ellipse->ellipse_data = &emodel->ellipse_data;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 

Index: goocanvasgroup.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasgroup.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- goocanvasgroup.c	2 Feb 2007 21:54:00 -0000	1.17
+++ goocanvasgroup.c	15 Feb 2007 14:18:50 -0000	1.18
@@ -291,14 +291,12 @@
 goo_canvas_group_set_model (GooCanvasItem       *item,
 			    GooCanvasItemModel  *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasGroup *group = (GooCanvasGroup*) item;
   gint n_children, i;
 
   /* Do the default GooCanvasItemSimple code first. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 
   /* Now add our own handlers. */
   g_signal_connect (model, "child-added",

Index: goocanvasimage.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasimage.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- goocanvasimage.c	2 Feb 2007 21:54:00 -0000	1.11
+++ goocanvasimage.c	15 Feb 2007 14:18:50 -0000	1.12
@@ -373,11 +373,9 @@
 goo_canvas_image_set_model    (GooCanvasItem      *item,
 			       GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasImage *image = (GooCanvasImage*) item;
-  GooCanvasImageModel *emodel = (GooCanvasImageModel*) model;
+  GooCanvasImageModel *imodel = (GooCanvasImageModel*) model;
 
   /* If our data was allocated, free it. */
   if (!simple->model)
@@ -387,10 +385,10 @@
     }
 
   /* Now use the new model's data instead. */
-  image->image_data = &emodel->image_data;
+  image->image_data = &imodel->image_data;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 

Index: goocanvasitemsimple.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasitemsimple.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- goocanvasitemsimple.c	8 Feb 2007 17:54:26 -0000	1.19
+++ goocanvasitemsimple.c	15 Feb 2007 14:18:50 -0000	1.20
@@ -76,7 +76,9 @@
   PROP_POINTER_EVENTS,
   PROP_TITLE,
   PROP_DESCRIPTION,
-  PROP_CAN_FOCUS
+  PROP_CAN_FOCUS,
+  PROP_CLIP_PATH,
+  PROP_CLIP_FILL_RULE
 };
 
 
@@ -259,6 +261,38 @@
 
   g_object_class_override_property (gobject_class, PROP_CAN_FOCUS,
 				    "can-focus");
+
+  /**
+   * GooCanvasItemSimple:clip-path
+   *
+   * The sequence of commands describing the clip path of the item, specified
+   * as a string using the same syntax
+   * as in the <ulink url="http://www.w3.org/Graphics/SVG/">Scalable Vector
+   * Graphics (SVG)</ulink> path element.
+   */
+  /**
+   * GooCanvasItemModelSimple:clip-path
+   *
+   * The sequence of commands describing the clip path of the item, specified
+   * as a string using the same syntax
+   * as in the <ulink url="http://www.w3.org/Graphics/SVG/">Scalable Vector
+   * Graphics (SVG)</ulink> path element.
+   */
+  g_object_class_install_property (gobject_class, PROP_CLIP_PATH,
+				   g_param_spec_string ("clip-path",
+							_("Clip Path"),
+							_("The sequence of path commands specifying the clip path"),
+							NULL,
+							G_PARAM_WRITABLE));
+
+  g_object_class_install_property (gobject_class, PROP_CLIP_FILL_RULE,
+				   g_param_spec_enum ("clip-fill-rule",
+						      _("Clip Fill Rule"),
+						      _("The fill rule used to determine which parts of the item are clipped"),
+						      GOO_TYPE_CAIRO_FILL_RULE,
+						      CAIRO_FILL_RULE_WINDING,
+						      G_PARAM_READWRITE));
+
 }
 
 
@@ -293,6 +327,7 @@
   item->simple_data = g_slice_new0 (GooCanvasItemSimpleData);
   item->simple_data->visibility = GOO_CANVAS_ITEM_VISIBLE;
   item->simple_data->pointer_events = GOO_CANVAS_EVENTS_VISIBLE_PAINTED;
+  item->simple_data->clip_fill_rule = CAIRO_FILL_RULE_WINDING;
   item->need_update = TRUE;
   item->need_entire_subtree_update = TRUE;
 }
@@ -324,6 +359,12 @@
 	  simple_data->style = NULL;
 	}
 
+      if (simple_data->clip_path_commands)
+	{
+	  g_array_free (simple_data->clip_path_commands, TRUE);
+	  simple_data->clip_path_commands = NULL;
+	}
+
       g_slice_free (cairo_matrix_t, simple_data->transform);
     }
 }
@@ -434,6 +475,9 @@
     case PROP_CAN_FOCUS:
       g_value_set_boolean (value, simple_data->can_focus);
       break;
+    case PROP_CLIP_FILL_RULE:
+      g_value_set_enum (value, simple_data->clip_fill_rule);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -657,6 +701,16 @@
     case PROP_CAN_FOCUS:
       simple_data->can_focus = g_value_get_boolean (value);
       break;
+    case PROP_CLIP_PATH:
+      if (simple_data->clip_path_commands)
+	g_array_free (simple_data->clip_path_commands, TRUE);
+      simple_data->clip_path_commands = goo_canvas_parse_path_data (g_value_get_string (value));
+      recompute_bounds = TRUE;
+      break;
+    case PROP_CLIP_FILL_RULE:
+      simple_data->clip_fill_rule = g_value_get_enum (value);
+      recompute_bounds = TRUE;
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -906,6 +960,22 @@
 
   cairo_device_to_user (cr, &user_x, &user_y);
 
+  /* Remove any current translation, to avoid the 16-bit cairo limit. */
+  cairo_get_matrix (cr, &matrix);
+  matrix.x0 = matrix.y0 = 0.0;
+  cairo_set_matrix (cr, &matrix);
+
+  if (simple_data->clip_path_commands)
+    {
+      goo_canvas_create_path (simple_data->clip_path_commands, cr);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      if (!cairo_in_fill (cr, user_x, user_y))
+	{
+	  cairo_restore (cr);
+	  return NULL;
+	}
+    }
+
   if (class->get_item_at)
     {
       found = class->get_item_at (simple, user_x, user_y, cr,
@@ -913,11 +983,6 @@
     }
   else
     {
-      /* Remove any current translation, to avoid the 16-bit cairo limit. */
-      cairo_get_matrix (cr, &matrix);
-      matrix.x0 = matrix.y0 = 0.0;
-      cairo_set_matrix (cr, &matrix);
-
       /* Use the virtual method subclasses define to create the path. */
       class->create_path (simple, cr);
 
@@ -994,12 +1059,16 @@
 					 cairo_t             *cr)
 {
   GooCanvasItemSimpleClass *class = GOO_CANVAS_ITEM_SIMPLE_GET_CLASS (simple);
+  GooCanvasItemSimpleData *simple_data = simple->simple_data;
+  GooCanvasBounds tmp_bounds;
   cairo_matrix_t transform;
 
   simple->need_update = FALSE;
 
   goo_canvas_item_simple_check_style (simple);
 
+  cairo_get_matrix (cr, &transform);
+
   if (class->update)
     {
       class->update (simple, cr);
@@ -1007,15 +1076,31 @@
   else
     {
       /* Use the identity matrix to get the bounds completely in user space. */
-      cairo_get_matrix (cr, &transform);
       cairo_identity_matrix (cr);
-
       class->create_path (simple, cr);
-
       goo_canvas_item_simple_get_path_bounds (simple, cr, &simple->bounds);
+    }
 
-      cairo_set_matrix (cr, &transform);
+  /* Modify the extents by the item's clip path. */
+  if (simple_data->clip_path_commands)
+    {
+      cairo_identity_matrix (cr);
+      goo_canvas_create_path (simple_data->clip_path_commands, cr);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1,
+			  &tmp_bounds.x2, &tmp_bounds.y2);
+      simple->bounds.x1 = MAX (simple->bounds.x1, tmp_bounds.x1);
+      simple->bounds.y1 = MAX (simple->bounds.y1, tmp_bounds.y1);
+      simple->bounds.x2 = MIN (simple->bounds.x2, tmp_bounds.x2);
+      simple->bounds.y2 = MIN (simple->bounds.y2, tmp_bounds.y2);
+
+      if (simple->bounds.x1 > simple->bounds.x2)
+	simple->bounds.x2 = simple->bounds.x1;
+      if (simple->bounds.y1 > simple->bounds.y2)
+	simple->bounds.y2 = simple->bounds.y1;
     }
+
+  cairo_set_matrix (cr, &transform);
 }
 
 
@@ -1175,6 +1260,13 @@
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
 
+  if (simple_data->clip_path_commands)
+    {
+      goo_canvas_create_path (simple_data->clip_path_commands, cr);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      cairo_clip (cr);
+    }
+
   if (class->paint)
     {
       class->paint (simple, cr, bounds);
@@ -1256,22 +1348,28 @@
 }
 
 
-static void
-goo_canvas_item_simple_set_model    (GooCanvasItem      *item,
-				     GooCanvasItemModel *model)
+/**
+ * goo_canvas_item_simple_set_model:
+ * @item: a #GooCanvasItemSimple.
+ * @model: the model that @item will view.
+ * 
+ * This function should be called by subclasses of #GooCanvasItemSimple
+ * in their set_model() method.
+ **/
+void
+goo_canvas_item_simple_set_model (GooCanvasItemSimple  *item,
+				  GooCanvasItemModel   *model)
 {
-  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
-
   g_return_if_fail (model != NULL);
 
-  goo_canvas_item_simple_reset_model (simple);
-  goo_canvas_item_simple_free_data (simple->simple_data);
-  g_slice_free (GooCanvasItemSimpleData, simple->simple_data);
+  goo_canvas_item_simple_reset_model (item);
+  goo_canvas_item_simple_free_data (item->simple_data);
+  g_slice_free (GooCanvasItemSimpleData, item->simple_data);
 
-  simple->model = g_object_ref (model);
-  simple->simple_data = &simple->model->simple_data;
+  item->model = g_object_ref (model);
+  item->simple_data = &item->model->simple_data;
 
-  goo_canvas_item_simple_setup_accessibility (simple);
+  goo_canvas_item_simple_setup_accessibility (item);
 
   g_signal_connect (model, "changed",
 		    G_CALLBACK (goo_canvas_item_model_simple_changed),
@@ -1280,6 +1378,14 @@
 
 
 static void
+goo_canvas_item_simple_set_model_internal    (GooCanvasItem      *item,
+					      GooCanvasItemModel *model)
+{
+  goo_canvas_item_simple_set_model ((GooCanvasItemSimple*) item, model);
+}
+
+
+static void
 canvas_item_interface_init (GooCanvasItemIface *iface)
 {
   iface->get_canvas         = goo_canvas_item_simple_get_canvas;
@@ -1301,7 +1407,7 @@
   iface->is_visible         = goo_canvas_item_simple_is_visible;
 
   iface->get_model          = goo_canvas_item_simple_get_model;
-  iface->set_model          = goo_canvas_item_simple_set_model;
+  iface->set_model          = goo_canvas_item_simple_set_model_internal;
 }
 
 
@@ -1589,6 +1695,7 @@
 {
   smodel->simple_data.visibility = GOO_CANVAS_ITEM_VISIBLE;
   smodel->simple_data.pointer_events = GOO_CANVAS_EVENTS_VISIBLE_PAINTED;
+  smodel->simple_data.clip_fill_rule = CAIRO_FILL_RULE_WINDING;
 }
 
 

Index: goocanvasitemsimple.h
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasitemsimple.h,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- goocanvasitemsimple.h	9 Feb 2007 13:41:03 -0000	1.16
+++ goocanvasitemsimple.h	15 Feb 2007 14:18:50 -0000	1.17
@@ -20,6 +20,8 @@
  * GooCanvasItemSimpleData
  * @style: the style to draw with.
  * @transform: the transformation matrix of the item, or %NULL.
+ * @clip_path_commands: an array of #GooCanvasPathCommand specifying the clip
+ *  path of the item, or %NULL.
  * @visibility_threshold: the threshold scale setting at which to show the item
  *  (if the @visibility setting is set to %VISIBLE_ABOVE_THRESHOLD).
  * @visibility: whether the item is visible, invisible, or visible above a
@@ -27,6 +29,7 @@
  * @pointer_events: the events the item should receive.
  * @can_focus: if the item can take the keyboard focus.
  * @own_style: if the item has its own style, rather than using its parent's.
+ * @clip_fill_rule: the fill rule used for the clip path.
  *
  * This is the data common to both the model and view classes.
  */
@@ -35,11 +38,13 @@
 {
   GooCanvasStyle *style;
   cairo_matrix_t *transform;
+  GArray *clip_path_commands;
   gdouble visibility_threshold;
   GooCanvasItemVisibility visibility	: 2;
   GooCanvasPointerEvents pointer_events : 4;
   guint can_focus                       : 1;
   guint own_style                       : 1;
+  guint clip_fill_rule			: 4;
 };
 
 
@@ -158,6 +163,8 @@
 void     goo_canvas_item_simple_changed            (GooCanvasItemSimple *item,
 						    gboolean             recompute_bounds);
 void     goo_canvas_item_simple_check_style        (GooCanvasItemSimple *item);
+void	 goo_canvas_item_simple_set_model	   (GooCanvasItemSimple *item,
+						    GooCanvasItemModel  *model);
 
 
 

Index: goocanvaspath.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspath.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- goocanvaspath.c	2 Feb 2007 21:54:00 -0000	1.9
+++ goocanvaspath.c	15 Feb 2007 14:18:50 -0000	1.10
@@ -30,7 +30,6 @@
  * g_object_get() and g_object_set().
  */
 #include <config.h>
-#include <math.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
 #include "goocanvaspath.h"
@@ -100,225 +99,7 @@
 static void
 goo_canvas_path_init (GooCanvasPath *path)
 {
-  path->path_commands = g_array_new (0, 0, sizeof (GooCanvasPathCommand));
-}
-
-
-static gdouble
-parse_double (gchar    **pos,
-	      gboolean  *error)
-{
-  gdouble result;
-  gchar *p;
-
-  /* If an error has already occurred, just return. */
-  if (*error)
-    return 0;
-
-  /* Skip whitespace and commas. */
-  p = *pos;
-  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
-    p++;
-
-  /* Parse the double, and set pos to the first char after it. */
-  result = g_ascii_strtod (p, pos);
-
-  /* If no characters were parsed, set the error flag. */
-  if (p == *pos)
-    *error = TRUE;
-
-  return result;
-}
-
-
-static gint
-parse_flag (gchar    **pos,
-	    gboolean  *error)
-{
-  gint result = 0;
-  gchar *p;
-
-  /* If an error has already occurred, just return. */
-  if (*error)
-    return 0;
-
-  /* Skip whitespace and commas. */
-  p = *pos;
-  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
-    p++;
-
-  /* The flag must be a '0' or a '1'. */
-  if (*p == '0')
-    result = 0;
-  else if (*p == '1')
-    result = 1;
-  else
-    {
-      *error = TRUE;
-      return 0;
-    }
-
-  *pos = p + 1;
-
-  return result;
-}
-
-
-static void
-goo_canvas_path_parse_data (GArray            *path_commands,
-			    const gchar       *path_data)
-{
-  GooCanvasPathCommand cmd;
-  gchar *pos, command = 0, next_command;
-  gboolean error = FALSE;
-
-  /* Clear any current commands. */
-  g_array_set_size (path_commands, 0);
-
-  if (!path_data)
-    return;
-
-  pos = (gchar*) path_data;
-  for (;;)
-    {
-      while (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n')
-	pos++;
-      if (!*pos)
-	break;
-
-      next_command = *pos;
-
-      /* If there is no command letter, we use the same command as the last
-	 one, except for the first command, and 'moveto' (which becomes
-	 'lineto'). */
-      if ((next_command < 'a' || next_command > 'z')
-	  && (next_command < 'A' || next_command > 'Z'))
-	{
-	  /* If this is the first command, then set the error flag and assume
-	     a simple close-path command. */
-	  if (!command)
-	    {
-	      error = TRUE;
-	      command = 'Z';
-	    }
-	  /* moveto commands change to lineto. */
-	  else if (command == 'm')
-	    command = 'l';
-	  else if (command == 'M')
-	    command = 'L';
-	}
-      else
-	{
-	  command = next_command;
-	  pos++;
-	}
-
-      cmd.simple.relative = 0;
-      switch (command)
-	{
-	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
-	case 'm':
-	  cmd.simple.relative = 1;
-	case 'M':
-	  cmd.simple.type = GOO_CANVAS_PATH_MOVE_TO;
-	  cmd.simple.x = parse_double (&pos, &error);
-	  cmd.simple.y = parse_double (&pos, &error);
-	  break;
-
-	case 'Z':
-	case 'z':
-	  cmd.simple.type = GOO_CANVAS_PATH_CLOSE_PATH;
-	  break;
-
-	case 'l':
-	  cmd.simple.relative = 1;
-	case 'L':
-	  cmd.simple.type = GOO_CANVAS_PATH_LINE_TO;
-	  cmd.simple.x = parse_double (&pos, &error);
-	  cmd.simple.y = parse_double (&pos, &error);
-	  break;
-
-	case 'h':
-	  cmd.simple.relative = 1;
-	case 'H':
-	  cmd.simple.type = GOO_CANVAS_PATH_HORIZONTAL_LINE_TO;
-	  cmd.simple.x = parse_double (&pos, &error);
-	  break;
-
-	case 'v':
-	  cmd.simple.relative = 1;
-	case 'V':
-	  cmd.simple.type = GOO_CANVAS_PATH_VERTICAL_LINE_TO;
-	  cmd.simple.y = parse_double (&pos, &error);
-	  break;
-
-	  /* Bezier curve commands: CcSsQqTt. */
-	case 'c':
-	  cmd.curve.relative = 1;
-	case 'C':
-	  cmd.curve.type = GOO_CANVAS_PATH_CURVE_TO;
-	  cmd.curve.x1 = parse_double (&pos, &error);
-	  cmd.curve.y1 = parse_double (&pos, &error);
-	  cmd.curve.x2 = parse_double (&pos, &error);
-	  cmd.curve.y2 = parse_double (&pos, &error);
-	  cmd.curve.x = parse_double (&pos, &error);
-	  cmd.curve.y = parse_double (&pos, &error);
-	  break;
-
-	case 's':
-	  cmd.curve.relative = 1;
-	case 'S':
-	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_CURVE_TO;
-	  cmd.curve.x2 = parse_double (&pos, &error);
-	  cmd.curve.y2 = parse_double (&pos, &error);
-	  cmd.curve.x = parse_double (&pos, &error);
-	  cmd.curve.y = parse_double (&pos, &error);
-	  break;
-
-	case 'q':
-	  cmd.curve.relative = 1;
-	case 'Q':
-	  cmd.curve.type = GOO_CANVAS_PATH_QUADRATIC_CURVE_TO;
-	  cmd.curve.x1 = parse_double (&pos, &error);
-	  cmd.curve.y1 = parse_double (&pos, &error);
-	  cmd.curve.x = parse_double (&pos, &error);
-	  cmd.curve.y = parse_double (&pos, &error);
-	  break;
-
-	case 't':
-	  cmd.curve.relative = 1;
-	case 'T':
-	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO;
-	  cmd.curve.x = parse_double (&pos, &error);
-	  cmd.curve.y = parse_double (&pos, &error);
-	  break;
-
-	  /* The elliptical arc commands: Aa. */
-	case 'a':
-	  cmd.arc.relative = 1;
-	case 'A':
-	  cmd.arc.type = GOO_CANVAS_PATH_ELLIPTICAL_ARC;
-	  cmd.arc.rx = parse_double (&pos, &error);
-	  cmd.arc.ry = parse_double (&pos, &error);
-	  cmd.arc.x_axis_rotation = parse_double (&pos, &error);
-	  cmd.arc.large_arc_flag = parse_flag (&pos, &error);
-	  cmd.arc.sweep_flag = parse_flag (&pos, &error);
-	  cmd.arc.x = parse_double (&pos, &error);
-	  cmd.arc.y = parse_double (&pos, &error);
-	  break;
-
-	default:
-	  error = TRUE;
-	  break;
-	}
-
-      /* If an error has occurred, return without adding the new command.
-	 Thus we include everything in the path up to the error, like SVG. */
-      if (error)
-	return;
-
-      g_array_append_val (path_commands, cmd);
-    }
+  path->path_commands = NULL;
 }
 
 
@@ -382,7 +163,7 @@
   item = g_object_new (GOO_TYPE_CANVAS_PATH, NULL);
   path = (GooCanvasPath*) item;
 
-  goo_canvas_path_parse_data (path->path_commands, path_data);
+  path->path_commands = goo_canvas_parse_path_data (path_data);
 
   va_start (var_args, path_data);
   first_property = va_arg (var_args, char*);
@@ -407,7 +188,10 @@
   GooCanvasPath *path = (GooCanvasPath*) object;
 
   if (!simple->model)
-    g_array_free (path->path_commands, TRUE);
+    {
+      if (path->path_commands)
+	g_array_free (path->path_commands, TRUE);
+    }
   path->path_commands = NULL;
 
   G_OBJECT_CLASS (goo_canvas_path_parent_class)->finalize (object);
@@ -445,7 +229,7 @@
 
 static void
 goo_canvas_path_set_common_property (GObject              *object,
-				     GArray               *path_commands,
+				     GArray              **path_commands,
 				     guint                 prop_id,
 				     const GValue         *value,
 				     GParamSpec           *pspec)
@@ -453,7 +237,9 @@
   switch (prop_id)
     {
     case PROP_DATA:
-      goo_canvas_path_parse_data (path_commands, g_value_get_string (value));
+      if (*path_commands)
+	g_array_free (*path_commands, TRUE);
+      *path_commands = goo_canvas_parse_path_data (g_value_get_string (value));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -477,433 +263,19 @@
       return;
     }
 
-  goo_canvas_path_set_common_property (object, path->path_commands, prop_id,
+  goo_canvas_path_set_common_property (object, &path->path_commands, prop_id,
 				       value, pspec);
   goo_canvas_item_simple_changed (simple, TRUE);
 }
 
 
 static void
-do_curve_to (GooCanvasPathCommand *cmd,
-	     cairo_t              *cr,
-	     gdouble              *x,
-	     gdouble              *y,
-	     gdouble              *last_control_point_x,
-	     gdouble              *last_control_point_y)
-{
-  if (cmd->curve.relative)
-    {
-      cairo_curve_to (cr, *x + cmd->curve.x1, *y + cmd->curve.y1,
-		      *x + cmd->curve.x2, *y + cmd->curve.y2,
-		      *x + cmd->curve.x, *y + cmd->curve.y);
-      *last_control_point_x = *x + cmd->curve.x2;
-      *last_control_point_y = *y + cmd->curve.y2;
-      *x += cmd->curve.x;
-      *y += cmd->curve.y;
-    }
-  else
-    {
-      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
-		      cmd->curve.x2, cmd->curve.y2,
-		      cmd->curve.x, cmd->curve.y);
-      *last_control_point_x = cmd->curve.x2;
-      *last_control_point_y = cmd->curve.y2;
-      *x = cmd->curve.x;
-      *y = cmd->curve.y;
-    }
-}
-
-
-static void
-do_smooth_curve_to (GooCanvasPathCommand    *cmd,
-		    GooCanvasPathCommandType prev_cmd_type,
-		    cairo_t                 *cr,
-		    gdouble                 *x,
-		    gdouble                 *y,
-		    gdouble                 *last_control_point_x,
-		    gdouble                 *last_control_point_y)
-{
-  gdouble x1, y1;
-
-  /* If the last command was a curveto or smooth curveto, we use the
-     reflection of the last control point about the current point as
-     the first control point of this curve. Otherwise we use the
-     current point as the first control point. */
-  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
-      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
-    {
-      x1 = *x + (*x - *last_control_point_x);
-      y1 = *y + (*y - *last_control_point_y);
-    }
-  else
-    {
-      x1 = *x;
-      y1 = *y;
-    }
-
-  if (cmd->curve.relative)
-    {
-      cairo_curve_to (cr, x1, y1, *x + cmd->curve.x2, *y + cmd->curve.y2,
-		      *x + cmd->curve.x, *y + cmd->curve.y);
-      *last_control_point_x = *x + cmd->curve.x2;
-      *last_control_point_y = *y + cmd->curve.y2;
-      *x += cmd->curve.x;
-      *y += cmd->curve.y;
-    }
-  else
-    {
-      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
-		      cmd->curve.x, cmd->curve.y);
-      *last_control_point_x = cmd->curve.x2;
-      *last_control_point_y = cmd->curve.y2;
-      *x = cmd->curve.x;
-      *y = cmd->curve.y;
-    }
-}
-
-
-static void
-do_quadratic_curve_to (GooCanvasPathCommand *cmd,
-		       cairo_t              *cr,
-		       gdouble              *x,
-		       gdouble              *y,
-		       gdouble              *last_control_point_x,
-		       gdouble              *last_control_point_y)
-{
-  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
-
-  if (cmd->curve.relative)
-    {
-      qx1 = *x + cmd->curve.x1;
-      qy1 = *y + cmd->curve.y1;
-      qx2 = *x + cmd->curve.x;
-      qy2 = *y + cmd->curve.y;
-    }
-  else
-    {
-      qx1 = cmd->curve.x1;
-      qy1 = cmd->curve.y1;
-      qx2 = cmd->curve.x;
-      qy2 = cmd->curve.y;
-    }
-
-  /* We need to convert the quadratic into a cubic bezier. */
-  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
-  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
-
-  x2 = x1 + (qx2 - *x) / 3.0;
-  y2 = y1 + (qy2 - *y) / 3.0;
-
-  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
-
-  *x = qx2;
-  *y = qy2;
-  *last_control_point_x = qx1;
-  *last_control_point_y = qy1;
-}
-
-
-static void
-do_smooth_quadratic_curve_to (GooCanvasPathCommand    *cmd,
-			      GooCanvasPathCommandType prev_cmd_type,
-			      cairo_t                 *cr,
-			      gdouble                 *x,
-			      gdouble                 *y,
-			      gdouble                 *last_control_point_x,
-			      gdouble                 *last_control_point_y)
-{
-  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
-
-  /* If the last command was a quadratic or smooth quadratic, we use
-     the reflection of the last control point about the current point
-     as the first control point of this curve. Otherwise we use the
-     current point as the first control point. */
-  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
-      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
-    {
-      qx1 = *x + (*x - *last_control_point_x);
-      qy1 = *y + (*y - *last_control_point_y);
-    }
-  else
-    {
-      qx1 = *x;
-      qy1 = *y;
-    }
-
-  if (cmd->curve.relative)
-    {
-      qx2 = *x + cmd->curve.x;
-      qy2 = *y + cmd->curve.y;
-    }
-  else
-    {
-      qx2 = cmd->curve.x;
-      qy2 = cmd->curve.y;
-    }
-
-  /* We need to convert the quadratic into a cubic bezier. */
-  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
-  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
-
-  x2 = x1 + (qx2 - *x) / 3.0;
-  y2 = y1 + (qy2 - *y) / 3.0;
-
-  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
-
-  *x = qx2;
-  *y = qy2;
-  *last_control_point_x = qx1;
-  *last_control_point_y = qy1;
-}
-
-
-static gdouble
-calc_angle (gdouble ux, gdouble uy, gdouble vx, gdouble vy)
-{
-  gdouble top, u_magnitude, v_magnitude, angle_cos, angle;
-
-  top = ux * vx + uy * vy;
-  u_magnitude = sqrt (ux * ux + uy * uy);
-  v_magnitude = sqrt (vx * vx + vy * vy);
-  angle_cos = top / (u_magnitude * v_magnitude);
-
-  /* We check if the cosine is slightly out-of-bounds. */
-  if (angle_cos >= 1.0)
-    angle = 0.0;
-  if (angle_cos <= -1.0)
-    angle = M_PI;
-  else
-    angle = acos (angle_cos);
-
-  if (ux * vy - uy * vx < 0)
-    angle = - angle;
-
-  return angle;
-}
-
-
-/* FIXME: Maybe we should do these calculations once when the path data is
-   parsed, and keep the cairo parameters we need in the command instead. */
-static void
-do_elliptical_arc (GooCanvasPathCommand    *cmd,
-		   cairo_t                 *cr,
-		   gdouble                 *x,
-		   gdouble                 *y)
-{
-  gdouble x1 = *x, y1 = *y, x2, y2, rx, ry, lambda;
-  gdouble v1, v2, angle, angle_sin, angle_cos, x11, y11;
-  gdouble rx_squared, ry_squared, x11_squared, y11_squared, top, bottom;
-  gdouble c, cx1, cy1, cx, cy, start_angle, angle_delta;
-
-  /* Calculate the end point of the arc - x2,y2. */
-  if (cmd->arc.relative)
-    {
-      x2 = x1 + cmd->arc.x;
-      y2 = y1 + cmd->arc.y;
-    }
-  else
-    {
-      x2 = cmd->arc.x;
-      y2 = cmd->arc.y;
-    }
-
-  *x = x2;
-  *y = y2;
-
-  /* If the endpoints are exactly the same, just return (see SVG spec). */
-  if (x1 == x2 && y1 == y2)
-    return;
-
-  /* If either rx or ry is 0, do a simple lineto (see SVG spec). */
-  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
-    {
-      cairo_line_to (cr, x2, y2);
-      return;
-    }
-
-  /* Calculate x1' and y1' (as per SVG implementation notes). */
-  v1 = (x1 - x2) / 2.0;
-  v2 = (y1 - y2) / 2.0;
-
-  angle = cmd->arc.x_axis_rotation * (M_PI / 180.0);
-  angle_sin = sin (angle);
-  angle_cos = cos (angle);
-
-  x11 = (angle_cos * v1) + (angle_sin * v2);
-  y11 = - (angle_sin * v1) + (angle_cos * v2);
-
-  /* Ensure rx and ry are positive and large enough. */
-  rx = cmd->arc.rx > 0.0 ? cmd->arc.rx : - cmd->arc.rx;
-  ry = cmd->arc.ry > 0.0 ? cmd->arc.ry : - cmd->arc.ry;
-  lambda = (x11 * x11) / (rx * rx) + (y11 * y11) / (ry * ry);
-  if (lambda > 1.0)
-    {
-      gdouble square_root = sqrt (lambda);
-      rx *= square_root;
-      ry *= square_root;
-    }
-
-  /* Calculate cx' and cy'. */
-  rx_squared = rx * rx;
-  ry_squared = ry * ry;
-  x11_squared = x11 * x11;
-  y11_squared = y11 * y11;
-
-  top = (rx_squared * ry_squared) - (rx_squared * y11_squared)
-    - (ry_squared * x11_squared);
-  if (top < 0.0)
-    {
-      c = 0.0;
-    }
-  else
-    {
-      bottom = (rx_squared * y11_squared) + (ry_squared * x11_squared);
-      c = sqrt (top / bottom);
-    }
-
-  if (cmd->arc.large_arc_flag == cmd->arc.sweep_flag)
-    c = - c;
-
-  cx1 = c * ((rx * y11) / ry);
-  cy1 = c * (- (ry * x11) / rx);
-
-  /* Calculate cx and cy. */
-  cx = (angle_cos * cx1) - (angle_sin * cy1) + (x1 + x2) / 2;
-  cy = (angle_sin * cx1) + (angle_cos * cy1) + (y1 + y2) / 2;
-
-  /* Calculate the start and end angles. */
-  v1 = (x11 - cx1) / rx;
-  v2 = (y11 - cy1) / ry;
-
-  start_angle = calc_angle (1, 0, v1, v2);
-  angle_delta = calc_angle (v1, v2, (-x11 - cx1) / rx, (-y11 - cy1) / ry);
-
-  if (cmd->arc.sweep_flag == 0 && angle_delta > 0.0)
-    angle_delta -= 2 * M_PI;
-  else if (cmd->arc.sweep_flag == 1 && angle_delta < 0.0)
-    angle_delta += 2 * M_PI;
-
-  /* Now draw the arc. */
-  cairo_save (cr);
-  cairo_translate (cr, cx, cy);
-  cairo_rotate (cr, angle);
-  cairo_scale (cr, rx, ry);
-
-  if (angle_delta > 0.0)
-    cairo_arc (cr, 0.0, 0.0, 1.0,
-	       start_angle, start_angle + angle_delta);
-  else
-    cairo_arc_negative (cr, 0.0, 0.0, 1.0,
-			start_angle, start_angle + angle_delta);
-
-  cairo_restore (cr);
-}
-
-
-static void
 goo_canvas_path_create_path (GooCanvasItemSimple *simple,
 			     cairo_t             *cr)
 {
   GooCanvasPath *path = (GooCanvasPath*) simple;
-  GooCanvasPathCommand *cmd;
-  GooCanvasPathCommandType prev_cmd_type = GOO_CANVAS_PATH_CLOSE_PATH;
-  gdouble x = 0, y = 0, path_start_x = 0, path_start_y = 0;
-  gdouble last_control_point_x = 0.0, last_control_point_y = 0.0;
-  gint i;
-
-  cairo_new_path (cr);
-
-  if (!path->path_commands || path->path_commands->len == 0)
-    return;
-
-  for (i = 0; i < path->path_commands->len; i++)
-    {
-      cmd = &g_array_index (path->path_commands, GooCanvasPathCommand, i);
-      switch (cmd->simple.type)
-	{
-	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
-	case GOO_CANVAS_PATH_MOVE_TO:
-	  if (cmd->simple.relative)
-	    {
-	      x += cmd->simple.x;
-	      y += cmd->simple.y;
-	    }
-	  else
-	    {
-	      x = cmd->simple.x;
-	      y = cmd->simple.y;
-	    }
-	  path_start_x = x;
-	  path_start_y = y;
-	  cairo_move_to (cr, x, y);
-	  break;
-
-	case GOO_CANVAS_PATH_CLOSE_PATH:
-	  x = path_start_x;
-	  y = path_start_y;
-	  cairo_close_path (cr);
-	  break;
-
-	case GOO_CANVAS_PATH_LINE_TO:
-	  if (cmd->simple.relative)
-	    {
-	      x += cmd->simple.x;
-	      y += cmd->simple.y;
-	    }
-	  else
-	    {
-	      x = cmd->simple.x;
-	      y = cmd->simple.y;
-	    }
-	  cairo_line_to (cr, x, y);
-	  break;
-
-	case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
-	  if (cmd->simple.relative)
-	    x += cmd->simple.x;
-	  else
-	    x = cmd->simple.x;
-	  cairo_line_to (cr, x, y);
-	  break;
 
-	case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
-	  if (cmd->simple.relative)
-	    y += cmd->simple.y;
-	  else
-	    y = cmd->simple.y;
-	  cairo_line_to (cr, x, y);
-	  break;
-
-	  /* Bezier curve commands: CcSsQqTt. */
-	case GOO_CANVAS_PATH_CURVE_TO:
-	  do_curve_to (cmd, cr, &x, &y,
-		       &last_control_point_x, &last_control_point_y);
-	  break;
-
-	case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
-	  do_smooth_curve_to (cmd, prev_cmd_type, cr, &x, &y,
-			      &last_control_point_x, &last_control_point_y);
-	  break;
-
-	case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
-	  do_quadratic_curve_to (cmd, cr, &x, &y,
-				 &last_control_point_x, &last_control_point_y);
-	  break;
-
-	case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
-	  do_smooth_quadratic_curve_to (cmd, prev_cmd_type, cr, &x, &y,
-					&last_control_point_x,
-					&last_control_point_y);
-	  break;
-
-	  /* The elliptical arc commands: Aa. */
-	case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
-	  do_elliptical_arc (cmd, cr, &x, &y);
-	  break;
-	}
-
-      prev_cmd_type = cmd->simple.type;
-    }
+  goo_canvas_create_path (path->path_commands, cr);
 }
 
 
@@ -911,21 +283,22 @@
 goo_canvas_path_set_model    (GooCanvasItem      *item,
 			      GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasPath *path = (GooCanvasPath*) item;
   GooCanvasPathModel *emodel = (GooCanvasPathModel*) model;
 
   /* If our data was allocated, free it. */
   if (!simple->model)
-    g_array_free (path->path_commands, TRUE);
+    {
+      if (path->path_commands)
+	g_array_free (path->path_commands, TRUE);
+    }
 
   /* Now use the new model's data instead. */
   path->path_commands = emodel->path_commands;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 
@@ -1063,7 +436,7 @@
   model = g_object_new (GOO_TYPE_CANVAS_PATH_MODEL, NULL);
   pmodel = (GooCanvasPathModel*) model;
 
-  goo_canvas_path_parse_data (pmodel->path_commands, path_data);
+  pmodel->path_commands = goo_canvas_parse_path_data (path_data);
 
   va_start (var_args, path_data);
   first_property = va_arg (var_args, char*);
@@ -1086,7 +459,8 @@
 {
   GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object;
 
-  g_array_free (pmodel->path_commands, TRUE);
+  if (pmodel->path_commands)
+    g_array_free (pmodel->path_commands, TRUE);
 
   G_OBJECT_CLASS (goo_canvas_path_model_parent_class)->finalize (object);
 }
@@ -1094,9 +468,9 @@
 
 static void
 goo_canvas_path_model_get_property (GObject              *object,
-				       guint                 prop_id,
-				       GValue               *value,
-				       GParamSpec           *pspec)
+				    guint                 prop_id,
+				    GValue               *value,
+				    GParamSpec           *pspec)
 {
   GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object;
 
@@ -1107,13 +481,13 @@
 
 static void
 goo_canvas_path_model_set_property (GObject              *object,
-				       guint                 prop_id,
-				       const GValue         *value,
-				       GParamSpec           *pspec)
+				    guint                 prop_id,
+				    const GValue         *value,
+				    GParamSpec           *pspec)
 {
   GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object;
 
-  goo_canvas_path_set_common_property (object, pmodel->path_commands, prop_id,
+  goo_canvas_path_set_common_property (object, &pmodel->path_commands, prop_id,
 				       value, pspec);
   g_signal_emit_by_name (pmodel, "changed", TRUE);
 }

Index: goocanvaspath.h
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspath.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- goocanvaspath.h	9 Feb 2007 13:41:03 -0000	1.7
+++ goocanvaspath.h	15 Feb 2007 14:18:50 -0000	1.8
@@ -13,95 +13,6 @@
 G_BEGIN_DECLS
 
 
-/**
- * GooCanvasPathCommandType
- * @GOO_CANVAS_PATH_MOVE_TO: move to the given point.
- * @GOO_CANVAS_PATH_CLOSE_PATH: close the current path, drawing a line from the
- *  current position to the start of the path.
- * @GOO_CANVAS_PATH_LINE_TO: draw a line to the given point.
- * @GOO_CANVAS_PATH_HORIZONTAL_LINE_TO: draw a horizontal line to the given
- *  x coordinate.
- * @GOO_CANVAS_PATH_VERTICAL_LINE_TO: draw a vertical line to the given y
- *  coordinate.
- * @GOO_CANVAS_PATH_CURVE_TO: draw a bezier curve using two control
- *  points to the given point.
- * @GOO_CANVAS_PATH_SMOOTH_CURVE_TO: draw a bezier curve using a reflection
- *  of the last control point of the last curve as the first control point,
- *  and one new control point, to the given point.
- * @GOO_CANVAS_PATH_QUADRATIC_CURVE_TO: draw a quadratic bezier curve using
- *  a single control point to the given point.
- * @GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO: draw a quadratic bezier curve
- *  using a reflection of the control point from the previous curve as the
- *  control point, to the given point.
- * @GOO_CANVAS_PATH_ELLIPTICAL_ARC: draw an elliptical arc, using the given
- *  2 radii, the x axis rotation, and the 2 flags to disambiguate the arc,
- *  to the given point.
- *
- * GooCanvasPathCommandType specifies the type of each command in the path.
- * See the path element in the <ulink url="http://www.w3.org/Graphics/SVG/">
- * Scalable Vector Graphics (SVG) specification</ulink> for more details.
- */
-typedef enum
-{
-  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
-  GOO_CANVAS_PATH_MOVE_TO,
-  GOO_CANVAS_PATH_CLOSE_PATH,
-  GOO_CANVAS_PATH_LINE_TO,
-  GOO_CANVAS_PATH_HORIZONTAL_LINE_TO,
-  GOO_CANVAS_PATH_VERTICAL_LINE_TO,
-
-  /* Bezier curve commands: CcSsQqTt. */
-  GOO_CANVAS_PATH_CURVE_TO,
-  GOO_CANVAS_PATH_SMOOTH_CURVE_TO,
-  GOO_CANVAS_PATH_QUADRATIC_CURVE_TO,
-  GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO,
-
-  /* The elliptical arc commands: Aa. */
-  GOO_CANVAS_PATH_ELLIPTICAL_ARC
-} GooCanvasPathCommandType;
-
-
-typedef union _GooCanvasPathCommand  GooCanvasPathCommand;
-
-/* Note that the command type is always the first element in each struct, so
-   we can always use it whatever type of command it is. */
-
-/**
- * GooCanvasPathCommand
- *
- * GooCanvasPathCommand holds the data for each command in the path.
- *
- * The @relative flag specifies that the coordinates for the command are
- * relative to the current point. Otherwise they are assumed to be absolute
- * coordinates.
- */
-union _GooCanvasPathCommand
-{
-  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
-  struct {
-    guint type : 5; /* GooCanvasPathCommandType */
-    guint relative : 1;
-    gdouble x, y;
-  } simple;
-
-  /* Bezier curve commands: CcSsQqTt. */
-  struct {
-    guint type : 5; /* GooCanvasPathCommandType */
-    guint relative : 1;
-    gdouble x, y, x1, y1, x2, y2;
-  } curve;
-
-  /* The elliptical arc commands: Aa. */
-  struct {
-    guint type : 5; /* GooCanvasPathCommandType */
-    guint relative : 1;
-    guint large_arc_flag : 1;
-    guint sweep_flag : 1;
-    gdouble rx, ry, x_axis_rotation, x, y;
-  } arc;
-};
-
-
 #define GOO_TYPE_CANVAS_PATH            (goo_canvas_path_get_type ())
 #define GOO_CANVAS_PATH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_PATH, GooCanvasPath))
 #define GOO_CANVAS_PATH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_PATH, GooCanvasPathClass))

Index: goocanvaspolyline.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspolyline.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- goocanvaspolyline.c	2 Feb 2007 21:54:00 -0000	1.10
+++ goocanvaspolyline.c	15 Feb 2007 14:18:50 -0000	1.11
@@ -935,8 +935,6 @@
 goo_canvas_polyline_set_model    (GooCanvasItem      *item,
 				 GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasPolyline *polyline = (GooCanvasPolyline*) item;
   GooCanvasPolylineModel *pmodel = (GooCanvasPolylineModel*) model;
@@ -949,7 +947,7 @@
   polyline->polyline_data = &pmodel->polyline_data;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 

Index: goocanvasrect.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasrect.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- goocanvasrect.c	2 Feb 2007 21:54:00 -0000	1.8
+++ goocanvasrect.c	15 Feb 2007 14:18:50 -0000	1.9
@@ -398,21 +398,19 @@
 goo_canvas_rect_set_model    (GooCanvasItem      *item,
 			      GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasRect *rect = (GooCanvasRect*) item;
-  GooCanvasRectModel *emodel = (GooCanvasRectModel*) model;
+  GooCanvasRectModel *rmodel = (GooCanvasRectModel*) model;
 
   /* If our rect_data was allocated, free it. */
   if (!simple->model)
     g_slice_free (GooCanvasRectData, rect->rect_data);
 
   /* Now use the new model's rect_data instead. */
-  rect->rect_data = &emodel->rect_data;
+  rect->rect_data = &rmodel->rect_data;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 

Index: goocanvastext.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvastext.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- goocanvastext.c	2 Feb 2007 21:54:00 -0000	1.10
+++ goocanvastext.c	15 Feb 2007 14:18:50 -0000	1.11
@@ -576,21 +576,19 @@
 goo_canvas_text_set_model    (GooCanvasItem      *item,
 			      GooCanvasItemModel *model)
 {
-  GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
-  GooCanvasItemIface *parent_iface = g_type_interface_peek_parent (iface);
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasText *text = (GooCanvasText*) item;
-  GooCanvasTextModel *emodel = (GooCanvasTextModel*) model;
+  GooCanvasTextModel *tmodel = (GooCanvasTextModel*) model;
 
   /* If our text_data was allocated, free it. */
   if (!simple->model)
     g_slice_free (GooCanvasTextData, text->text_data);
 
   /* Now use the new model's text_data instead. */
-  text->text_data = &emodel->text_data;
+  text->text_data = &tmodel->text_data;
 
   /* Let the parent GooCanvasItemSimple code do the rest. */
-  parent_iface->set_model (item, model);
+  goo_canvas_item_simple_set_model (simple, model);
 }
 
 
@@ -684,7 +682,7 @@
 
 
 static void
-goo_canvas_text_model_init (GooCanvasTextModel *emodel)
+goo_canvas_text_model_init (GooCanvasTextModel *tmodel)
 {
 
 }

Index: goocanvasutils.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasutils.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- goocanvasutils.c	7 Feb 2007 15:14:13 -0000	1.9
+++ goocanvasutils.c	15 Feb 2007 14:18:50 -0000	1.10
@@ -13,6 +13,7 @@
  * This section describes the types used throughout GooCanvas.
  */
 #include <config.h>
+#include <math.h>
 #include <gtk/gtk.h>
 #include "goocanvas.h"
 
@@ -465,3 +466,665 @@
   return NULL;
 }
 
+
+static gdouble
+parse_double (gchar    **pos,
+	      gboolean  *error)
+{
+  gdouble result;
+  gchar *p;
+
+  /* If an error has already occurred, just return. */
+  if (*error)
+    return 0;
+
+  /* Skip whitespace and commas. */
+  p = *pos;
+  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
+    p++;
+
+  /* Parse the double, and set pos to the first char after it. */
+  result = g_ascii_strtod (p, pos);
+
+  /* If no characters were parsed, set the error flag. */
+  if (p == *pos)
+    *error = TRUE;
+
+  return result;
+}
+
+
+static gint
+parse_flag (gchar    **pos,
+	    gboolean  *error)
+{
+  gint result = 0;
+  gchar *p;
+
+  /* If an error has already occurred, just return. */
+  if (*error)
+    return 0;
+
+  /* Skip whitespace and commas. */
+  p = *pos;
+  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
+    p++;
+
+  /* The flag must be a '0' or a '1'. */
+  if (*p == '0')
+    result = 0;
+  else if (*p == '1')
+    result = 1;
+  else
+    {
+      *error = TRUE;
+      return 0;
+    }
+
+  *pos = p + 1;
+
+  return result;
+}
+
+
+/**
+ * goo_canvas_parse_path_data:
+ * @path_data: the sequence of path commands, specified as a string using the
+ *  same syntax as in the <ulink url="http://www.w3.org/Graphics/SVG/">Scalable
+ *  Vector Graphics (SVG)</ulink> path element.
+ * 
+ * Parses the given SVG path specification string.
+ * 
+ * Returns: a #GArray of #GooCanvasPathCommand elements.
+ **/
+GArray*
+goo_canvas_parse_path_data (const gchar       *path_data)
+{
+  GArray *commands;
+  GooCanvasPathCommand cmd;
+  gchar *pos, command = 0, next_command;
+  gboolean error = FALSE;
+
+  commands = g_array_new (0, 0, sizeof (GooCanvasPathCommand));
+
+  if (!path_data)
+    return commands;
+
+  pos = (gchar*) path_data;
+  for (;;)
+    {
+      while (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n')
+	pos++;
+      if (!*pos)
+	break;
+
+      next_command = *pos;
+
+      /* If there is no command letter, we use the same command as the last
+	 one, except for the first command, and 'moveto' (which becomes
+	 'lineto'). */
+      if ((next_command < 'a' || next_command > 'z')
+	  && (next_command < 'A' || next_command > 'Z'))
+	{
+	  /* If this is the first command, then set the error flag and assume
+	     a simple close-path command. */
+	  if (!command)
+	    {
+	      error = TRUE;
+	      command = 'Z';
+	    }
+	  /* moveto commands change to lineto. */
+	  else if (command == 'm')
+	    command = 'l';
+	  else if (command == 'M')
+	    command = 'L';
+	}
+      else
+	{
+	  command = next_command;
+	  pos++;
+	}
+
+      cmd.simple.relative = 0;
+      switch (command)
+	{
+	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
+	case 'm':
+	  cmd.simple.relative = 1;
+	case 'M':
+	  cmd.simple.type = GOO_CANVAS_PATH_MOVE_TO;
+	  cmd.simple.x = parse_double (&pos, &error);
+	  cmd.simple.y = parse_double (&pos, &error);
+	  break;
+
+	case 'Z':
+	case 'z':
+	  cmd.simple.type = GOO_CANVAS_PATH_CLOSE_PATH;
+	  break;
+
+	case 'l':
+	  cmd.simple.relative = 1;
+	case 'L':
+	  cmd.simple.type = GOO_CANVAS_PATH_LINE_TO;
+	  cmd.simple.x = parse_double (&pos, &error);
+	  cmd.simple.y = parse_double (&pos, &error);
+	  break;
+
+	case 'h':
+	  cmd.simple.relative = 1;
+	case 'H':
+	  cmd.simple.type = GOO_CANVAS_PATH_HORIZONTAL_LINE_TO;
+	  cmd.simple.x = parse_double (&pos, &error);
+	  break;
+
+	case 'v':
+	  cmd.simple.relative = 1;
+	case 'V':
+	  cmd.simple.type = GOO_CANVAS_PATH_VERTICAL_LINE_TO;
+	  cmd.simple.y = parse_double (&pos, &error);
+	  break;
+
+	  /* Bezier curve commands: CcSsQqTt. */
+	case 'c':
+	  cmd.curve.relative = 1;
+	case 'C':
+	  cmd.curve.type = GOO_CANVAS_PATH_CURVE_TO;
+	  cmd.curve.x1 = parse_double (&pos, &error);
+	  cmd.curve.y1 = parse_double (&pos, &error);
+	  cmd.curve.x2 = parse_double (&pos, &error);
+	  cmd.curve.y2 = parse_double (&pos, &error);
+	  cmd.curve.x = parse_double (&pos, &error);
+	  cmd.curve.y = parse_double (&pos, &error);
+	  break;
+
+	case 's':
+	  cmd.curve.relative = 1;
+	case 'S':
+	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_CURVE_TO;
+	  cmd.curve.x2 = parse_double (&pos, &error);
+	  cmd.curve.y2 = parse_double (&pos, &error);
+	  cmd.curve.x = parse_double (&pos, &error);
+	  cmd.curve.y = parse_double (&pos, &error);
+	  break;
+
+	case 'q':
+	  cmd.curve.relative = 1;
+	case 'Q':
+	  cmd.curve.type = GOO_CANVAS_PATH_QUADRATIC_CURVE_TO;
+	  cmd.curve.x1 = parse_double (&pos, &error);
+	  cmd.curve.y1 = parse_double (&pos, &error);
+	  cmd.curve.x = parse_double (&pos, &error);
+	  cmd.curve.y = parse_double (&pos, &error);
+	  break;
+
+	case 't':
+	  cmd.curve.relative = 1;
+	case 'T':
+	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO;
+	  cmd.curve.x = parse_double (&pos, &error);
+	  cmd.curve.y = parse_double (&pos, &error);
+	  break;
+
+	  /* The elliptical arc commands: Aa. */
+	case 'a':
+	  cmd.arc.relative = 1;
+	case 'A':
+	  cmd.arc.type = GOO_CANVAS_PATH_ELLIPTICAL_ARC;
+	  cmd.arc.rx = parse_double (&pos, &error);
+	  cmd.arc.ry = parse_double (&pos, &error);
+	  cmd.arc.x_axis_rotation = parse_double (&pos, &error);
+	  cmd.arc.large_arc_flag = parse_flag (&pos, &error);
+	  cmd.arc.sweep_flag = parse_flag (&pos, &error);
+	  cmd.arc.x = parse_double (&pos, &error);
+	  cmd.arc.y = parse_double (&pos, &error);
+	  break;
+
+	default:
+	  error = TRUE;
+	  break;
+	}
+
+      /* If an error has occurred, return without adding the new command.
+	 Thus we include everything in the path up to the error, like SVG. */
+      if (error)
+	return commands;
+
+      g_array_append_val (commands, cmd);
+    }
+
+  return commands;
+}
+
+
+static void
+do_curve_to (GooCanvasPathCommand *cmd,
+	     cairo_t              *cr,
+	     gdouble              *x,
+	     gdouble              *y,
+	     gdouble              *last_control_point_x,
+	     gdouble              *last_control_point_y)
+{
+  if (cmd->curve.relative)
+    {
+      cairo_curve_to (cr, *x + cmd->curve.x1, *y + cmd->curve.y1,
+		      *x + cmd->curve.x2, *y + cmd->curve.y2,
+		      *x + cmd->curve.x, *y + cmd->curve.y);
+      *last_control_point_x = *x + cmd->curve.x2;
+      *last_control_point_y = *y + cmd->curve.y2;
+      *x += cmd->curve.x;
+      *y += cmd->curve.y;
+    }
+  else
+    {
+      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
+		      cmd->curve.x2, cmd->curve.y2,
+		      cmd->curve.x, cmd->curve.y);
+      *last_control_point_x = cmd->curve.x2;
+      *last_control_point_y = cmd->curve.y2;
+      *x = cmd->curve.x;
+      *y = cmd->curve.y;
+    }
+}
+
+
+static void
+do_smooth_curve_to (GooCanvasPathCommand    *cmd,
+		    GooCanvasPathCommandType prev_cmd_type,
+		    cairo_t                 *cr,
+		    gdouble                 *x,
+		    gdouble                 *y,
+		    gdouble                 *last_control_point_x,
+		    gdouble                 *last_control_point_y)
+{
+  gdouble x1, y1;
+
+  /* If the last command was a curveto or smooth curveto, we use the
+     reflection of the last control point about the current point as
+     the first control point of this curve. Otherwise we use the
+     current point as the first control point. */
+  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
+      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
+    {
+      x1 = *x + (*x - *last_control_point_x);
+      y1 = *y + (*y - *last_control_point_y);
+    }
+  else
+    {
+      x1 = *x;
+      y1 = *y;
+    }
+
+  if (cmd->curve.relative)
+    {
+      cairo_curve_to (cr, x1, y1, *x + cmd->curve.x2, *y + cmd->curve.y2,
+		      *x + cmd->curve.x, *y + cmd->curve.y);
+      *last_control_point_x = *x + cmd->curve.x2;
+      *last_control_point_y = *y + cmd->curve.y2;
+      *x += cmd->curve.x;
+      *y += cmd->curve.y;
+    }
+  else
+    {
+      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
+		      cmd->curve.x, cmd->curve.y);
+      *last_control_point_x = cmd->curve.x2;
+      *last_control_point_y = cmd->curve.y2;
+      *x = cmd->curve.x;
+      *y = cmd->curve.y;
+    }
+}
+
+
+static void
+do_quadratic_curve_to (GooCanvasPathCommand *cmd,
+		       cairo_t              *cr,
+		       gdouble              *x,
+		       gdouble              *y,
+		       gdouble              *last_control_point_x,
+		       gdouble              *last_control_point_y)
+{
+  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
+
+  if (cmd->curve.relative)
+    {
+      qx1 = *x + cmd->curve.x1;
+      qy1 = *y + cmd->curve.y1;
+      qx2 = *x + cmd->curve.x;
+      qy2 = *y + cmd->curve.y;
+    }
+  else
+    {
+      qx1 = cmd->curve.x1;
+      qy1 = cmd->curve.y1;
+      qx2 = cmd->curve.x;
+      qy2 = cmd->curve.y;
+    }
+
+  /* We need to convert the quadratic into a cubic bezier. */
+  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
+  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
+
+  x2 = x1 + (qx2 - *x) / 3.0;
+  y2 = y1 + (qy2 - *y) / 3.0;
+
+  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
+
+  *x = qx2;
+  *y = qy2;
+  *last_control_point_x = qx1;
+  *last_control_point_y = qy1;
+}
+
+
+static void
+do_smooth_quadratic_curve_to (GooCanvasPathCommand    *cmd,
+			      GooCanvasPathCommandType prev_cmd_type,
+			      cairo_t                 *cr,
+			      gdouble                 *x,
+			      gdouble                 *y,
+			      gdouble                 *last_control_point_x,
+			      gdouble                 *last_control_point_y)
+{
+  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
+
+  /* If the last command was a quadratic or smooth quadratic, we use
+     the reflection of the last control point about the current point
+     as the first control point of this curve. Otherwise we use the
+     current point as the first control point. */
+  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
+      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
+    {
+      qx1 = *x + (*x - *last_control_point_x);
+      qy1 = *y + (*y - *last_control_point_y);
+    }
+  else
+    {
+      qx1 = *x;
+      qy1 = *y;
+    }
+
+  if (cmd->curve.relative)
+    {
+      qx2 = *x + cmd->curve.x;
+      qy2 = *y + cmd->curve.y;
+    }
+  else
+    {
+      qx2 = cmd->curve.x;
+      qy2 = cmd->curve.y;
+    }
+
+  /* We need to convert the quadratic into a cubic bezier. */
+  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
+  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
+
+  x2 = x1 + (qx2 - *x) / 3.0;
+  y2 = y1 + (qy2 - *y) / 3.0;
+
+  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
+
+  *x = qx2;
+  *y = qy2;
+  *last_control_point_x = qx1;
+  *last_control_point_y = qy1;
+}
+
+
+static gdouble
+calc_angle (gdouble ux, gdouble uy, gdouble vx, gdouble vy)
+{
+  gdouble top, u_magnitude, v_magnitude, angle_cos, angle;
+
+  top = ux * vx + uy * vy;
+  u_magnitude = sqrt (ux * ux + uy * uy);
+  v_magnitude = sqrt (vx * vx + vy * vy);
+  angle_cos = top / (u_magnitude * v_magnitude);
+
+  /* We check if the cosine is slightly out-of-bounds. */
+  if (angle_cos >= 1.0)
+    angle = 0.0;
+  if (angle_cos <= -1.0)
+    angle = M_PI;
+  else
+    angle = acos (angle_cos);
+
+  if (ux * vy - uy * vx < 0)
+    angle = - angle;
+
+  return angle;
+}
+
+
+/* FIXME: Maybe we should do these calculations once when the path data is
+   parsed, and keep the cairo parameters we need in the command instead. */
+static void
+do_elliptical_arc (GooCanvasPathCommand    *cmd,
+		   cairo_t                 *cr,
+		   gdouble                 *x,
+		   gdouble                 *y)
+{
+  gdouble x1 = *x, y1 = *y, x2, y2, rx, ry, lambda;
+  gdouble v1, v2, angle, angle_sin, angle_cos, x11, y11;
+  gdouble rx_squared, ry_squared, x11_squared, y11_squared, top, bottom;
+  gdouble c, cx1, cy1, cx, cy, start_angle, angle_delta;
+
+  /* Calculate the end point of the arc - x2,y2. */
+  if (cmd->arc.relative)
+    {
+      x2 = x1 + cmd->arc.x;
+      y2 = y1 + cmd->arc.y;
+    }
+  else
+    {
+      x2 = cmd->arc.x;
+      y2 = cmd->arc.y;
+    }
+
+  *x = x2;
+  *y = y2;
+
+  /* If the endpoints are exactly the same, just return (see SVG spec). */
+  if (x1 == x2 && y1 == y2)
+    return;
+
+  /* If either rx or ry is 0, do a simple lineto (see SVG spec). */
+  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
+    {
+      cairo_line_to (cr, x2, y2);
+      return;
+    }
+
+  /* Calculate x1' and y1' (as per SVG implementation notes). */
+  v1 = (x1 - x2) / 2.0;
+  v2 = (y1 - y2) / 2.0;
+
+  angle = cmd->arc.x_axis_rotation * (M_PI / 180.0);
+  angle_sin = sin (angle);
+  angle_cos = cos (angle);
+
+  x11 = (angle_cos * v1) + (angle_sin * v2);
+  y11 = - (angle_sin * v1) + (angle_cos * v2);
+
+  /* Ensure rx and ry are positive and large enough. */
+  rx = cmd->arc.rx > 0.0 ? cmd->arc.rx : - cmd->arc.rx;
+  ry = cmd->arc.ry > 0.0 ? cmd->arc.ry : - cmd->arc.ry;
+  lambda = (x11 * x11) / (rx * rx) + (y11 * y11) / (ry * ry);
+  if (lambda > 1.0)
+    {
+      gdouble square_root = sqrt (lambda);
+      rx *= square_root;
+      ry *= square_root;
+    }
+
+  /* Calculate cx' and cy'. */
+  rx_squared = rx * rx;
+  ry_squared = ry * ry;
+  x11_squared = x11 * x11;
+  y11_squared = y11 * y11;
+
+  top = (rx_squared * ry_squared) - (rx_squared * y11_squared)
+    - (ry_squared * x11_squared);
+  if (top < 0.0)
+    {
+      c = 0.0;
+    }
+  else
+    {
+      bottom = (rx_squared * y11_squared) + (ry_squared * x11_squared);
+      c = sqrt (top / bottom);
+    }
+
+  if (cmd->arc.large_arc_flag == cmd->arc.sweep_flag)
+    c = - c;
+
+  cx1 = c * ((rx * y11) / ry);
+  cy1 = c * (- (ry * x11) / rx);
+
+  /* Calculate cx and cy. */
+  cx = (angle_cos * cx1) - (angle_sin * cy1) + (x1 + x2) / 2;
+  cy = (angle_sin * cx1) + (angle_cos * cy1) + (y1 + y2) / 2;
+
+  /* Calculate the start and end angles. */
+  v1 = (x11 - cx1) / rx;
+  v2 = (y11 - cy1) / ry;
+
+  start_angle = calc_angle (1, 0, v1, v2);
+  angle_delta = calc_angle (v1, v2, (-x11 - cx1) / rx, (-y11 - cy1) / ry);
+
+  if (cmd->arc.sweep_flag == 0 && angle_delta > 0.0)
+    angle_delta -= 2 * M_PI;
+  else if (cmd->arc.sweep_flag == 1 && angle_delta < 0.0)
+    angle_delta += 2 * M_PI;
+
+  /* Now draw the arc. */
+  cairo_save (cr);
+  cairo_translate (cr, cx, cy);
+  cairo_rotate (cr, angle);
+  cairo_scale (cr, rx, ry);
+
+  if (angle_delta > 0.0)
+    cairo_arc (cr, 0.0, 0.0, 1.0,
+	       start_angle, start_angle + angle_delta);
+  else
+    cairo_arc_negative (cr, 0.0, 0.0, 1.0,
+			start_angle, start_angle + angle_delta);
+
+  cairo_restore (cr);
+}
+
+
+/**
+ * goo_canvas_create_path:
+ * @commands: an array of #GooCanvasPathCommand.
+ * @cr: a cairo context.
+ * 
+ * Creates the path specified by the given #GooCanvasPathCommand array.
+ **/
+void
+goo_canvas_create_path (GArray              *commands,
+			cairo_t             *cr)
+{
+  GooCanvasPathCommand *cmd;
+  GooCanvasPathCommandType prev_cmd_type = GOO_CANVAS_PATH_CLOSE_PATH;
+  gdouble x = 0, y = 0, path_start_x = 0, path_start_y = 0;
+  gdouble last_control_point_x = 0.0, last_control_point_y = 0.0;
+  gint i;
+
+  cairo_new_path (cr);
+
+  if (!commands || commands->len == 0)
+    return;
+
+  for (i = 0; i < commands->len; i++)
+    {
+      cmd = &g_array_index (commands, GooCanvasPathCommand, i);
+      switch (cmd->simple.type)
+	{
+	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
+	case GOO_CANVAS_PATH_MOVE_TO:
+	  if (cmd->simple.relative)
+	    {
+	      x += cmd->simple.x;
+	      y += cmd->simple.y;
+	    }
+	  else
+	    {
+	      x = cmd->simple.x;
+	      y = cmd->simple.y;
+	    }
+	  path_start_x = x;
+	  path_start_y = y;
+	  cairo_move_to (cr, x, y);
+	  break;
+
+	case GOO_CANVAS_PATH_CLOSE_PATH:
+	  x = path_start_x;
+	  y = path_start_y;
+	  cairo_close_path (cr);
+	  break;
+
+	case GOO_CANVAS_PATH_LINE_TO:
+	  if (cmd->simple.relative)
+	    {
+	      x += cmd->simple.x;
+	      y += cmd->simple.y;
+	    }
+	  else
+	    {
+	      x = cmd->simple.x;
+	      y = cmd->simple.y;
+	    }
+	  cairo_line_to (cr, x, y);
+	  break;
+
+	case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
+	  if (cmd->simple.relative)
+	    x += cmd->simple.x;
+	  else
+	    x = cmd->simple.x;
+	  cairo_line_to (cr, x, y);
+	  break;
+
+	case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
+	  if (cmd->simple.relative)
+	    y += cmd->simple.y;
+	  else
+	    y = cmd->simple.y;
+	  cairo_line_to (cr, x, y);
+	  break;
+
+	  /* Bezier curve commands: CcSsQqTt. */
+	case GOO_CANVAS_PATH_CURVE_TO:
+	  do_curve_to (cmd, cr, &x, &y,
+		       &last_control_point_x, &last_control_point_y);
+	  break;
+
+	case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
+	  do_smooth_curve_to (cmd, prev_cmd_type, cr, &x, &y,
+			      &last_control_point_x, &last_control_point_y);
+	  break;
+
+	case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
+	  do_quadratic_curve_to (cmd, cr, &x, &y,
+				 &last_control_point_x, &last_control_point_y);
+	  break;
+
+	case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
+	  do_smooth_quadratic_curve_to (cmd, prev_cmd_type, cr, &x, &y,
+					&last_control_point_x,
+					&last_control_point_y);
+	  break;
+
+	  /* The elliptical arc commands: Aa. */
+	case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+	  do_elliptical_arc (cmd, cr, &x, &y);
+	  break;
+	}
+
+      prev_cmd_type = cmd->simple.type;
+    }
+}
+
+
+
+

Index: goocanvasutils.h
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvasutils.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- goocanvasutils.h	8 Feb 2007 17:54:26 -0000	1.10
+++ goocanvasutils.h	15 Feb 2007 14:18:50 -0000	1.11
@@ -90,6 +90,100 @@
 } GooCanvasItemVisibility;
 
 
+/**
+ * GooCanvasPathCommandType
+ * @GOO_CANVAS_PATH_MOVE_TO: move to the given point.
+ * @GOO_CANVAS_PATH_CLOSE_PATH: close the current path, drawing a line from the
+ *  current position to the start of the path.
+ * @GOO_CANVAS_PATH_LINE_TO: draw a line to the given point.
+ * @GOO_CANVAS_PATH_HORIZONTAL_LINE_TO: draw a horizontal line to the given
+ *  x coordinate.
+ * @GOO_CANVAS_PATH_VERTICAL_LINE_TO: draw a vertical line to the given y
+ *  coordinate.
+ * @GOO_CANVAS_PATH_CURVE_TO: draw a bezier curve using two control
+ *  points to the given point.
+ * @GOO_CANVAS_PATH_SMOOTH_CURVE_TO: draw a bezier curve using a reflection
+ *  of the last control point of the last curve as the first control point,
+ *  and one new control point, to the given point.
+ * @GOO_CANVAS_PATH_QUADRATIC_CURVE_TO: draw a quadratic bezier curve using
+ *  a single control point to the given point.
+ * @GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO: draw a quadratic bezier curve
+ *  using a reflection of the control point from the previous curve as the
+ *  control point, to the given point.
+ * @GOO_CANVAS_PATH_ELLIPTICAL_ARC: draw an elliptical arc, using the given
+ *  2 radii, the x axis rotation, and the 2 flags to disambiguate the arc,
+ *  to the given point.
+ *
+ * GooCanvasPathCommandType specifies the type of each command in the path.
+ * See the path element in the <ulink url="http://www.w3.org/Graphics/SVG/">
+ * Scalable Vector Graphics (SVG) specification</ulink> for more details.
+ */
+typedef enum
+{
+  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
+  GOO_CANVAS_PATH_MOVE_TO,
+  GOO_CANVAS_PATH_CLOSE_PATH,
+  GOO_CANVAS_PATH_LINE_TO,
+  GOO_CANVAS_PATH_HORIZONTAL_LINE_TO,
+  GOO_CANVAS_PATH_VERTICAL_LINE_TO,
+
+  /* Bezier curve commands: CcSsQqTt. */
+  GOO_CANVAS_PATH_CURVE_TO,
+  GOO_CANVAS_PATH_SMOOTH_CURVE_TO,
+  GOO_CANVAS_PATH_QUADRATIC_CURVE_TO,
+  GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO,
+
+  /* The elliptical arc commands: Aa. */
+  GOO_CANVAS_PATH_ELLIPTICAL_ARC
+} GooCanvasPathCommandType;
+
+
+typedef union _GooCanvasPathCommand  GooCanvasPathCommand;
+
+/* Note that the command type is always the first element in each struct, so
+   we can always use it whatever type of command it is. */
+
+/**
+ * GooCanvasPathCommand
+ *
+ * GooCanvasPathCommand holds the data for each command in the path.
+ *
+ * The @relative flag specifies that the coordinates for the command are
+ * relative to the current point. Otherwise they are assumed to be absolute
+ * coordinates.
+ */
+union _GooCanvasPathCommand
+{
+  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
+  struct {
+    guint type : 5; /* GooCanvasPathCommandType */
+    guint relative : 1;
+    gdouble x, y;
+  } simple;
+
+  /* Bezier curve commands: CcSsQqTt. */
+  struct {
+    guint type : 5; /* GooCanvasPathCommandType */
+    guint relative : 1;
+    gdouble x, y, x1, y1, x2, y2;
+  } curve;
+
+  /* The elliptical arc commands: Aa. */
+  struct {
+    guint type : 5; /* GooCanvasPathCommandType */
+    guint relative : 1;
+    guint large_arc_flag : 1;
+    guint sweep_flag : 1;
+    gdouble rx, ry, x_axis_rotation, x, y;
+  } arc;
+};
+
+
+GArray*	goo_canvas_parse_path_data	(const gchar       *path_data);
+void	goo_canvas_create_path		(GArray		   *commands,
+					 cairo_t           *cr);
+
+
 /*
  * Cairo utilities.
  */



More information about the cairo-commit mailing list