[gegl] GeglPath: heavy reorganisation and some coding style



commit 000a3f39fd6b7dba07875466ed62600aa20ae1f8
Author: Michael Murà <batolettre gmail com>
Date:   Tue Jun 28 17:29:10 2011 +0200

    GeglPath: heavy reorganisation and some coding style

 gegl/property-types/gegl-path.c | 2669 ++++++++++++++++-----------------------
 1 files changed, 1121 insertions(+), 1548 deletions(-)
---
diff --git a/gegl/property-types/gegl-path.c b/gegl/property-types/gegl-path.c
index 4be0465..2b7ab2b 100644
--- a/gegl/property-types/gegl-path.c
+++ b/gegl/property-types/gegl-path.c
@@ -13,6 +13,7 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
  *
+ * Copyright 2011 Michael Murà <batolettre gmail com>
  * Copyright 2008 Ãyvind KolÃs <pippin gimp org>
  * Copyright 2006 Mark Probst <mark probst gmail com>
  */
@@ -102,6 +103,88 @@ static void get_property (GObject      *gobject,
                           GValue       *value,
                           GParamSpec   *pspec);
 
+static const gchar *    parse_float_pair              (const gchar      *p,
+                                                       gdouble          *x,
+                                                       gdouble          *y);
+static gboolean         path_calc2                    (GeglPathList     *path,
+                                                       gdouble           pos,
+                                                       gdouble          *xd,
+                                                       gdouble          *yd,
+                                                       GeglPathList    **stop,
+                                                       gdouble          *leftover);
+static void             path_calc_values              (GeglPathList     *path,
+                                                       guint             num_samples,
+                                                       gdouble          *xs,
+                                                       gdouble          *ys);
+static gdouble          path_get_length               (GeglPathList     *path);
+static void             gegl_path_emit_changed        (GeglPath         *self,
+                                                       const GeglRectangle *bounds);
+static void             ensure_flattened              (GeglPath         *vector);
+static GeglPathList *   ensure_tail                   (GeglPathPrivate  *priv);
+
+static GeglPathList *   flatten_copy                  (GeglMatrix3      *matrix,
+                                                       GeglPathList     *head,
+                                                       GeglPathList     *prev,
+                                                       GeglPathList     *self);
+static GeglPathList *   flatten_rel_copy              (GeglMatrix3      *matrix,
+                                                       GeglPathList     *head,
+                                                       GeglPathList     *prev,
+                                                       GeglPathList     *self);
+static GeglPathList *   flatten_nop                   (GeglMatrix3      *matrix,
+                                                       GeglPathList     *head,
+                                                       GeglPathList     *prev,
+                                                       GeglPathList     *self);
+static GeglPathList *   flatten_curve                 (GeglMatrix3      *matrix,
+                                                       GeglPathList     *head,
+                                                       GeglPathList     *prev,
+                                                       GeglPathList     *self);
+
+static InstructionInfo *lookup_instruction_info       (gchar             type);
+static void             transform_data                (GeglMatrix3      *matrix,
+                                                       GeglPathItem     *dst);
+static void             copy_data                     (const GeglPathItem *src,
+                                                       GeglPathItem     *dst);
+static void             bezier2                       (GeglPathItem     *prev,
+                                                       GeglPathItem     *curve,
+                                                       Point            *dest,
+                                                       gfloat            t);
+static void             gegl_path_item_free           (GeglPathList     *p);
+static GeglPathList *   gegl_path_list_append_item    (GeglPathList     *head,
+                                                       gchar             type,
+                                                       GeglPathList    **res,
+                                                       GeglPathList     *tail);
+static GeglPathList *   gegl_path_list_flatten        (GeglMatrix3      *matrix,
+                                                       GeglPathList     *original);
+static void             lerp                          (Point            *dest,
+                                                       Point            *a,
+                                                       Point            *b,
+                                                       gfloat            t);
+static gdouble          point_dist                    (Point            *a,
+                                                       Point            *b);
+
+
+/* FIXME: handling of relative commands should be moved to the flattening stage */
+
+/* This table can be extended at runtime and extends the type of knots understood by the
+ * "SVG path" parser/serializer.
+ */
+static InstructionInfo knot_types[64]= /* reserve space for a total of up to 64 types of instructions. */
+{
+  {'M',  2, "move to",              flatten_copy},
+  {'L',  2, "line to",              flatten_copy},
+  {'C',  6, "curve to",             flatten_curve},
+
+  {'m',  2, "rel move to",          flatten_rel_copy},
+  {'l',  2, "rel line to",          flatten_rel_copy},
+  {'c',  6, "rel curve to",         flatten_rel_copy},
+
+  {'s',  0, "sentinel",             flatten_nop},
+  {'z',  0, "sentinel",             flatten_nop},
+  {'\0', 0, "end of instructions",  flatten_copy},
+};
+
+
+
 static void
 gegl_path_init (GeglPath *self)
 {
@@ -178,1189 +261,421 @@ get_property (GObject    *gobject,
         break;
     }
 }
+/***** GeglPath *****/
+GeglPath *
+gegl_path_new (void)
+{
+  GeglPath        *self = GEGL_PATH (g_object_new (GEGL_TYPE_PATH, NULL));
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
 
-static GeglPathList *flatten_copy      (GeglMatrix3 *matrix, GeglPathList *head, GeglPathList *prev, GeglPathList *self);
-static GeglPathList *flatten_rel_copy  (GeglMatrix3 *matrix, GeglPathList *head, GeglPathList *prev, GeglPathList *self);
-static GeglPathList *flatten_nop       (GeglMatrix3 *matrix, GeglPathList *head, GeglPathList *prev, GeglPathList *self);
-static GeglPathList *flatten_curve     (GeglMatrix3 *matrix, GeglPathList *head, GeglPathList *prev, GeglPathList *self);
-
-/* FIXME: handling of relative commands should be moved to the flattening stage */
+  gegl_path_init (self);
+  priv->flat_path_clean = FALSE;
+  priv->length_clean = FALSE;
+  priv->calc_clean = FALSE;
 
-/* This table can be extended at runtime and extends the type of knots understood by the
- * "SVG path" parser/serializer.
- */
+  return self;
+}
 
-static InstructionInfo knot_types[64]= /* reserve space for a total of up to 64 types of instructions. */
+GeglPath *
+gegl_path_new_from_string (const gchar *path_string)
 {
-  {'M',  2, "move to",              flatten_copy},
-  {'L',  2, "line to",              flatten_copy},
-  {'C',  6, "curve to",             flatten_curve},
-
-  {'m',  2, "rel move to",          flatten_rel_copy},
-  {'l',  2, "rel line to",          flatten_rel_copy},
-  {'c',  6, "rel curve to",         flatten_rel_copy},
-
-  {'s',  0, "sentinel",             flatten_nop},
-  {'z',  0, "sentinel",             flatten_nop},
-  {'\0', 0, "end of instructions",  flatten_copy},
-};
+  GeglPath *self = gegl_path_new ();
+  gegl_path_parse_string (self, path_string);
+  return self;
+}
 
-static InstructionInfo *lookup_instruction_info (gchar type)
+gboolean gegl_path_is_empty (GeglPath *path)
 {
-  gint i;
-  for (i=0; knot_types[i].type != '\0'; i++)
-    if (knot_types[i].type == type)
-      return &knot_types[i];
-  return NULL;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
+  return priv->path != NULL;
 }
 
-static void transform_data (
- GeglMatrix3         *matrix,
- GeglPathItem       *dst
-                            )
+gint
+gegl_path_get_n_nodes  (GeglPath *vector)
 {
-  InstructionInfo *dst_info = lookup_instruction_info(dst->type);
-  gint i;
+  GeglPathPrivate *priv;
+  GeglPathList *iter;
+  gint count=0;
+  if (!vector)
+    return 0;
+  priv = GEGL_PATH_GET_PRIVATE (vector);
 
-  for (i=0;i<(dst_info->n_items+1)/2;i++)
+  for (iter = priv->path; iter; iter=iter->next)
     {
-      gdouble x = dst->point[i].x;
-      gdouble y = dst->point[i].y;
-      gegl_matrix3_transform_point (matrix, &x, &y);
-      dst->point[i].x=x;
-      dst->point[i].y=y;
+      count ++;
     }
-}
+  return count;
 
-/* XXX: copy_data should exit for internal to external conversions */
+}
 
-static void copy_data (const GeglPathItem *src,
-                       GeglPathItem       *dst)
+gdouble
+gegl_path_get_length (GeglPath *self)
 {
-  InstructionInfo *src_info;
-  gint i;
-
-  if (!src)
-    return;
-
-  src_info = lookup_instruction_info(src->type);
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
+  if (!self)
+    return 0.0;
 
-  dst->type = src->type;
-  for (i=0;i<(src_info->n_items+1)/2;i++)
+  if (!priv->length_clean)
     {
-      dst->point[i].x = src->point[i].x;
-      dst->point[i].y = src->point[i].y;
+      ensure_flattened (self);
+      priv->length = path_get_length (priv->flat_path);
+      priv->length_clean = TRUE;
     }
+  return priv->length;
 }
 
-
-
-void
-gegl_path_add_type (gchar        type,
-                    gint         n_items,
-                    const gchar *name)
+gboolean
+gegl_path_get_node (GeglPath     *vector,
+                    gint          index,
+                    GeglPathItem *node)
 {
-  gint i;
-  for (i=0; knot_types[i].type != '\0'; i++)
-    if (knot_types[i].type == type)
-      {
-        g_warning ("control point type %c already exists\n", type);
-        return;
-      }
-  knot_types[i].type = type;
-  knot_types[i].n_items = n_items;
-  knot_types[i].name = g_strdup (name);
-  knot_types[i].flatten = flatten_copy;
-  knot_types[i+1].type = '\0';
-  return;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
+  GeglPathList *iter;
+  GeglPathItem *last = NULL;
+  gint count=0;
+  for (iter = priv->path; iter; iter=iter->next)
+    {
+      last=&iter->d;
+      if (count == index)
+        {
+          copy_data (last, node);
+          return TRUE;
+        }
+      count ++;
+    }
+  if (index==-1)
+    {
+      copy_data (last, node);
+      return TRUE;
+    }
+  return FALSE;
 }
 
-static GeglPathList *
-gegl_path_list_append_item  (GeglPathList  *head,
-                             gchar          type,
-                             GeglPathList **res,
-                             GeglPathList  *tail)
+/* this code is generic and should also work for extensions providing
+ * new knot types to the infrastructure
+ */
+gchar *
+gegl_path_to_string (GeglPath  *vector)
 {
-  GeglPathList *iter = tail?tail:head;
-  InstructionInfo *info = lookup_instruction_info (type);
-  g_assert (info);
+  GeglPathPrivate *priv;
+  GString *str;
+  gchar *ret;
+  GeglPathList *iter;
+  if (!vector)
+    return g_strdup ("");
+  str = g_string_new ("");
+  priv = GEGL_PATH_GET_PRIVATE (vector);
+  for (iter = priv->path; iter; iter=iter->next)
+    {
+      gint i;
+      InstructionInfo *info = lookup_instruction_info(iter->d.type);
 
-  while (iter && iter->next)
-    iter=iter->next;
+      g_string_append_c (str, iter->d.type);
+      for (i=0;i<(info->n_items+1)/2;i++)
+        {
+          gchar buf[16];
+          gchar *eptr;
+          g_sprintf (buf, "%f", iter->d.point[i].x);
 
-  if (iter)
-    {
-      /* the +3 is padding, +1 was excpected to be sufficient */
-      iter->next =
-        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
-      iter->next->d.type = type;
-      iter = iter->next;
-    }
-  else /* creating new path */
-    {
-      /* the +3 is padding, +1 was excpected to be sufficient */
-      head =
-        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
-      head->d.type = type;
-      iter=head;
+          for (eptr = &buf[strlen(buf)-1];eptr != buf && (*eptr=='0');eptr--)
+              *eptr='\0';
+          if (*eptr=='.')
+            *eptr='\0';
+
+          /* FIXME: make this work better also for other odd count
+           * of n_items
+           */
+          if (info->n_items>1)
+            {
+              g_string_append_printf (str, "%s,", buf);
+              sprintf (buf, "%f", iter->d.point[i].y);
+
+              for (eptr = &buf[strlen(buf)-1];eptr != buf && (*eptr=='0');eptr--)
+                  *eptr='\0';
+              if (*eptr=='.')
+                *eptr='\0';
+            }
+
+          g_string_append_printf (str, "%s ", buf);
+        }
     }
-  g_assert (res);
-    *res = iter;
 
-  return head;
+  ret = str->str;
+  g_string_free (str, FALSE);
+  return ret;
 }
 
-static GeglPathList *flatten_nop (GeglMatrix3   *matrix,
-                                  GeglPathList *head,
-                                  GeglPathList *prev,
-                                  GeglPathList *self)
+void
+gegl_path_set_matrix (GeglPath    *path,
+                      GeglMatrix3  *matrix)
 {
-  return head;
+  GeglPathPrivate *priv;
+  if (!path)
+    {
+      g_warning ("EEek! no path\n");
+      return;
+    }
+  priv = GEGL_PATH_GET_PRIVATE (path);
+  gegl_matrix3_copy_into (&priv->matrix, matrix);
+  priv->flat_path_clean = FALSE;
+  priv->length_clean = FALSE;
 }
 
-static GeglPathList *flatten_copy (GeglMatrix3   *matrix,
-                                   GeglPathList *head,
-                                   GeglPathList *prev,
-                                   GeglPathList *self)
+void gegl_path_get_matrix (GeglPath    *path,
+                           GeglMatrix3  *matrix)
 {
-  GeglPathList *newp;
-  head = gegl_path_list_append_item (head, self->d.type, &newp, NULL);
-  copy_data (&self->d, &newp->d);
-  transform_data (matrix, &newp->d);
-  return head;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
+  gegl_matrix3_copy_into (matrix, &priv->matrix);
 }
 
-static GeglPathList *
-flatten_rel_copy (GeglMatrix3   *matrix,
-                  GeglPathList *head,
-                  GeglPathList *prev,
-                  GeglPathList *self)
+gdouble
+gegl_path_closest_point (GeglPath *path,
+                         gdouble   x,
+                         gdouble   y,
+                         gdouble  *dx,
+                         gdouble  *dy,
+                         gint     *node_pos_before)
 {
-  GeglPathList *newp;
-  InstructionInfo *info;
-  gint i;
+  gdouble length = gegl_path_get_length (path);
+  gint     i, n;
+  gdouble closest_dist = 100000;
+  gint   closest_val = 0;
+  gdouble  *samples_x;
+  gdouble  *samples_y;
+  n = ceil(length);
+  samples_x = g_malloc (sizeof (gdouble)* n);
+  samples_y = g_malloc (sizeof (gdouble)* n);
 
-  head = gegl_path_list_append_item (head, self->d.type, &newp, NULL);
-  copy_data (&self->d, &newp->d);
-  info = lookup_instruction_info (self->d.type);
-  for (i=0;i<(info->n_items+1)/2;i++)
-    {
-      newp->d.point[i].x += prev->d.point[0].x;
-      newp->d.point[i].y += prev->d.point[0].y;
-    }
-  switch (newp->d.type)
+  if (length == 0)
     {
-      case 'l': newp->d.type = 'L'; break;
-      case 'm': newp->d.type = 'M'; break;
-      case 'c': newp->d.type = 'C'; break;
+      if (node_pos_before)
+        *node_pos_before = 0;
+      return 0.0;
     }
-  transform_data (matrix, &newp->d);
-  return head;
-}
-
-/* linear interpolation between two points */
-static void
-lerp (Point  *dest,
-      Point  *a,
-      Point  *b,
-      gfloat  t)
-{
-  dest->x = a->x + (b->x-a->x) * t;
-  dest->y = a->y + (b->y-a->y) * t;
-}
 
-static gdouble
-point_dist (Point *a,
-            Point *b)
-{
-  return sqrt ((a->x-b->x)*(a->x-b->x) +
-               (a->y-b->y)*(a->y-b->y));
-}
+  gegl_path_calc_values (path, n, samples_x, samples_y);
 
-static void
-bezier2 (GeglPathItem  *prev,
-         GeglPathItem  *curve,
-         Point *dest,
-         gfloat t)
-{
-  Point ab,bc,cd,abbc,bccd;
+  for (i=0;i<n;i++)
+    {
+      gdouble dist = (samples_x[i]-x) * (samples_x[i]-x)  +
+                     (samples_y[i]-y) * (samples_y[i]-y);
+      if (dist < closest_dist)
+        {
+          closest_dist = dist;
+          closest_val = i;
+        }
+    }
 
-  if (prev->type == 'c')
-    lerp (&ab, &prev->point[2], &curve->point[0], t);
-  else
-    lerp (&ab, &prev->point[0], &curve->point[0], t);
-  lerp (&bc, &curve->point[0], &curve->point[1], t);
-  lerp (&cd, &curve->point[1], &curve->point[2], t);
-  lerp (&abbc, &ab, &bc,t);
-  lerp (&bccd, &bc, &cd,t);
-  lerp (dest, &abbc, &bccd, t);
-}
+  if (fabs (samples_x[n-1] - samples_x[0]) < 2.1)
+    {
+      if (closest_val == n-1)
+        {
+          closest_val = 0;
+        }
+    }
 
+  if (dx)
+    {
+      *dx = samples_x[closest_val];
+    }
+  if (dy)
+    {
+      *dy = samples_y[closest_val];
+    }
 
+  if (node_pos_before)
+    {
+      GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
+      GeglPathList *iter;
+      /* what node was the one before us ? */
 
-static GeglPathList *flatten_curve (GeglMatrix3  *matrix,
-                                    GeglPathList *head,
-                                    GeglPathList *prev,
-                                    GeglPathList *self)
-{ /* create piecevise linear approximation of bezier curve */
-  gfloat f;
-  Point res;
-  gchar buf[64]="C";
-  GeglPathItem *item=(void*)buf;
+      for (iter=priv->path,i=0; iter;i++,iter=iter->next)
+        {
+          gdouble dist;
+          if (iter->d.type == 'z')
+            continue;
+          dist = gegl_path_closest_point (path,
+                                   iter->d.point[0].x,
+                                   iter->d.point[0].y,
+                                   NULL, NULL, NULL);
+          *node_pos_before = i;
+          if(dist >= closest_val - 2)
+            {
+              *node_pos_before = i-1;
+              break;
+            }
+          /*if(dist > closest_val)
+            {
+              break;
+            }*/
+        }
+    }
 
-  copy_data (&self->d, (void*)buf);
-  transform_data (matrix, (void*)buf);
 
-  for (f=0; f<1.0; f += 1.0 / BEZIER_SEGMENTS)
-    {
-      bezier2 (&prev->d, (void*)buf, &res, f);
-      head = gegl_path_list_append (head, 'L', res.x, res.y);
-    }
+  g_free (samples_x);
+  g_free (samples_y);
 
-  res = item->point[2];
-  head = gegl_path_list_append (head, 'L', res.x, res.y);
 
-  return head;
+  return closest_val * 1.0;
 }
 
-/**
- * gegl_path_list_flatten: (skip)
- */
-static GeglPathList *
-gegl_path_list_flatten (GeglMatrix3  *matrix,
-                        GeglPathList *original)
+gboolean
+gegl_path_calc (GeglPath   *self,
+                gdouble     pos,
+                gdouble    *xd,
+                gdouble    *yd)
 {
-  GeglPathList *iter;
-  GeglPathList *self = NULL;
-
-  GeglPathList *endp = NULL;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
+  GeglPathList *entry = priv->flat_path;
+  GeglPathList *stop  = NULL;
+  gdouble rel_pos  = 0.0;
+  gdouble leftover = 0.0;
+  gboolean result = FALSE;
 
-  if (!original)
-    return NULL;
+  if (!self)
+    return FALSE;
+  ensure_flattened (self);
 
-  for (iter=original; iter; iter=iter->next)
+  if (priv->calc_clean && (pos > priv->calc_leftover))
     {
-      InstructionInfo *info = lookup_instruction_info (iter->d.type);
-      if(info)
-        self = info->flatten (matrix, self, endp, iter);
-      if (!endp)
-        endp = self;
-      while (endp && endp->next)
-        endp=endp->next;
+      entry    = priv->calc_stop;
+      leftover = priv->calc_leftover;
+      rel_pos  = pos - leftover;
+    }
+  else
+    {
+      rel_pos = pos;
     }
-  return self;
-}
-
-static gdouble path_get_length (GeglPathList *path);
 
-static void gegl_path_item_free (GeglPathList *p);
+  if (path_calc2 (entry,rel_pos,xd,yd,&stop,&leftover))
+    {
+      priv->calc_stop = stop;
+      priv->calc_leftover = leftover;
+      priv->calc_clean = TRUE;
 
-GeglPathList *
-gegl_path_list_destroy (GeglPathList *path)
-{
-  GeglPathList *iter = path;
-  while (iter)
+      result = TRUE;
+    }
+  else
     {
-      GeglPathList *next = iter->next;
-      gegl_path_item_free (iter);
-      iter = next;
+      priv->calc_clean = FALSE;
+      result = FALSE;
     }
-  return NULL;
+  return result;
 }
 
-GeglPathList * gegl_path_list_append (GeglPathList *head,
-                                       ...)
+void
+gegl_path_calc_values (GeglPath *self,
+                         guint      num_samples,
+                         gdouble   *xs,
+                         gdouble   *ys)
 {
-  InstructionInfo *info;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
+  if (!self)
+    return;
+  ensure_flattened (self);
+  path_calc_values (priv->flat_path, num_samples, xs, ys);
+}
+
+void gegl_path_get_bounds (GeglPath *self,
+                           gdouble  *min_x,
+                           gdouble  *max_x,
+                           gdouble  *min_y,
+                           gdouble  *max_y)
+{
+  GeglPathPrivate *priv;
   GeglPathList *iter;
-  gchar type;
-  gint pair_no;
 
-  va_list var_args;
-  va_start (var_args, head);
-  type = va_arg (var_args, int); /* we pass in a char, but it is promoted to int by varargs*/
+  *min_x = 256.0;
+  *min_y = 256.0;
+  *max_x = -256.0;
+  *max_y = -256.0;
 
-  info = lookup_instruction_info(type);
-  if (!info)
-    g_error ("didn't find [%c]", type);
-
-  head = gegl_path_list_append_item (head, type, &iter, NULL);
-
-  iter->d.type       = type;
-  for (pair_no=0;pair_no<(info->n_items+2)/2;pair_no++)
-    {
-      iter->d.point[pair_no].x = va_arg (var_args, gdouble);
-      iter->d.point[pair_no].y = va_arg (var_args, gdouble);
-    }
-  va_end (var_args);
-  return head;
-}
-
-static gboolean
-path_calc2 (GeglPathList *path,
-           gdouble       pos,
-           gdouble      *xd,
-           gdouble      *yd,
-           GeglPathList **stop,
-           gdouble      *leftover)
-{
-  GeglPathList *iter = path, *prev = NULL;
-  gfloat traveled = 0.0, next_pos = 0.0;
-
-  while (iter && !prev)
-  /* fetch the start point of the path */
-    {
-      /* fprintf (stderr, "%c, %f %f\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
-      switch (iter->d.type)
-        {
-          case 'M':
-          case 'L':
-            prev = iter;
-            break;
-          default :
-            break;
-        }
-      iter = iter->next;
-    }
-
-  while (iter)
-  /* travel along the path */
-    {
-      /* fprintf (stderr, "%c, %f %f\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
-      switch (iter->d.type)
-        {
-          case 'M':
-            prev = iter;
-            break;
-
-          case 'L':
-            {
-              Point a,b;
-              gfloat distance;
-
-              a.x = prev->d.point[0].x;
-              a.y = prev->d.point[0].y;
-
-              b.x = iter->d.point[0].x;
-              b.y = iter->d.point[0].y;
-
-              distance = point_dist (&a, &b);
-              next_pos += distance;
-
-              if (pos <= next_pos)
-                {
-                  Point spot;
-                  gfloat ratio = (pos - traveled) / (next_pos - traveled);
-
-                  lerp (&spot, &a, &b, ratio);
-
-                  *xd = spot.x;
-                  *yd = spot.y;
-
-                  *stop = prev;
-                  *leftover += traveled;
-                  return TRUE;
-                }
-
-              traveled = next_pos;
-
-              prev = iter;
-            }
-            break;
-          case 's':
-            break;
-          default:
-            g_warning ("can't compute length for instruction: %c\n", iter->d.type);
-            break;
-        }
-      iter=iter->next;
-    }
-  /*fprintf (stderr, "outside iterator bounds");*/
-  return FALSE;
-}
-
-static void path_calc_values (GeglPathList *path,
-                              guint         num_samples,
-                              gdouble      *xs,
-                              gdouble      *ys)
-{
-  GeglPathList *iter = path;
-  gdouble length = path_get_length (path);
-  gfloat spacing = length / (num_samples-1);
-
-  gfloat traveled = 0, next_pos = 0, next_sample = 0;
-  gfloat x = 0, y = 0;
-
-  gint i=0;
-
-  while (iter)
-    {
-      /*fprintf (stderr, "%c, %i %i\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
-      switch (iter->d.type)
-        {
-          case 'M':
-            x = iter->d.point[0].x;
-            y = iter->d.point[0].y;
-            break;
-          case 'L':
-            {
-              Point a,b;
-              gfloat distance;
-
-              a.x = x;
-              a.y = y;
-
-              b.x = iter->d.point[0].x;
-              b.y = iter->d.point[0].y;
-
-              distance = point_dist (&a, &b);
-              next_pos += distance;
-
-              while (next_sample <= next_pos)
-                {
-                  Point spot;
-                  gfloat ratio = (next_sample - traveled) / (next_pos - traveled);
-
-                  lerp (&spot, &a, &b, ratio);
-
-                  xs[i]=spot.x;
-                  ys[i]=spot.y;
-
-                  next_sample += spacing;
-                  i++;
-                }
-              if (!iter->next)
-                {
-                  xs[num_samples-1]=b.x;
-                  ys[num_samples-1]=b.y;
-                }
-
-              x = b.x;
-              y = b.y;
-
-              traveled = next_pos;
-            }
-
-            break;
-          case 'u':
-            g_error ("stroking uninitialized path\n");
-            break;
-          case 's':
-            break;
-          default:
-            g_error ("can't stroke for instruction: %i\n", iter->d.type);
-            break;
-        }
-      iter=iter->next;
-    }
-}
-
-static gdouble
-path_get_length (GeglPathList *path)
-{
-  GeglPathList *iter = path;
-  gfloat traveled_length = 0;
-  gfloat x = 0, y = 0;
-
-  while (iter)
-    {
-      switch (iter->d.type)
-        {
-          case 'M':
-            x = iter->d.point[0].x;
-            y = iter->d.point[0].y;
-            break;
-          case 'L':
-            {
-              Point a,b;
-              gfloat distance;
-
-              a.x = x;
-              a.y = y;
-
-              b.x = iter->d.point[0].x;
-              b.y = iter->d.point[0].y;
-
-              distance = point_dist (&a, &b);
-              traveled_length += distance;
-
-              x = b.x;
-              y = b.y;
-            }
-            break;
-          case 'u':
-            break;
-          case 's':
-            break;
-          default:
-            g_warning ("can't compute length for instruction: %c\n", iter->d.type);
-            return traveled_length;
-            break;
-        }
-      iter=iter->next;
-    }
-  return traveled_length;
-}
-
-
-
-/******************/
-
-typedef struct _PathNameEntity  PathNameEntity;
-
-
-
-
-static GeglPathList *ensure_tail (GeglPathPrivate *priv)
-{
-  GeglPathList *tail_attempt = NULL;
-  GeglPathList *tail;
-
-  if (priv->tail)
-    {
-      for (tail_attempt=priv->tail;
-           tail_attempt && tail_attempt->next;
-           tail_attempt=tail_attempt->next);
-      return tail_attempt; /* comment his out, and
-                           let failures be shown by
-                           the assert below,.. */
-    }
-  for (tail=priv->tail;
-       tail && tail->next;
-       tail=tail->next);
-  if (tail_attempt)
-    {
-      g_assert (tail_attempt == tail);
-    }
-  priv->tail = tail;
-  return tail;
-}
-
-
-
-#include <gegl-buffer.h>
-
-static gint compare_ints (gconstpointer a,
-                          gconstpointer b)
-{
-  return GPOINTER_TO_INT (a)-GPOINTER_TO_INT (b);
-}
-
-static void
-gegl_path_emit_changed (GeglPath            *self,
-                        const GeglRectangle *bounds);
-
-void gegl_path_freeze (GeglPath *path)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
-  priv->frozen ++;
-}
-void gegl_path_thaw (GeglPath *path)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
-  priv->frozen --;
-  gegl_path_emit_changed (path, NULL); /* expose a full changed */
-}
-
-void gegl_path_get_matrix (GeglPath    *path,
-                           GeglMatrix3  *matrix)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
-  gegl_matrix3_copy_into (matrix, &priv->matrix);
-}
-
-void
-gegl_path_set_matrix (GeglPath    *path,
-                      GeglMatrix3  *matrix)
-{
-  GeglPathPrivate *priv;
-  if (!path)
-    {
-      g_warning ("EEek! no path\n");
-      return;
-    }
-  priv = GEGL_PATH_GET_PRIVATE (path);
-  gegl_matrix3_copy_into (&priv->matrix, matrix);
-  priv->flat_path_clean = FALSE;
-  priv->length_clean = FALSE;
-}
-
-static void ensure_flattened (GeglPath *vector)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  gint i;
-  GeglPathList *path = priv->path;
-  GeglPathList *new_path;
-  GeglPathClass *klass= GEGL_PATH_GET_CLASS (vector);
-
-  if (priv->flat_path_clean)
-    return;
-  if (priv->flat_path)
-    gegl_path_list_destroy (priv->flat_path);
-
-  for (i=0;klass->flattener[i];i++)
-    {
-      new_path = klass->flattener[i] (path);
-      if (new_path != path)
-        {
-          if (path != priv->path)
-            gegl_path_list_destroy (path);
-          path = new_path;
-        }
-    }
-
-  priv->flat_path = gegl_path_list_flatten (&priv->matrix, path);
-  if (path != priv->path)
-    gegl_path_list_destroy (path);
-  priv->flat_path_clean = TRUE;
-  priv->length_clean = FALSE;
-  priv->calc_clean = FALSE;
-}
-
-static void
-path_calc_values (GeglPathList *path,
-                  guint         num_samples,
-                  gdouble      *xs,
-                  gdouble      *ys);
-
-
-
-static void
-gegl_path_emit_changed (GeglPath            *self,
-                        const GeglRectangle *bounds)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
-  GeglRectangle rect;
-  GeglRectangle temp;
-  gdouble min_x;
-  gdouble max_x;
-  gdouble min_y;
-  gdouble max_y;
-
-  if (priv->frozen)
-    return;
-
-  gegl_path_get_bounds (self, &min_x, &max_x, &min_y, &max_y);
-
-  rect.x = floor (min_x);
-  rect.y = floor (min_y);
-  rect.width = ceil (max_x) - floor (min_x);
-  rect.height = ceil (max_y) - floor (min_y);
-
-  temp = priv->cached_extent;
-  priv->cached_extent = rect;
-
-  if (!bounds)
-    {
-       gegl_rectangle_bounding_box (&temp, &temp, &rect);
-       bounds = &temp;
-    }
-  g_signal_emit (self, gegl_path_signals[GEGL_PATH_CHANGED], 0,
-                 bounds, NULL);
-}
-
-
-
-GeglPath *
-gegl_path_new (void)
-{
-  GeglPath        *self = GEGL_PATH (g_object_new (GEGL_TYPE_PATH, NULL));
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
-
-  gegl_path_init (self);
-  priv->flat_path_clean = FALSE;
-  priv->length_clean = FALSE;
-  priv->calc_clean = FALSE;
-
-  return self;
-}
-
-GeglPath *
-gegl_path_new_from_string (const gchar *path_string)
-{
-  GeglPath *self = gegl_path_new ();
-  gegl_path_parse_string (self, path_string);
-  return self;
-}
-
-gdouble
-gegl_path_get_length (GeglPath *self)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
-  if (!self)
-    return 0.0;
-
-  if (!priv->length_clean)
-    {
-      ensure_flattened (self);
-      priv->length = path_get_length (priv->flat_path);
-      priv->length_clean = TRUE;
-    }
-  return priv->length;
-}
-
-void gegl_path_get_bounds (GeglPath *self,
-                           gdouble  *min_x,
-                           gdouble  *max_x,
-                           gdouble  *min_y,
-                           gdouble  *max_y)
-{
-  GeglPathPrivate *priv;
-  GeglPathList *iter;
-
-  *min_x = 256.0;
-  *min_y = 256.0;
-  *max_x = -256.0;
-  *max_y = -256.0;
-
-  if (!self)
-    return;
-
-  priv = GEGL_PATH_GET_PRIVATE (self);
-
-  ensure_flattened (self);
-  iter = priv->flat_path;
-
-  while (iter)
-    {
-      gint i;
-      gint max = 0;
-
-      if (iter->d.type == 'M')
-        max = 1;
-      else if (iter->d.type == 'L')
-        max = 1;
-      else if (iter->d.type == 'C')
-        max = 3;
-      else if (iter->d.type == 'z')
-        max = 0;
-
-      for (i=0;i<max;i++)
-        {
-          if (iter->d.point[i].x < *min_x)
-            *min_x = iter->d.point[i].x;
-          if (iter->d.point[i].x > *max_x)
-            *max_x = iter->d.point[i].x;
-          if (iter->d.point[i].y < *min_y)
-            *min_y = iter->d.point[i].y;
-          if (iter->d.point[i].y > *max_y)
-            *max_y = iter->d.point[i].y;
-
-        }
-      iter=iter->next;
-    }
-}
-
-
-
-gboolean
-gegl_path_calc (GeglPath   *self,
-                gdouble     pos,
-                gdouble    *xd,
-                gdouble    *yd)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
-  GeglPathList *entry = priv->flat_path;
-  GeglPathList *stop  = NULL;
-  gdouble rel_pos  = 0.0;
-  gdouble leftover = 0.0;
-  gboolean result = FALSE;
-
-  if (!self)
-    return FALSE;
-  ensure_flattened (self);
-
-  if (priv->calc_clean && (pos > priv->calc_leftover))
-    {
-      entry    = priv->calc_stop;
-      leftover = priv->calc_leftover;
-      rel_pos  = pos - leftover;
-    }
-  else
-    {
-      rel_pos = pos;
-    }
-
-  if (path_calc2 (entry,rel_pos,xd,yd,&stop,&leftover))
-    {
-      priv->calc_stop = stop;
-      priv->calc_leftover = leftover;
-      priv->calc_clean = TRUE;
-
-      result = TRUE;
-    }
-  else
-    {
-      priv->calc_clean = FALSE;
-      result = FALSE;
-    }
-  return result;
-}
-
-
-void
-gegl_path_calc_values (GeglPath *self,
-                         guint      num_samples,
-                         gdouble   *xs,
-                         gdouble   *ys)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
   if (!self)
     return;
-  ensure_flattened (self);
-  path_calc_values (priv->flat_path, num_samples, xs, ys);
-}
-
-
-/* --------------------------------------------------------------------------
- * A GParamSpec class to describe behavior of GeglPath as an object property
- * follows.
- * --------------------------------------------------------------------------
- */
-
-#define GEGL_PARAM_PATH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_PARAM_PATH, GeglParamPath))
-#define GEGL_IS_PARAM_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_PARAM_PATH))
-
-typedef struct _GeglParamPath GeglParamPath;
-
-struct _GeglParamPath
-{
-  GParamSpec parent_instance;
-};
-
-static void
-gegl_param_vector_init (GParamSpec *self)
-{
-}
-
-static void
-gegl_param_vector_finalize (GParamSpec *self)
-{
-  GParamSpecClass *parent_class = g_type_class_peek (g_type_parent (
-                                                       GEGL_TYPE_PARAM_PATH));
-
-  parent_class->finalize (self);
-}
-
-GType
-gegl_param_path_get_type (void)
-{
-  static GType param_vector_type = 0;
-
-  if (G_UNLIKELY (param_vector_type == 0))
-    {
-      static GParamSpecTypeInfo param_vector_type_info = {
-        sizeof (GeglParamPath),
-        0,
-        gegl_param_vector_init,
-        0,
-        gegl_param_vector_finalize,
-        NULL,
-        NULL,
-        NULL
-      };
-      param_vector_type_info.value_type = GEGL_TYPE_PATH;
-
-      param_vector_type = g_param_type_register_static ("GeglParamPath",
-                                                       &param_vector_type_info);
-    }
-
-  return param_vector_type;
-}
-
-const GeglRectangle *gegl_path_changed_rect (GeglPath *vector);
-
-const GeglRectangle *gegl_path_changed_rect (GeglPath *vector)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  return &priv->dirtied;
-}
-
-GParamSpec *
-gegl_param_spec_path   (const gchar *name,
-                        const gchar *nick,
-                        const gchar *blurb,
-                        GeglPath  *default_vector,
-                        GParamFlags  flags)
-{
-  GeglParamPath *param_vector;
-
-  param_vector = g_param_spec_internal (GEGL_TYPE_PARAM_PATH,
-                                       name, nick, blurb, flags);
-
-  return G_PARAM_SPEC (param_vector);
-}
 
-static const gchar *parse_float_pair (const gchar *p,
-                                      gdouble *x,
-                                      gdouble *y)
-{
-  gchar *t = (void*) p;
-  while (*t && ((*t<'0' || *t > '9') && *t!='-')) t++;
-  if (!t)
-    return p;
-  *x = g_ascii_strtod (t, &t);
-  while (*t && ((*t<'0' || *t > '9') && *t!='-')) t++;
-  if (!t)
-    return p;
-  *y = g_ascii_strtod (t, &t);
-  return t;
-}
+  priv = GEGL_PATH_GET_PRIVATE (self);
 
+  ensure_flattened (self);
+  iter = priv->flat_path;
 
-/* this code is generic and should also work for extensions providing
- * new knot types to the infrastructure
- */
-gchar *
-gegl_path_to_string (GeglPath  *vector)
-{
-  GeglPathPrivate *priv;
-  GString *str;
-  gchar *ret;
-  GeglPathList *iter;
-  if (!vector)
-    return g_strdup ("");
-  str = g_string_new ("");
-  priv = GEGL_PATH_GET_PRIVATE (vector);
-  for (iter = priv->path; iter; iter=iter->next)
+  while (iter)
     {
       gint i;
-      InstructionInfo *info = lookup_instruction_info(iter->d.type);
-
-      g_string_append_c (str, iter->d.type);
-      for (i=0;i<(info->n_items+1)/2;i++)
-        {
-          gchar buf[16];
-          gchar *eptr;
-          g_sprintf (buf, "%f", iter->d.point[i].x);
-
-          for (eptr = &buf[strlen(buf)-1];eptr != buf && (*eptr=='0');eptr--)
-              *eptr='\0';
-          if (*eptr=='.')
-            *eptr='\0';
-
-          /* FIXME: make this work better also for other odd count
-           * of n_items
-           */
-          if (info->n_items>1)
-            {
-              g_string_append_printf (str, "%s,", buf);
-              sprintf (buf, "%f", iter->d.point[i].y);
-
-              for (eptr = &buf[strlen(buf)-1];eptr != buf && (*eptr=='0');eptr--)
-                  *eptr='\0';
-              if (*eptr=='.')
-                *eptr='\0';
-            }
-
-          g_string_append_printf (str, "%s ", buf);
-        }
-    }
-
-  ret = str->str;
-  g_string_free (str, FALSE);
-  return ret;
-}
-
-void gegl_path_clear (GeglPath *vector)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  if (priv->path)
-    gegl_path_list_destroy (priv->path);
-  priv->path = NULL;
-  priv->tail = NULL;
-}
-
-void gegl_path_parse_string (GeglPath *vector,
-                             const gchar *path)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  const gchar *p = path;
-  InstructionInfo *previnfo = NULL;
-  gdouble x0, y0, x1, y1, x2, y2;
-  gchar *param_name = NULL;
+      gint max = 0;
 
-  while (*p)
-    {
-      gchar     type = *p;
+      if (iter->d.type == 'M')
+        max = 1;
+      else if (iter->d.type == 'L')
+        max = 1;
+      else if (iter->d.type == 'C')
+        max = 3;
+      else if (iter->d.type == 'z')
+        max = 0;
 
-      if (type == '!')
-        {
-          gint i = 0;
-          if (param_name)
-            g_free (param_name);
-          param_name = g_malloc0 (32); /* XXX: nasty limitation, might have
-                                          security issues */
-          p++;
-          while (*p != ' ')
-            {
-              param_name[i++]=*p;
-              p++;
-            }
-        }
-      else
+      for (i=0;i<max;i++)
         {
-          InstructionInfo *info = lookup_instruction_info(type);
-          if (!info && ((type>= '0' && type <= '9') || type == '-'))
-            {
-              if (!previnfo)   /* XXX: suspicious code !!! */
-                {
-                  info = previnfo;
-                  type = previnfo->type;  /* XXX: previnfo _is_ NULL */
-                }
-              else
-                {
-                  if (previnfo->type == 'M')
-                    {
-                      info = lookup_instruction_info(type = 'L');
-                    }
-                  else if (previnfo->type == 'm')
-                    {
-                      info = lookup_instruction_info(type = 'l');
-                    }
-                  else if (previnfo->type == ' ')
-                    g_warning ("EEEK");
-                }
-            }
-
-          if (info)
-            {
-              switch (info->n_items)
-                {
-                  case 0:
-                    priv->path = gegl_path_list_append (priv->path, type, 0., 0.);
-                    /* coordinates are ignored, all of these could have used add3)*/
-                    break;
-                  case 2:
-                    p = parse_float_pair (p, &x0, &y0);
-                    priv->path = gegl_path_list_append (priv->path, type, x0, y0);
-                    continue;
-                  case 4:
-                    p = parse_float_pair (p, &x0, &y0);
-                    p = parse_float_pair (p, &x1, &y1);
-                    priv->path = gegl_path_list_append (priv->path, type, x0, y0, x1, y1);
-                    continue;
-                  case 6:
-                    p = parse_float_pair (p, &x0, &y0);
-                    p = parse_float_pair (p, &x1, &y1);
-                    p = parse_float_pair (p, &x2, &y2);
-                    priv->path = gegl_path_list_append (priv->path, type, x0, y0, x1, y1, x2, y2);
-                    continue;
-                  default:
-                    g_warning ("parsing of data %i items not implemented\n", info->n_items);
-                    continue;
-                }
-              previnfo = info;
-            }
-          if (*p)
-            p++;
+          if (iter->d.point[i].x < *min_x)
+            *min_x = iter->d.point[i].x;
+          if (iter->d.point[i].x > *max_x)
+            *max_x = iter->d.point[i].x;
+          if (iter->d.point[i].y < *min_y)
+            *min_y = iter->d.point[i].y;
+          if (iter->d.point[i].y > *max_y)
+            *max_y = iter->d.point[i].y;
+
         }
+      iter=iter->next;
     }
-
-  priv->flat_path_clean = FALSE;
-  priv->length_clean = FALSE;
-  {
-   gegl_path_emit_changed (vector, NULL);
-  }
-}
-
-gboolean gegl_path_is_empty (GeglPath *path)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
-  return priv->path != NULL;
 }
 
-gint
-gegl_path_get_n_nodes  (GeglPath *vector)
+void  gegl_path_foreach (GeglPath *vector,
+                                void (*func) (const GeglPathItem *knot,
+                                              gpointer              data),
+                                gpointer    data)
 {
   GeglPathPrivate *priv;
   GeglPathList *iter;
-  gint count=0;
   if (!vector)
-    return 0;
+    return;
   priv = GEGL_PATH_GET_PRIVATE (vector);
-
   for (iter = priv->path; iter; iter=iter->next)
     {
-      count ++;
+      func (&(iter->d), data);
     }
-  return count;
-
 }
 
-gboolean
-gegl_path_get_node (GeglPath     *vector,
-                    gint          index,
-                    GeglPathItem *node)
+
+void  gegl_path_foreach_flat (GeglPath *vector,
+                                     void (*func) (const GeglPathItem *knot,
+                                                   gpointer              data),
+                                     gpointer    data)
 {
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
+  GeglPathPrivate *priv;
   GeglPathList *iter;
-  GeglPathItem *last = NULL;
-  gint count=0;
-  for (iter = priv->path; iter; iter=iter->next)
-    {
-      last=&iter->d;
-      if (count == index)
-        {
-          copy_data (last, node);
-          return TRUE;
-        }
-      count ++;
-    }
-  if (index==-1)
+  if (!vector)
+    return;
+  priv = GEGL_PATH_GET_PRIVATE (vector);
+  ensure_flattened (vector);
+  for (iter = priv->flat_path; iter; iter=iter->next)
     {
-      copy_data (last, node);
-      return TRUE;
+      func (&(iter->d), data);
     }
-  return FALSE;
-}
-
-/* -1 means last */
-
-/* pos = 0, pushes the existing 0 if any to 1,
- * passing -1 means add at end
- */
-
-static void gegl_path_item_free (GeglPathList *p)
-{
-  InstructionInfo *info = lookup_instruction_info(p->d.type);
-  g_slice_free1 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 * (info->n_items+3)/2, p);
 }
 
-void  gegl_path_remove_node  (GeglPath *vector,
-                              gint      pos)
+void gegl_path_clear (GeglPath *vector)
 {
   GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  GeglPathList *iter;
-  GeglPathList *prev = NULL;
-
-  gint count=0;
-
-  if (pos == -1)
-    pos = gegl_path_get_n_nodes (vector)-1;
-
-  for (iter = priv->path; iter; iter=iter->next)
-    {
-      if (count == pos)
-        {
-          if (prev)
-            prev->next = iter->next;
-          else
-            priv->path = iter->next;
-          gegl_path_item_free (iter);
-          break;
-        }
-      prev = iter;
-      count ++;
-    }
-
-  priv->flat_path_clean = FALSE;
-  priv->length_clean = FALSE;
+  if (priv->path)
+    gegl_path_list_destroy (priv->path);
+  priv->path = NULL;
   priv->tail = NULL;
-  gegl_path_emit_changed (vector, NULL);
 }
 
+/* -1 means last */
 
+/* pos = 0, pushes the existing 0 if any to 1,
+ * passing -1 means add at end
+ */
 void  gegl_path_insert_node     (GeglPath           *vector,
                             gint                pos,
                             const GeglPathItem *knot)
@@ -1440,61 +755,131 @@ void  gegl_path_replace_node (GeglPath           *vector,
   gegl_path_emit_changed (vector, NULL);
 }
 
-void  gegl_path_foreach (GeglPath *vector,
-                                void (*func) (const GeglPathItem *knot,
-                                              gpointer              data),
-                                gpointer    data)
+void  gegl_path_remove_node  (GeglPath *vector,
+                              gint      pos)
 {
-  GeglPathPrivate *priv;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
   GeglPathList *iter;
-  if (!vector)
-    return;
-  priv = GEGL_PATH_GET_PRIVATE (vector);
-  for (iter = priv->path; iter; iter=iter->next)
-    {
-      func (&(iter->d), data);
-    }
-}
+  GeglPathList *prev = NULL;
 
+  gint count=0;
 
-void  gegl_path_foreach_flat (GeglPath *vector,
-                                     void (*func) (const GeglPathItem *knot,
-                                                   gpointer              data),
-                                     gpointer    data)
-{
-  GeglPathPrivate *priv;
-  GeglPathList *iter;
-  if (!vector)
-    return;
-  priv = GEGL_PATH_GET_PRIVATE (vector);
-  ensure_flattened (vector);
-  for (iter = priv->flat_path; iter; iter=iter->next)
+  if (pos == -1)
+    pos = gegl_path_get_n_nodes (vector)-1;
+
+  for (iter = priv->path; iter; iter=iter->next)
     {
-      func (&(iter->d), data);
+      if (count == pos)
+        {
+          if (prev)
+            prev->next = iter->next;
+          else
+            priv->path = iter->next;
+          gegl_path_item_free (iter);
+          break;
+        }
+      prev = iter;
+      count ++;
     }
+
+  priv->flat_path_clean = FALSE;
+  priv->length_clean = FALSE;
+  priv->tail = NULL;
+  gegl_path_emit_changed (vector, NULL);
 }
 
-void  gegl_path_add_flattener (GeglPathList *(*flattener) (GeglPathList *original))
+void gegl_path_parse_string (GeglPath *vector,
+                             const gchar *path)
 {
-  GeglPath *vector = g_object_new (GEGL_TYPE_PATH, NULL);
-  GeglPathClass *klass= GEGL_PATH_GET_CLASS (vector);
-  gint i;
-  g_object_unref (vector);
-  /* currently only one additional flattener is supported, this should be fixed,
-   * and flatteners should be able to return the original pointer to indicate
-   * that no op was done, making memory handling more efficient
-   */
-  for (i=0;i<8;i++)
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
+  const gchar *p = path;
+  InstructionInfo *previnfo = NULL;
+  gdouble x0, y0, x1, y1, x2, y2;
+  gchar *param_name = NULL;
+
+  while (*p)
     {
-      if (klass->flattener[i]==NULL)
+      gchar     type = *p;
+
+      if (type == '!')
         {
-          klass->flattener[i] = flattener;
-          klass->flattener[i+1] = NULL;
-          return;
+          gint i = 0;
+          if (param_name)
+            g_free (param_name);
+          param_name = g_malloc0 (32); /* XXX: nasty limitation, might have
+                                          security issues */
+          p++;
+          while (*p != ' ')
+            {
+              param_name[i++]=*p;
+              p++;
+            }
+        }
+      else
+        {
+          InstructionInfo *info = lookup_instruction_info(type);
+          if (!info && ((type>= '0' && type <= '9') || type == '-'))
+            {
+              if (!previnfo)   /* XXX: suspicious code !!! */
+                {
+                  info = previnfo;
+                  type = previnfo->type;  /* XXX: previnfo _is_ NULL */
+                }
+              else
+                {
+                  if (previnfo->type == 'M')
+                    {
+                      info = lookup_instruction_info(type = 'L');
+                    }
+                  else if (previnfo->type == 'm')
+                    {
+                      info = lookup_instruction_info(type = 'l');
+                    }
+                  else if (previnfo->type == ' ')
+                    g_warning ("EEEK");
+                }
+            }
+
+          if (info)
+            {
+              switch (info->n_items)
+                {
+                  case 0:
+                    priv->path = gegl_path_list_append (priv->path, type, 0., 0.);
+                    /* coordinates are ignored, all of these could have used add3)*/
+                    break;
+                  case 2:
+                    p = parse_float_pair (p, &x0, &y0);
+                    priv->path = gegl_path_list_append (priv->path, type, x0, y0);
+                    continue;
+                  case 4:
+                    p = parse_float_pair (p, &x0, &y0);
+                    p = parse_float_pair (p, &x1, &y1);
+                    priv->path = gegl_path_list_append (priv->path, type, x0, y0, x1, y1);
+                    continue;
+                  case 6:
+                    p = parse_float_pair (p, &x0, &y0);
+                    p = parse_float_pair (p, &x1, &y1);
+                    p = parse_float_pair (p, &x2, &y2);
+                    priv->path = gegl_path_list_append (priv->path, type, x0, y0, x1, y1, x2, y2);
+                    continue;
+                  default:
+                    g_warning ("parsing of data %i items not implemented\n", info->n_items);
+                    continue;
+                }
+              previnfo = info;
+            }
+          if (*p)
+            p++;
         }
     }
-}
 
+  priv->flat_path_clean = FALSE;
+  priv->length_clean = FALSE;
+  {
+   gegl_path_emit_changed (vector, NULL);
+  }
+}
 
 void
 gegl_path_append (GeglPath *self,
@@ -1577,527 +962,715 @@ gegl_path_append (GeglPath *self,
     }
 }
 
-gdouble
-gegl_path_closest_point (GeglPath *path,
-                         gdouble   x,
-                         gdouble   y,
-                         gdouble  *dx,
-                         gdouble  *dy,
-                         gint     *node_pos_before)
+void gegl_path_freeze (GeglPath *path)
 {
-  gdouble length = gegl_path_get_length (path);
-  gint     i, n;
-  gdouble closest_dist = 100000;
-  gint   closest_val = 0;
-  gdouble  *samples_x;
-  gdouble  *samples_y;
-  n = ceil(length);
-  samples_x = g_malloc (sizeof (gdouble)* n);
-  samples_y = g_malloc (sizeof (gdouble)* n);
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
+  priv->frozen ++;
+}
 
-  if (length == 0)
+void gegl_path_thaw (GeglPath *path)
+{
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
+  priv->frozen --;
+  gegl_path_emit_changed (path, NULL); /* expose a full changed */
+}
+
+void
+gegl_path_add_type (gchar        type,
+                    gint         n_items,
+                    const gchar *name)
+{
+  gint i;
+  for (i=0; knot_types[i].type != '\0'; i++)
+    if (knot_types[i].type == type)
+      {
+        g_warning ("control point type %c already exists\n", type);
+        return;
+      }
+  knot_types[i].type = type;
+  knot_types[i].n_items = n_items;
+  knot_types[i].name = g_strdup (name);
+  knot_types[i].flatten = flatten_copy;
+  knot_types[i+1].type = '\0';
+  return;
+}
+
+void  gegl_path_add_flattener (GeglPathList *(*flattener) (GeglPathList *original))
+{
+  GeglPath *vector = g_object_new (GEGL_TYPE_PATH, NULL);
+  GeglPathClass *klass= GEGL_PATH_GET_CLASS (vector);
+  gint i;
+  g_object_unref (vector);
+  /* currently only one additional flattener is supported, this should be fixed,
+   * and flatteners should be able to return the original pointer to indicate
+   * that no op was done, making memory handling more efficient
+   */
+  for (i=0;i<8;i++)
     {
-      if (node_pos_before)
-        *node_pos_before = 0;
-      return 0.0;
+      if (klass->flattener[i]==NULL)
+        {
+          klass->flattener[i] = flattener;
+          klass->flattener[i+1] = NULL;
+          return;
+        }
+    }
+}
+
+static const gchar *parse_float_pair (const gchar *p,
+                                      gdouble *x,
+                                      gdouble *y)
+{
+  gchar *t = (void*) p;
+  while (*t && ((*t<'0' || *t > '9') && *t!='-')) t++;
+  if (!t)
+    return p;
+  *x = g_ascii_strtod (t, &t);
+  while (*t && ((*t<'0' || *t > '9') && *t!='-')) t++;
+  if (!t)
+    return p;
+  *y = g_ascii_strtod (t, &t);
+  return t;
+}
+
+
+static gboolean
+path_calc2 (GeglPathList *path,
+           gdouble       pos,
+           gdouble      *xd,
+           gdouble      *yd,
+           GeglPathList **stop,
+           gdouble      *leftover)
+{
+  GeglPathList *iter = path, *prev = NULL;
+  gfloat traveled = 0.0, next_pos = 0.0;
+
+  while (iter && !prev)
+  /* fetch the start point of the path */
+    {
+      /* fprintf (stderr, "%c, %f %f\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
+      switch (iter->d.type)
+        {
+          case 'M':
+          case 'L':
+            prev = iter;
+            break;
+          default :
+            break;
+        }
+      iter = iter->next;
+    }
+
+  while (iter)
+  /* travel along the path */
+    {
+      /* fprintf (stderr, "%c, %f %f\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
+      switch (iter->d.type)
+        {
+          case 'M':
+            prev = iter;
+            break;
+
+          case 'L':
+            {
+              Point a,b;
+              gfloat distance;
+
+              a.x = prev->d.point[0].x;
+              a.y = prev->d.point[0].y;
+
+              b.x = iter->d.point[0].x;
+              b.y = iter->d.point[0].y;
+
+              distance = point_dist (&a, &b);
+              next_pos += distance;
+
+              if (pos <= next_pos)
+                {
+                  Point spot;
+                  gfloat ratio = (pos - traveled) / (next_pos - traveled);
+
+                  lerp (&spot, &a, &b, ratio);
+
+                  *xd = spot.x;
+                  *yd = spot.y;
+
+                  *stop = prev;
+                  *leftover += traveled;
+                  return TRUE;
+                }
+
+              traveled = next_pos;
+
+              prev = iter;
+            }
+            break;
+          case 's':
+            break;
+          default:
+            g_warning ("can't compute length for instruction: %c\n", iter->d.type);
+            break;
+        }
+      iter=iter->next;
+    }
+  /*fprintf (stderr, "outside iterator bounds");*/
+  return FALSE;
+}
+
+
+static void path_calc_values (GeglPathList *path,
+                              guint         num_samples,
+                              gdouble      *xs,
+                              gdouble      *ys)
+{
+  GeglPathList *iter = path;
+  gdouble length = path_get_length (path);
+  gfloat spacing = length / (num_samples-1);
+
+  gfloat traveled = 0, next_pos = 0, next_sample = 0;
+  gfloat x = 0, y = 0;
+
+  gint i=0;
+
+  while (iter)
+    {
+      /*fprintf (stderr, "%c, %i %i\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
+      switch (iter->d.type)
+        {
+          case 'M':
+            x = iter->d.point[0].x;
+            y = iter->d.point[0].y;
+            break;
+          case 'L':
+            {
+              Point a,b;
+              gfloat distance;
+
+              a.x = x;
+              a.y = y;
+
+              b.x = iter->d.point[0].x;
+              b.y = iter->d.point[0].y;
+
+              distance = point_dist (&a, &b);
+              next_pos += distance;
+
+              while (next_sample <= next_pos)
+                {
+                  Point spot;
+                  gfloat ratio = (next_sample - traveled) / (next_pos - traveled);
+
+                  lerp (&spot, &a, &b, ratio);
+
+                  xs[i]=spot.x;
+                  ys[i]=spot.y;
+
+                  next_sample += spacing;
+                  i++;
+                }
+              if (!iter->next)
+                {
+                  xs[num_samples-1]=b.x;
+                  ys[num_samples-1]=b.y;
+                }
+
+              x = b.x;
+              y = b.y;
+
+              traveled = next_pos;
+            }
+
+            break;
+          case 'u':
+            g_error ("stroking uninitialized path\n");
+            break;
+          case 's':
+            break;
+          default:
+            g_error ("can't stroke for instruction: %i\n", iter->d.type);
+            break;
+        }
+      iter=iter->next;
     }
+}
 
-  gegl_path_calc_values (path, n, samples_x, samples_y);
+static gdouble
+path_get_length (GeglPathList *path)
+{
+  GeglPathList *iter = path;
+  gfloat traveled_length = 0;
+  gfloat x = 0, y = 0;
 
-  for (i=0;i<n;i++)
+  while (iter)
     {
-      gdouble dist = (samples_x[i]-x) * (samples_x[i]-x)  +
-                     (samples_y[i]-y) * (samples_y[i]-y);
-      if (dist < closest_dist)
+      switch (iter->d.type)
         {
-          closest_dist = dist;
-          closest_val = i;
-        }
-    }
+          case 'M':
+            x = iter->d.point[0].x;
+            y = iter->d.point[0].y;
+            break;
+          case 'L':
+            {
+              Point a,b;
+              gfloat distance;
 
-  if (fabs (samples_x[n-1] - samples_x[0]) < 2.1)
-    {
-      if (closest_val == n-1)
-        {
-          closest_val = 0;
-        }
-    }
+              a.x = x;
+              a.y = y;
 
-  if (dx)
-    {
-      *dx = samples_x[closest_val];
-    }
-  if (dy)
-    {
-      *dy = samples_y[closest_val];
-    }
+              b.x = iter->d.point[0].x;
+              b.y = iter->d.point[0].y;
 
-  if (node_pos_before)
-    {
-      GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (path);
-      GeglPathList *iter;
-      /* what node was the one before us ? */
+              distance = point_dist (&a, &b);
+              traveled_length += distance;
 
-      for (iter=priv->path,i=0; iter;i++,iter=iter->next)
-        {
-          gdouble dist;
-          if (iter->d.type == 'z')
-            continue;
-          dist = gegl_path_closest_point (path,
-                                   iter->d.point[0].x,
-                                   iter->d.point[0].y,
-                                   NULL, NULL, NULL);
-          *node_pos_before = i;
-          if(dist >= closest_val - 2)
-            {
-              *node_pos_before = i-1;
-              break;
+              x = b.x;
+              y = b.y;
             }
-          /*if(dist > closest_val)
-            {
-              break;
-            }*/
+            break;
+          case 'u':
+            break;
+          case 's':
+            break;
+          default:
+            g_warning ("can't compute length for instruction: %c\n", iter->d.type);
+            return traveled_length;
+            break;
         }
+      iter=iter->next;
     }
+  return traveled_length;
+}
 
 
-  g_free (samples_x);
-  g_free (samples_y);
+static void
+gegl_path_emit_changed (GeglPath            *self,
+                        const GeglRectangle *bounds)
+{
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (self);
+  GeglRectangle rect;
+  GeglRectangle temp;
+  gdouble min_x;
+  gdouble max_x;
+  gdouble min_y;
+  gdouble max_y;
+
+  if (priv->frozen)
+    return;
 
+  gegl_path_get_bounds (self, &min_x, &max_x, &min_y, &max_y);
 
-  return closest_val * 1.0;
-}
+  rect.x = floor (min_x);
+  rect.y = floor (min_y);
+  rect.width = ceil (max_x) - floor (min_x);
+  rect.height = ceil (max_y) - floor (min_y);
 
-/**************************************/
+  temp = priv->cached_extent;
+  priv->cached_extent = rect;
 
-#define AA 3
+  if (!bounds)
+    {
+       gegl_rectangle_bounding_box (&temp, &temp, &rect);
+       bounds = &temp;
+    }
+  g_signal_emit (self, gegl_path_signals[GEGL_PATH_CHANGED], 0,
+                 bounds, NULL);
+}
 
-static void gegl_buffer_accumulate (GeglBuffer    *buffer,
-                                    GeglRectangle *roi,
-                                    const gfloat  *col)
+static void ensure_flattened (GeglPath *vector)
 {
-  static Babl   *format = NULL;
-  static gfloat *buf = NULL;
-  static gint len = 0;
+  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
   gint i;
+  GeglPathList *path = priv->path;
+  GeglPathList *new_path;
+  GeglPathClass *klass= GEGL_PATH_GET_CLASS (vector);
 
-  if (!format)
-    format = babl_format ("RaGaBaA float");
+  if (priv->flat_path_clean)
+    return;
+  if (priv->flat_path)
+    gegl_path_list_destroy (priv->flat_path);
 
-  if (!buf || len < roi->width)
+  for (i=0;klass->flattener[i];i++)
     {
-      len = roi->width;
-      if (buf)
-        g_free (buf);
-      buf = g_malloc (len * 4 *4);
-      /* final scratch buf will be "leaked" */
+      new_path = klass->flattener[i] (path);
+      if (new_path != path)
+        {
+          if (path != priv->path)
+            gegl_path_list_destroy (path);
+          path = new_path;
+        }
     }
 
-  gegl_buffer_get (buffer, 1.0, roi, format, buf, 0);
-  for (i=0; i < roi->width; i++)
+  priv->flat_path = gegl_path_list_flatten (&priv->matrix, path);
+  if (path != priv->path)
+    gegl_path_list_destroy (path);
+  priv->flat_path_clean = TRUE;
+  priv->length_clean = FALSE;
+  priv->calc_clean = FALSE;
+}
+
+static GeglPathList *ensure_tail (GeglPathPrivate *priv)
+{
+  GeglPathList *tail_attempt = NULL;
+  GeglPathList *tail;
+
+  if (priv->tail)
+    {
+      for (tail_attempt=priv->tail;
+           tail_attempt && tail_attempt->next;
+           tail_attempt=tail_attempt->next);
+      return tail_attempt; /* comment his out, and
+                           let failures be shown by
+                           the assert below,.. */
+    }
+  for (tail=priv->tail;
+       tail && tail->next;
+       tail=tail->next);
+  if (tail_attempt)
     {
-      gint j;
-      for (j=0; j<4; j++)
-        buf[i*4 + j] += col[j];
+      g_assert (tail_attempt == tail);
     }
-  gegl_buffer_set (buffer, roi, format, buf, 0);
+  priv->tail = tail;
+  return tail;
+}
+/***** Flattener *****/
+static GeglPathList *flatten_nop (GeglMatrix3   *matrix,
+                                  GeglPathList *head,
+                                  GeglPathList *prev,
+                                  GeglPathList *self)
+{
+  return head;
 }
 
+static GeglPathList *flatten_copy (GeglMatrix3   *matrix,
+                                   GeglPathList *head,
+                                   GeglPathList *prev,
+                                   GeglPathList *self)
+{
+  GeglPathList *newp;
+  head = gegl_path_list_append_item (head, self->d.type, &newp, NULL);
+  copy_data (&self->d, &newp->d);
+  transform_data (matrix, &newp->d);
+  return head;
+}
 
-/* XXX: should be removed?  */
-void gegl_path_fill (GeglBuffer *buffer,
-                     GeglPath   *vector,
-                     GeglColor  *color,
-                     gboolean    winding);
-
+static GeglPathList *
+flatten_rel_copy (GeglMatrix3   *matrix,
+                  GeglPathList *head,
+                  GeglPathList *prev,
+                  GeglPathList *self)
+{
+  GeglPathList *newp;
+  InstructionInfo *info;
+  gint i;
 
+  head = gegl_path_list_append_item (head, self->d.type, &newp, NULL);
+  copy_data (&self->d, &newp->d);
+  info = lookup_instruction_info (self->d.type);
+  for (i=0;i<(info->n_items+1)/2;i++)
+    {
+      newp->d.point[i].x += prev->d.point[0].x;
+      newp->d.point[i].y += prev->d.point[0].y;
+    }
+  switch (newp->d.type)
+    {
+      case 'l': newp->d.type = 'L'; break;
+      case 'm': newp->d.type = 'M'; break;
+      case 'c': newp->d.type = 'C'; break;
+    }
+  transform_data (matrix, &newp->d);
+  return head;
+}
 
-void gegl_path_fill (GeglBuffer *buffer,
-                     GeglPath   *vector,
-                     GeglColor  *color,
-                     gboolean    winding)
-{
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  gdouble xmin, xmax, ymin, ymax;
-  GeglRectangle extent;
-  gfloat  horsub = AA;
-  gint    versubi = horsub;
-  gfloat  versub = versubi;
-  gint    samples;
+static GeglPathList *flatten_curve (GeglMatrix3  *matrix,
+                                    GeglPathList *head,
+                                    GeglPathList *prev,
+                                    GeglPathList *self)
+{ /* create piecevise linear approximation of bezier curve */
+  gfloat f;
+  Point res;
+  gchar buf[64]="C";
+  GeglPathItem *item=(void*)buf;
 
-  if (!vector)
-    return;
+  copy_data (&self->d, (void*)buf);
+  transform_data (matrix, (void*)buf);
 
-  ensure_flattened (vector);
-  samples = path_get_length (priv->flat_path);
-  gegl_path_get_bounds (vector, &xmin, &xmax, &ymin, &ymax);
+  for (f=0; f<1.0; f += 1.0 / BEZIER_SEGMENTS)
+    {
+      bezier2 (&prev->d, (void*)buf, &res, f);
+      head = gegl_path_list_append (head, 'L', res.x, res.y);
+    }
 
-  extent.x = floor (xmin);
-  extent.y = floor (ymin);
-  extent.width = ceil (xmax) - extent.x;
-  extent.height = ceil (ymax) - extent.y;
+  res = item->point[2];
+  head = gegl_path_list_append (head, 'L', res.x, res.y);
 
-  {
-    GSList **scanlines;
-    gdouble *xs;
-    gdouble *ys;
-
-    gint i;
-    gdouble prev_x;
-    gint    prev_y;
-    gdouble first_x;
-    gint    first_y;
-    gint    lastline=-1;
-    gint    lastdir=-2;
-
-    xs = g_newa (gdouble, samples);
-    ys = g_newa (gdouble, samples);
-    path_calc_values (priv->flat_path, samples, xs, ys);
-
-    /* clear scanline intersection lists */
-    scanlines = g_newa (GSList*, extent.height * versubi);
-    for (i=0; i < extent.height * versub; i++)
-      scanlines[i]=NULL;
-
-    first_x = prev_x = xs[0] * horsub;
-    first_y = prev_y = ys[0] * versub;
-
-    /* saturate scanline intersection list */
-    for (i=1; i<samples; i++)
-      {
-        gint dest_x = xs[i] * horsub;
-        gint dest_y = ys[i] * versub;
-        gint ydir;
-        gint dx;
-        gint dy;
-        gint y;
-fill_close:  /* label used for goto to close last segment */
-        dx = dest_x - prev_x;
-        dy = dest_y - prev_y;
-
-        if (dy < 0)
-          ydir = -1;
-        else
-          ydir = 1;
-
-        /* do linear interpolation between vertexes */
-        for (y=prev_y; y!= dest_y; y += ydir)
-          {
-            if (y-extent.y * versub >= 0 &&
-                y-extent.y * versub < extent.height * versub &&
-                lastline != y)
-              {
-                gint x = prev_x + (dx * (y-prev_y)) / dy;
-                /* XXX: * clamp the spans to the width/height of the buffer ? */
-
-                scanlines[ y - extent.y * versubi]=
-                  g_slist_insert_sorted (scanlines[ y - extent.y * versubi],
-                                         GINT_TO_POINTER(x),
-                                         compare_ints);
-                if (ydir != lastdir &&
-                    lastdir != -2)
-                  scanlines[ y - extent.y * versubi]=
-                    g_slist_insert_sorted (scanlines[ y - extent.y * versubi],
-                                           GINT_TO_POINTER(x),
-                                           compare_ints);
-                lastdir = ydir;
-                lastline = y;
-              }
-          }
-
-        prev_x = dest_x;
-        prev_y = dest_y;
-
-        /* if we're on the last knot, fake the first vertex being a next one */
-        if (i+1 == samples)
-          {
-            dest_x = first_x;
-            dest_y = first_y;
-            i++; /* to make the loop finally end */
-            goto fill_close;
-          }
-      }
+  return head;
+}
 
-    /* Fill the spans */
+/***** InstructInfo *****/
+static InstructionInfo *lookup_instruction_info (gchar type)
 {
-    gfloat col[4];
-    gfloat factor = 1.0/(horsub * versub);
-
-    gegl_color_get_rgba4f (color, col);
+  gint i;
+  for (i=0; knot_types[i].type != '\0'; i++)
+    if (knot_types[i].type == type)
+      return &knot_types[i];
+  return NULL;
+}
 
-    col[0] *= factor;
-    col[1] *= factor;
-    col[2] *= factor;
-    col[3] *= factor;
+/***** GeglPathItem *****/
+static void transform_data (GeglMatrix3  *matrix,
+                            GeglPathItem *dst)
+{
+  InstructionInfo *dst_info = lookup_instruction_info(dst->type);
+  gint i;
 
-    for (i=0; i < extent.height * versub; i++)
-      {
-        GSList *iter = scanlines[i];
-        while (iter)
-          {
-            GSList *next = iter->next;
-            gint    j;
-            gint startx, endx;
-            if (!next)
-              break;
+  for (i=0;i<(dst_info->n_items+1)/2;i++)
+    {
+      gdouble x = dst->point[i].x;
+      gdouble y = dst->point[i].y;
+      gegl_matrix3_transform_point (matrix, &x, &y);
+      dst->point[i].x=x;
+      dst->point[i].y=y;
+    }
+}
 
-            startx = GPOINTER_TO_INT (iter->data);
-            endx   = GPOINTER_TO_INT (next->data);
+/* XXX: copy_data should exit for internal to external conversions */
 
-            for (j=0;j<horsub;j++)
-            {
-              GeglRectangle roi;
+static void copy_data (const GeglPathItem *src,
+                       GeglPathItem       *dst)
+{
+  InstructionInfo *src_info;
+  gint i;
 
-              roi.x = (startx+j)/horsub;
-              roi.y = extent.y + i/versub;
-              roi.width = (endx - startx-j + horsub) / horsub;
-              roi.height = 1;
-              gegl_buffer_accumulate (buffer, &roi, col);
-            }
+  if (!src)
+    return;
 
-            iter = next->next;
-          }
-        if (scanlines[i])
-          g_slist_free (scanlines[i]);
-      }
+  src_info = lookup_instruction_info(src->type);
 
-    if (gegl_buffer_is_shared (buffer))
-    gegl_buffer_unlock (buffer);
-  }
-  }
+  dst->type = src->type;
+  for (i=0;i<(src_info->n_items+1)/2;i++)
+    {
+      dst->point[i].x = src->point[i].x;
+      dst->point[i].y = src->point[i].y;
+    }
 }
 
-typedef struct StampStatic {
-  gboolean  valid;
-  Babl     *format;
-  gfloat   *buf;
-  gdouble   radius;
-}StampStatic;
-
-
-static void gegl_path_stamp (GeglBuffer *buffer,
-                             const GeglRectangle *clip_rect,
-                             gdouble     x,
-                             gdouble     y,
-                             gdouble     radius,
-                             gdouble     hardness,
-                             GeglColor  *color,
-                             gdouble     opacity)
+static void
+bezier2 (GeglPathItem  *prev,
+         GeglPathItem  *curve,
+         Point *dest,
+         gfloat t)
 {
-  gfloat col[4];
-  StampStatic s = {FALSE,}; /* there should be a cache of stamps,
-                               note that stamps are accessed in multiple threads
-                             */
+  Point ab,bc,cd,abbc,bccd;
 
-  GeglRectangle temp;
-  GeglRectangle roi;
+  if (prev->type == 'c')
+    lerp (&ab, &prev->point[2], &curve->point[0], t);
+  else
+    lerp (&ab, &prev->point[0], &curve->point[0], t);
+  lerp (&bc, &curve->point[0], &curve->point[1], t);
+  lerp (&cd, &curve->point[1], &curve->point[2], t);
+  lerp (&abbc, &ab, &bc,t);
+  lerp (&bccd, &bc, &cd,t);
+  lerp (dest, &abbc, &bccd, t);
+}
+static void gegl_path_item_free (GeglPathList *p)
+{
+  InstructionInfo *info = lookup_instruction_info(p->d.type);
+  g_slice_free1 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 * (info->n_items+3)/2, p);
+}
 
-  roi.x = floor(x-radius);
-  roi.y = floor(y-radius);
-  roi.width = ceil (x+radius) - floor (x-radius);
-  roi.height = ceil (y+radius) - floor (y-radius);
+/***** GeglPathList *****/
+static GeglPathList *
+gegl_path_list_append_item  (GeglPathList  *head,
+                             gchar          type,
+                             GeglPathList **res,
+                             GeglPathList  *tail)
+{
+  GeglPathList *iter = tail?tail:head;
+  InstructionInfo *info = lookup_instruction_info (type);
+  g_assert (info);
 
-  gegl_color_get_rgba4f (color, col);
+  while (iter && iter->next)
+    iter=iter->next;
 
-  /* bail out if we wouldn't leave a mark on the buffer */
-  if (!gegl_rectangle_intersect (&temp, &roi, clip_rect))
+  if (iter)
     {
-      return;
+      /* the +3 is padding, +1 was excpected to be sufficient */
+      iter->next =
+        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
+      iter->next->d.type = type;
+      iter = iter->next;
     }
-
-  if (s.format == NULL)
-    s.format = babl_format ("RaGaBaA float");
-
-  if (s.buf == NULL ||
-      s.radius != radius)
+  else /* creating new path */
     {
-      if (s.buf != NULL)
-        g_free (s.buf);
-      /* allocate a little bit more, just in case due to rounding errors and
-       * such */
-      s.buf = g_malloc (4*4* (roi.width + 2 ) * (roi.height + 2));
-      s.radius = radius;
-      s.valid = TRUE;
+      /* the +3 is padding, +1 was excpected to be sufficient */
+      head =
+        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
+      head->d.type = type;
+      iter=head;
     }
-  g_assert (s.buf);
-
-  gegl_buffer_get_unlocked (buffer, 1.0, &roi, s.format, s.buf, 0);
-
-  {
-    gint u, v;
-    gint i=0;
+  g_assert (res);
+    *res = iter;
 
-    gfloat radius_squared = radius * radius;
-    gfloat inner_radius_squared = (radius * hardness)*(radius * hardness);
-    gfloat soft_range = radius_squared - inner_radius_squared;
+  return head;
+}
 
-    for (v= roi.y; v < roi.y + roi.height ; v++)
+GeglPathList *
+gegl_path_list_destroy (GeglPathList *path)
+{
+  GeglPathList *iter = path;
+  while (iter)
     {
-      gfloat vy2 = (v-y)*(v-y);
-      for (u= roi.x; u < roi.x + roi.width; u++)
-        {
-          gfloat o = (u-x) * (u-x) + vy2;
-
-          if (o < inner_radius_squared)
-             o = col[3];
-          else if (o < radius_squared)
-            {
-              o = (1.0 - (o-inner_radius_squared) / (soft_range)) * col[3];
-            }
-          else
-            {
-              o=0.0;
-            }
-         if (o!=0.0)
-           {
-             gint c;
-             o = o*opacity;
-             for (c=0;c<4;c++)
-               s.buf[i*4+c] = (s.buf[i*4+c] * (1.0-o) + col[c] * o);
-           }
-         i++;
-        }
+      GeglPathList *next = iter->next;
+      gegl_path_item_free (iter);
+      iter = next;
     }
-  }
-  gegl_buffer_set_unlocked (buffer, &roi, s.format, s.buf, 0);
-  g_free (s.buf);
+  return NULL;
 }
 
-
-void gegl_path_stroke (GeglBuffer *buffer,
-                       const GeglRectangle *clip_rect,
-                       GeglPath *vector,
-                       GeglColor  *color,
-                       gdouble     linewidth,
-                       gdouble     hardness,
-                       gdouble     opacity);
-
-
-void gegl_path_stroke (GeglBuffer *buffer,
-                       const GeglRectangle *clip_rect,
-                       GeglPath *vector,
-                       GeglColor  *color,
-                       gdouble     linewidth,
-                       gdouble     hardness,
-                       gdouble     opacity)
+GeglPathList * gegl_path_list_append (GeglPathList *head,
+                                       ...)
 {
-  GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
-  gfloat traveled_length = 0;
-  gfloat need_to_travel = 0;
-  gfloat x = 0,y = 0;
+  InstructionInfo *info;
   GeglPathList *iter;
-  gdouble       xmin, xmax, ymin, ymax;
-  GeglRectangle extent;
+  gchar type;
+  gint pair_no;
 
-  if (!vector)
-    return;
+  va_list var_args;
+  va_start (var_args, head);
+  type = va_arg (var_args, int); /* we pass in a char, but it is promoted to int by varargs*/
+
+  info = lookup_instruction_info(type);
+  if (!info)
+    g_error ("didn't find [%c]", type);
+
+  head = gegl_path_list_append_item (head, type, &iter, NULL);
 
-  if (!clip_rect)
+  iter->d.type       = type;
+  for (pair_no=0;pair_no<(info->n_items+2)/2;pair_no++)
     {
-      g_print ("using buffer extent\n");
-      clip_rect = gegl_buffer_get_extent (buffer);
+      iter->d.point[pair_no].x = va_arg (var_args, gdouble);
+      iter->d.point[pair_no].y = va_arg (var_args, gdouble);
     }
+  va_end (var_args);
+  return head;
+}
 
-  ensure_flattened (vector);
-
-  iter = priv->flat_path;
-  gegl_path_get_bounds (vector, &xmin, &xmax, &ymin, &ymax);
-  extent.x = floor (xmin);
-  extent.y = floor (ymin);
-  extent.width = ceil (xmax) - extent.x;
-  extent.height = ceil (ymax) - extent.y;
+/**
+ * gegl_path_list_flatten: (skip)
+ */
+static GeglPathList *
+gegl_path_list_flatten (GeglMatrix3  *matrix,
+                        GeglPathList *original)
+{
+  GeglPathList *iter;
+  GeglPathList *self = NULL;
 
-  if (!gegl_rectangle_intersect (&extent, &extent, clip_rect))
-   {
-     return;
-   }
-  if (gegl_buffer_is_shared (buffer))
-    while (!gegl_buffer_try_lock (buffer));
+  GeglPathList *endp = NULL;
 
-  /*gegl_buffer_clear (buffer, &extent);*/
+  if (!original)
+    return NULL;
 
-  while (iter)
+  for (iter=original; iter; iter=iter->next)
     {
-      /*fprintf (stderr, "%c, %i %i\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
-      switch (iter->d.type)
-        {
-          case 'M':
-            x = iter->d.point[0].x;
-            y = iter->d.point[0].y;
-            need_to_travel = 0;
-            traveled_length = 0;
-            break;
-          case 'L':
-            {
-              Point a,b;
-
-              gfloat spacing;
-              gfloat local_pos;
-              gfloat distance;
-              gfloat offset;
-              gfloat leftover;
-              gfloat radius = linewidth / 2.0;
+      InstructionInfo *info = lookup_instruction_info (iter->d.type);
+      if(info)
+        self = info->flatten (matrix, self, endp, iter);
+      if (!endp)
+        endp = self;
+      while (endp && endp->next)
+        endp=endp->next;
+    }
+  return self;
+}
 
 
-              a.x = x;
-              a.y = y;
 
-              b.x = iter->d.point[0].x;
-              b.y = iter->d.point[0].y;
+/***** Point *****/
+/* linear interpolation between two points */
+static void
+lerp (Point  *dest,
+      Point  *a,
+      Point  *b,
+      gfloat  t)
+{
+  dest->x = a->x + (b->x-a->x) * t;
+  dest->y = a->y + (b->y-a->y) * t;
+}
 
-              spacing = 0.2 * radius;
+static gdouble
+point_dist (Point *a,
+            Point *b)
+{
+  return sqrt ((a->x-b->x)*(a->x-b->x) +
+               (a->y-b->y)*(a->y-b->y));
+}
 
-              distance = point_dist (&a, &b);
+/* --------------------------------------------------------------------------
+ * A GParamSpec class to describe behavior of GeglPath as an object property
+ * follows.
+ * --------------------------------------------------------------------------
+ */
 
-              leftover = need_to_travel - traveled_length;
-              offset = spacing - leftover;
+#define GEGL_PARAM_PATH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_PARAM_PATH, GeglParamPath))
+#define GEGL_IS_PARAM_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_PARAM_PATH))
 
-              local_pos = offset;
+typedef struct _GeglParamPath GeglParamPath;
 
-              if (distance > 0)
-                for (;
-                     local_pos <= distance;
-                     local_pos += spacing)
-                  {
-                    Point spot;
-                    gfloat ratio = local_pos / distance;
-                    gfloat radius = linewidth/2;
+struct _GeglParamPath
+{
+  GParamSpec parent_instance;
+};
 
-                    lerp (&spot, &a, &b, ratio);
+static void
+gegl_param_vector_init (GParamSpec *self)
+{
+}
 
-                    gegl_path_stamp (buffer, clip_rect,
-                      spot.x, spot.y, radius, hardness, color, opacity);
+static void
+gegl_param_vector_finalize (GParamSpec *self)
+{
+  GParamSpecClass *parent_class = g_type_class_peek (g_type_parent (
+                                                       GEGL_TYPE_PARAM_PATH));
 
-                    traveled_length += spacing;
-                  }
+  parent_class->finalize (self);
+}
 
-              need_to_travel += distance;
+GType
+gegl_param_path_get_type (void)
+{
+  static GType param_vector_type = 0;
 
-              x = b.x;
-              y = b.y;
-            }
+  if (G_UNLIKELY (param_vector_type == 0))
+    {
+      static GParamSpecTypeInfo param_vector_type_info = {
+        sizeof (GeglParamPath),
+        0,
+        gegl_param_vector_init,
+        0,
+        gegl_param_vector_finalize,
+        NULL,
+        NULL,
+        NULL
+      };
+      param_vector_type_info.value_type = GEGL_TYPE_PATH;
 
-            break;
-          case 'u':
-            g_error ("stroking uninitialized path\n");
-            break;
-          case 's':
-            break;
-          default:
-            g_error ("can't stroke for instruction: %i\n", iter->d.type);
-            break;
-        }
-      iter=iter->next;
+      param_vector_type = g_param_type_register_static ("GeglParamPath",
+                                                       &param_vector_type_info);
     }
 
-  if (gegl_buffer_is_shared (buffer))
-    gegl_buffer_unlock (buffer);
+  return param_vector_type;
 }
 
-gint gegl_path_type_get_n_items (gchar type)
+GParamSpec *
+gegl_param_spec_path   (const gchar *name,
+                        const gchar *nick,
+                        const gchar *blurb,
+                        GeglPath  *default_vector,
+                        GParamFlags  flags)
 {
-  InstructionInfo *info = lookup_instruction_info (type);
-  if (!info)
-    return -1;
-  return info->n_items;
+  GeglParamPath *param_vector;
+
+  param_vector = g_param_spec_internal (GEGL_TYPE_PARAM_PATH,
+                                       name, nick, blurb, flags);
+
+  return G_PARAM_SPEC (param_vector);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]