diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 20c57e5a8065d86696807c049977c8ca5a4bd0ac..72ef3f14fe9152ea9903d99e22896add48d6ac01 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -1581,7 +1581,7 @@ void *canvas_getblock(t_class *blockclass, t_canvas **canvasp)
     /* redraw all "scalars" (do this if a drawing command is changed.) 
     LATER we'll use the "template" information to select which ones we
     redraw.   Action = 0 for redraw, 1 for draw only, 2 for erase. */
-static void glist_redrawall(t_glist *gl, int action)
+static void glist_redrawall(t_template *template, t_glist *gl, int action)
 {
 	//fprintf(stderr,"glist_redrawall\n");
     t_gobj *g;
@@ -1589,7 +1589,8 @@ static void glist_redrawall(t_glist *gl, int action)
     for (g = gl->gl_list; g; g = g->g_next)
     {
         t_class *cl;
-        if (vis && g->g_pd == scalar_class)
+        if (vis && g->g_pd == scalar_class &&
+            template == template_findbyname(((t_scalar *)g)->sc_template))
         {
             if (action == 1)
             {
@@ -1604,7 +1605,7 @@ static void glist_redrawall(t_glist *gl, int action)
             else scalar_redraw((t_scalar *)g, gl);
         }
         else if (g->g_pd == canvas_class)
-            glist_redrawall((t_glist *)g, action);
+            glist_redrawall(template, (t_glist *)g, action);
     }
 	if (glist_isselected(glist_getcanvas(gl), (t_gobj *)gl)) {
 		sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n", glist_getcanvas(gl), gl, 1);
@@ -1617,7 +1618,7 @@ void canvas_redrawallfortemplate(t_template *template, int action)
     t_canvas *x;
         /* find all root canvases */
     for (x = canvas_list; x; x = x->gl_next)
-        glist_redrawall(x, action);
+        glist_redrawall(template, x, action);
 }
 
     /* find the template defined by a canvas, and redraw all elements
@@ -1639,7 +1640,7 @@ void canvas_redrawallfortemplatecanvas(t_canvas *x, int action)
         if (argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL
             || argv[0].a_w.w_symbol != s1)
                 continue;
-        tmpl = template_findbyname(argv[1].a_w.w_symbol);
+        tmpl = template_findbyname(canvas_makebindsym(argv[1].a_w.w_symbol));
         canvas_redrawallfortemplate(tmpl, action);
     }
     canvas_redrawallfortemplate(0, action);
diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c
index 4d98c14d5c3e186ef9ab16545a492b5f7626ee0d..4585ba2a54f09819d7a286fb786a63d2452e842d 100644
--- a/pd/src/g_graph.c
+++ b/pd/src/g_graph.c
@@ -113,7 +113,7 @@ void glist_delete(t_glist *x, t_gobj *y)
 		//fprintf(stderr,"glist_delete YES\n");
 		t_gobj *g;
 		t_object *ob;
-                t_template *tmpl;
+        t_template *tmpl;
 		t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp"));
 		t_canvas *canvas = glist_getcanvas(x);
 		int drawcommand = class_isdrawcommand(y->g_pd);
@@ -162,10 +162,10 @@ void glist_delete(t_glist *x, t_gobj *y)
                        belong to our template, before deleting
 		       it; we'll redraw them once it's deleted below. */
 		if (drawcommand)
-                {
-                    tmpl = template_findbydrawcommand(y);
-                    canvas_redrawallfortemplate(tmpl, 2);
-                }
+        {
+            tmpl = template_findbydrawcommand(y);
+            canvas_redrawallfortemplate(tmpl, 2);
+        }
 		if (glist_isvisible(canvas))
 		    gobj_vis(y, x, 0);
 		if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) {
@@ -194,7 +194,7 @@ void glist_delete(t_glist *x, t_gobj *y)
 		pd_free(&y->g_pd);
 		if (chkdsp) canvas_update_dsp();
 		if (drawcommand)
-                    canvas_redrawallfortemplate(tmpl, 1);
+            canvas_redrawallfortemplate(tmpl, 1);
 		canvas_setdeleting(canvas, wasdeleting);
 		x->gl_valid = ++glist_valid;
 		if (late_rtext_free) {
@@ -1200,16 +1200,16 @@ static void graph_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
     {
 		// first check for legacy objects that don't offer displacefnwtag and fallback on the old way of doing things
 		t_gobj *g;
-                /* special case for scalars, which have a group for
-                   the transform matrix */
-                for (g = x->gl_list; g; g = g->g_next)
-                {
-                    if (pd_class((t_pd *)g) == scalar_class &&
-                        g->g_pd->c_wb->w_displacefnwtag != NULL)
-                    {
-                        (*(g->g_pd->c_wb->w_displacefnwtag))(g, glist, dx, dy);
-                    }
-                }
+        /* special case for scalars, which have a group for
+           the transform matrix */
+        for (g = x->gl_list; g; g = g->g_next)
+        {
+            if (pd_class((t_pd *)g) == scalar_class &&
+                g->g_pd->c_wb->w_displacefnwtag != NULL)
+            {
+                (*(g->g_pd->c_wb->w_displacefnwtag))(g, glist, dx, dy);
+            }
+        }
 		for (g = x->gl_list; g; g = g->g_next) {
 			//fprintf(stderr,"shouldvis %d %d\n", gobj_shouldvis(g, glist), gobj_shouldvis(g, x));
 			if (g && gobj_shouldvis(g, x) && g->g_pd->c_wb->w_displacefnwtag == NULL && pd_class((t_pd *)g) != garray_class) {
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index b5057fe620c4a6b0730fd2baea2c78f623366cda..618ac3e24be9faa131b4ccb8a2b7c8e400f1b97c 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -154,6 +154,11 @@ void scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey)
 
 extern int array_joc;
 
+extern void template_notifyforscalar(t_template *template, t_glist *owner,
+    t_scalar *sc, t_symbol *s, int argc, t_atom *argv);
+
+static int sc_isentered = 0;
+static t_scalar *sc_entered = 0;
 static void scalar_getrect(t_gobj *z, t_glist *owner,
     int *xp1, int *yp1, int *xp2, int *yp2)
 {
@@ -165,6 +170,27 @@ static void scalar_getrect(t_gobj *z, t_glist *owner,
     t_gobj *y;
     t_float basex, basey;
 
+    /* hack for enter/leave struct notifications */
+    if (sc_entered == x)
+    {
+        if (sc_isentered == 1)
+        {
+            /* change value to see if there's a call
+               to scalar_click the next time around. Of
+               course it will be too late so we'll be
+               one pixel off, but it's better than nothing.
+            */
+            sc_isentered = -1;
+        }
+        else if (sc_isentered == -1)
+        {
+            t_atom at[1];
+            template_notifyforscalar(template, owner, x, gensym("leave"), 1, at);
+            sc_entered = 0;
+            sc_isentered = 0;
+        }
+    }
+
     // EXPERIMENTAL: we assume that entire canvas is withing the rectangle--this is for arrays
     // with "jump on click" enabled TODO: test for other regressions (there shouuld not be any
     // provided the global variable array_joc is properly maintained)
@@ -210,7 +236,7 @@ static void scalar_getrect(t_gobj *z, t_glist *owner,
     *yp2 = y2; 
 }
 
-static void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
+void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
 {
     if (state)
     {
@@ -231,7 +257,27 @@ static void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)
     }
 }
 
-static void scalar_select(t_gobj *z, t_glist *owner, int state)
+/* This is a workaround.  Since scalars are contained within a tkpath
+   group, and since tkpath groups don't have coords, we have to fudge
+   things with regard to Pd-l2ork's normal *_displace_withtag functionality.
+   (Additionally, we can't just fall back to the old displace method
+   because it too assumes the canvas item has an xy coord.)
+
+   We draw the selection rect but don't add the scalar to the
+   "selected" tag.  This means when Pd-l2ork issues the canvas "move"
+   command, our scalar doesn't go anywhere.  Instead we get the callback
+   to scalar_displace_withtag and do another workaround to get the
+   new position and feed it to the scalar's matrix.
+
+   This creates a problem with gop canvases, and yet _another_ partial
+   workaround.  We need the global variable below to let the so that
+   we can figure out whether the displace call is coming from the scalar
+   itself or from a parent graph.  This means scalars won't currently
+   respond properly in nested gops.  (I think that requires a window
+   item on the canvas to replace the current gop rect.)
+*/
+t_glist *select_owner = 0; /* kludge variable used by displace_withtag fn */
+void scalar_select(t_gobj *z, t_glist *owner, int state)
 {
 	//fprintf(stderr,"scalar_select %d\n", state);
     t_scalar *x = (t_scalar *)z;
@@ -250,7 +296,8 @@ static void scalar_select(t_gobj *z, t_glist *owner, int state)
 	}
     gpointer_unset(&gp);
 	if (state) {
-		sys_vgui(".x%lx.c addtag selected withtag scalar%lx\n",
+                select_owner = owner;
+		sys_vgui(".x%lx.c addtag selected withtag blankscalar%lx\n",
 			glist_getcanvas(owner), x);
 		/* how do we navigate through a t_word list?
         if (x->sc_vec) {
@@ -270,8 +317,11 @@ static void scalar_select(t_gobj *z, t_glist *owner, int state)
 				glist_getcanvas(owner), (t_int)tag);
 		}*/
 	} else {
-		sys_vgui(".x%lx.c dtag scalar%lx selected\n",
+        select_owner = 0;
+		sys_vgui(".x%lx.c dtag blankscalar%lx selected\n",
 			glist_getcanvas(owner), x);
+                sys_vgui(".x%lx.c dtag .x%lx.x%lx.template%lx selected\n",
+                    glist_getcanvas(owner), glist_getcanvas(owner), owner, x->sc_vec);
         /* how do we navigate through a t_word list?
         if (x->sc_vec) {
             t_word *v = x->sc_vec;
@@ -304,16 +354,17 @@ static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
     t_atom at[3];
     t_gpointer gp;
     int xonset, yonset, xtype, ytype, gotx, goty;
+    t_float basex = 0, basey = 0;
     if (!template)
     {
         error("scalar: couldn't find template %s", templatesym->s_name);
         return;
     }
     gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
-    if (gotx && (xtype != DT_FLOAT))
+    if ((gotx && (xtype != DT_FLOAT)) || select_owner != glist)
         gotx = 0;
     goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
-    if (goty && (ytype != DT_FLOAT))
+    if ((goty && (ytype != DT_FLOAT)) || select_owner != glist)
         goty = 0;
     if (gotx)
         *(t_float *)(((char *)(x->sc_vec)) + xonset) +=
@@ -330,6 +381,13 @@ static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
     scalar_redraw(x, glist);
 }
 
+/* Very complicated at the moment. If a scalar is in a gop canvas, then
+   we don't need to update its x/y fields (if it even has them) when displacing
+   it.  Otherwise we do.  The global selected_owner variable is used to store
+   the "owner" canvas-- if it matches the glist parameter below then we know
+   the scalar is directly selected.  If not it's in a gop canvas.  (This doesn't
+   yet handle nested GOPs, unfortunately.)
+*/
 static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
 {
 	//fprintf(stderr,"scalar_displace_withtag %lx %d %d\n", (t_int)z, dx, dy);
@@ -340,16 +398,17 @@ static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
     t_atom at[3];
     t_gpointer gp;
     int xonset, yonset, xtype, ytype, gotx, goty;
+    t_float basex = 0, basey = 0;
     if (!template)
     {
         error("scalar: couldn't find template %s", templatesym->s_name);
         return;
     }
     gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz);
-    if (gotx && (xtype != DT_FLOAT))
+    if ((gotx && (xtype != DT_FLOAT)) || select_owner != glist)
         gotx = 0;
     goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz);
-    if (goty && (ytype != DT_FLOAT))
+    if ((goty && (ytype != DT_FLOAT)) || select_owner != glist)
         goty = 0;
     if (gotx)
         *(t_float *)(((char *)(x->sc_vec)) + xonset) +=
@@ -357,12 +416,20 @@ static void scalar_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
     if (goty)
         *(t_float *)(((char *)(x->sc_vec)) + yonset) +=
             dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0));
+    scalar_getbasexy(x, &basex, &basey);
     gpointer_init(&gp);
     gpointer_setglist(&gp, glist, x);
     SETPOINTER(&at[0], &gp);
     SETFLOAT(&at[1], (t_float)dx);
     SETFLOAT(&at[2], (t_float)dy);
     template_notify(template, gensym("displace"), 2, at);
+
+    t_float xscale = glist_xtopixels(glist, 1) - glist_xtopixels(glist, 0);
+    t_float yscale = glist_ytopixels(glist, 1) - glist_ytopixels(glist, 0);
+
+    sys_vgui(".x%lx.c itemconfigure {.scalar%lx} -matrix { {%g %g} {%g %g} {%d %d} }\n",
+        glist_getcanvas(glist), x->sc_vec, 1.0, 0.0, 0.0, 1.0, (int)glist_xtopixels(select_owner, basex) + (select_owner == glist ? 0 : dx), (int)glist_ytopixels(select_owner, basey) + (select_owner == glist ? 0 : dy));
+
     //scalar_redraw(x, glist);
 }
 
@@ -377,6 +444,25 @@ static void scalar_delete(t_gobj *z, t_glist *glist)
     /* nothing to do */
 }
 
+/* At preset, scalars have a three-level hierarchy in tkpath,
+   with two levels accessible from within Pd:
+   scalar - tkpath group with a matrix based on x/y fields,
+     |      gop basexy, and gop/parent canvas scaling values.
+     |      This group is not configurable by the user.  This
+     |      way [draw group] doesn't need extra code to include
+     |      basexy and gop settings.
+     v
+   dgroup - user-facing group that is parent to all the scalar's
+     |      drawing instructions.  Its matrix and options can be
+     |      accessed from [draw group]
+     v
+   draw   - various drawing instructions (rectangles, paths, etc.).
+            Each has its own matrix and options that can be
+            accessed from the corresponding [draw] instruction.
+
+   The tag "blankscalar" is for scalars that don't have a visual
+   representation, but maybe this can just be merged with "scalar"
+*/
 static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
 {
 	//fprintf(stderr,"scalar_vis %d\n", vis);
@@ -393,13 +479,28 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         {
             int x1 = glist_xtopixels(owner, basex);
             int y1 = glist_ytopixels(owner, basey);
-            sys_vgui(".x%lx.c create prect %d %d %d %d -tags {scalar%lx}\n",
+            sys_vgui(".x%lx.c create prect %d %d %d %d -tags {blankscalar%lx}\n",
                 glist_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, x);
         }
-        else sys_vgui(".x%lx.c delete scalar%lx\n", glist_getcanvas(owner), x);
+        else sys_vgui(".x%lx.c delete blankscalar%lx\n", glist_getcanvas(owner), x);
         return;
     }
-	//else sys_vgui(".x%lx.c delete scalar%lx\n", glist_getcanvas(owner), x);
+	//else sys_vgui(".x%lx.c delete blankscalar%lx\n", glist_getcanvas(owner), x);
+
+    if (vis)
+    {
+        t_float xscale = glist_xtopixels(owner, 1) - glist_xtopixels(owner, 0);
+        t_float yscale = glist_ytopixels(owner, 1) - glist_ytopixels(owner, 0);
+        /* we could use the tag .template%lx for easy access from
+           the draw_class, but that's not necessary at this point */
+        sys_vgui(".x%lx.c create group -tags {.scalar%lx} "
+            "-matrix { {%g %g} {%g %g} {%d %d} }\n",
+            glist_getcanvas(owner), x->sc_vec,
+            1.0, 0.0, 0.0, 1.0, (int)glist_xtopixels(owner, basex), (int)glist_ytopixels(owner, basey)
+            );
+        sys_vgui(".x%lx.c create group -tags {.dgroup%lx} -parent {.scalar%lx}\n",
+            glist_getcanvas(owner), x->sc_vec, x->sc_vec);
+    }
 
     for (y = templatecanvas->gl_list; y; y = y->g_next)
     {
@@ -407,6 +508,9 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         if (!wb) continue;
         (*wb->w_parentvisfn)(y, owner, x, x->sc_vec, template, basex, basey, vis);
     }
+    if (!vis)
+        sys_vgui(".x%lx.c delete .scalar%lx\n", glist_getcanvas(owner), x->sc_vec);
+
     sys_unqueuegui(x);
     if (glist_isselected(owner, &x->sc_gobj))
     {
@@ -433,9 +537,6 @@ void scalar_redraw(t_scalar *x, t_glist *glist)
         //sys_queuegui(x, glist, scalar_doredraw);
 }
 
-extern void template_notifyforscalar(t_template *template, t_glist *owner,
-    t_scalar *sc, t_symbol *s, int argc, t_atom *argv);
-
 int scalar_doclick(t_word *data, t_template *template, t_scalar *sc,
     t_array *ap, struct _glist *owner,
     t_float xloc, t_float yloc, int xpix, int ypix,
@@ -485,6 +586,17 @@ static int scalar_click(t_gobj *z, struct _glist *owner,
 	//fprintf(stderr,"scalar_click %d %d\n", xpix, ypix);
     t_scalar *x = (t_scalar *)z;
     t_template *template = template_findbyname(x->sc_template);
+
+    /* hack for enter/leave notifications */
+    if (sc_isentered == 0)
+    {
+        t_atom at[1];
+        template_notifyforscalar(template, owner, x, gensym("enter"), 1, at);
+        sc_isentered = 1;
+        sc_entered = x;
+    }
+    else sc_isentered = 1;
+    
     return (scalar_doclick(x->sc_vec, template, x, 0,
         owner, 0, 0, xpix, ypix, shift, alt, dbl, doit));
 }
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index b0d688b63ed8c175f2bbf176112e04436d96e59e..c9fde89d6938dd98690ce9de98afffe41304726c 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -5,6 +5,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <math.h>  /* for matrix transforms */
 
 #include "m_pd.h"
 #include "s_stuff.h"    /* for sys_hostfontsize */
@@ -951,6 +952,2216 @@ void fielddesc_setcoord(t_fielddesc *f, t_template *template,
     }
 }
 
+/* ---------------- draw svg shapes and paths ---------------- */
+
+/*
+draws belong to templates and describe how the data in the template are to
+be drawn.  The coordinates of the draw (and other display features) can
+be attached to fields in the template.
+
+todo: draw_click doesn't work with paths yet
+todo: some better way than drawcurve for defining click widgetbehaviors (just
+      checking for field variables and moving joints is too simplistic)
+todo: make pathgetrect
+*/
+
+t_class *draw_class;
+
+typedef struct _draw
+{
+    t_object x_obj;
+    int x_flags;            /* CLOSED and/or BEZ and/or NOMOUSE */
+    t_symbol *x_drawtype;
+    t_symbol *x_fill;
+    t_fielddesc x_fill_r;
+    t_fielddesc x_fill_g;
+    t_fielddesc x_fill_b;
+    t_fielddesc x_fillopacity;
+    t_fielddesc x_fillrule;
+    t_symbol *x_stroke;
+    t_fielddesc x_stroke_r;
+    t_fielddesc x_stroke_g;
+    t_fielddesc x_stroke_b;
+    int x_ndash;
+    t_fielddesc *x_strokedasharray; /* array of lengths */
+    t_fielddesc x_strokelinecap;
+    t_fielddesc x_strokelinejoin;
+    t_fielddesc x_strokemiterlimit;
+    t_fielddesc x_strokeopacity;
+    t_fielddesc x_strokewidth;
+    t_fielddesc x_rx; /* for rounded rectangles */
+    t_fielddesc x_ry;
+    int x_transform_n;
+    t_fielddesc *x_transform;
+    t_fielddesc x_width;
+    t_fielddesc x_vis;
+    int x_pathrect_cache; /* 0 to recalc on next draw_getrect call
+                             1 for cached
+                            -1 to turn off caching */
+    int x_x1; /* use these for caching path bbox */
+    int x_y1;
+    int x_x2;
+    int x_y2;
+    int x_nargs;
+    t_fielddesc *x_vec;
+    int *x_nargs_per_cmd;      /* points per each path command */
+    int x_npathcmds;
+    char *x_pathcmds; /* for path commands */
+    t_canvas *x_canvas;
+} t_draw;
+
+static int is_svgpath_cmd(t_symbol *s)
+{
+    /* 1 for absolute cmd, 2 for relative */
+    if (s == gensym("M") || s == gensym("Z") || s == gensym("L") ||
+        s == gensym("H") || s == gensym("V") || s == gensym("C") ||
+        s == gensym("S") || s == gensym("Q") || s == gensym("T") ||
+        s == gensym("A"))
+        return 1;
+    else if (s == gensym("m") || s == gensym("z") || s == gensym("l") ||
+        s == gensym("h") || s == gensym("v") || s == gensym("c") ||
+        s == gensym("s") || s == gensym("q") || s == gensym("t") ||
+        s == gensym("a"))
+        return 2;
+    else return 0;
+}
+
+static int path_ncmds(int argc, t_atom *argv)
+{
+    int i, j = 0;
+    for(i = 0; i < argc; i++)
+    {
+        if (argv[i].a_type == A_SYMBOL && is_svgpath_cmd(atom_getsymbol(argv+i)))
+            j++;
+    }
+    return j;
+}
+
+t_draw *draw_getgroup(t_draw *x)
+{
+    t_gobj *g;
+    t_draw *dgroup = 0;
+    t_template *templ;
+    t_symbol *s1 = gensym("draw");
+    t_symbol *s2 = gensym("group");
+    for(g = x->x_canvas->gl_list; g; g = g->g_next)
+    {
+        t_object *ob = pd_checkobject(&g->g_pd);
+        t_atom *argv;
+        if (!ob || ob->te_type != T_OBJECT ||
+            binbuf_getnatom(ob->te_binbuf) < 2)
+            continue;
+        argv = binbuf_getvec(ob->te_binbuf);
+        if (argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL
+            || argv[0].a_w.w_symbol != s1 || argv[1].a_w.w_symbol != s2)
+            continue;
+        dgroup = (t_draw *)g;
+        break;
+    }
+    return (dgroup);
+}
+
+static void *draw_new(t_symbol *classsym, t_int argc, t_atom *argv)
+{
+    t_draw *x = (t_draw *)pd_new(draw_class);
+   /* not sure about classname here... */
+    if (argc && argv->a_type == A_SYMBOL)
+        x->x_drawtype = atom_getsymbolarg(0, argc--, argv++);
+    else
+    {
+        pd_error(x, "draw: need an svg shape (rect, circle, path, etc.)");
+    }
+    int flags = 0;
+    int nxy, i;
+    t_fielddesc *fd;
+    x->x_canvas = canvas_getcurrent();
+    fielddesc_setfloat_const(&x->x_vis, 1);
+    while (1)
+    {
+        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
+        if (!strcmp(firstarg->s_name, "-v") && argc > 1)
+        {
+            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);
+            argc -= 2; argv += 2;
+        }
+        else if (!strcmp(firstarg->s_name, "-x"))
+        {
+            flags |= NOMOUSE;
+            argc -= 1; argv += 1;
+        }
+        else break;
+    }
+    x->x_flags = flags;
+    if (argc < 0) argc = 0;
+    if (x->x_drawtype == gensym("path"))
+    {
+        int ncmds = x->x_npathcmds = path_ncmds(argc, argv);
+        x->x_pathcmds = (char *)t_getbytes(ncmds * sizeof(char));
+        x->x_nargs_per_cmd = (int *)t_getbytes(ncmds * sizeof(int));
+        for (i = 0; i < ncmds; i++) x->x_nargs_per_cmd[i] = 0;
+        nxy = x->x_nargs = argc - ncmds;
+    }
+    else if (x->x_drawtype == gensym("group"))
+    {
+        nxy = 0;
+        x->x_nargs = 0;
+        x->x_vec = 0;
+        t_draw *dgroup = draw_getgroup(x);
+        if (dgroup)
+        {
+            pd_error(x, "draw group: only one group per canvas allowed");
+            return (0);
+        }
+    }
+    else /* all other shapes */
+    {
+        nxy =  (argc + (argc & 1));
+        x->x_nargs = nxy;
+    }
+    x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc));
+
+    if (argc && x->x_drawtype == gensym("path"))
+    {
+        if (argv->a_type != A_SYMBOL ||
+            atom_getsymbol(argv) != gensym("M"))
+        {
+            pd_error(x, "draw path: path data must start "
+                        "with a moveto command (M)");
+            return 0;
+        }
+    }
+
+    int cmdn = -1; /* hack */
+    for (i = 0, fd = x->x_vec; i < argc; i++, argv++)
+    {
+        if (x->x_drawtype == gensym("path") &&
+            argv->a_type == A_SYMBOL &&
+            is_svgpath_cmd(atom_getsymbol(argv)))
+        {
+                x->x_pathcmds[++cmdn] = *(atom_getsymbol(argv)->s_name);
+        }
+        else
+        {
+            fielddesc_setfloatarg(fd++, 1, argv);
+            /* post("got a coord"); */
+            if (x->x_drawtype == gensym("path"))
+            {
+                (x->x_nargs_per_cmd[cmdn])++;
+                /* if we get a field variable, just
+                   turn off the get_rect caching */
+                if (argv->a_type == A_SYMBOL)
+                    x->x_pathrect_cache = -1;
+            }
+        }
+    }
+    if (argc & 1 && x->x_drawtype != gensym("path")) fielddesc_setfloat_const(fd, 0);
+    x->x_fill = gensym("\"\"");
+    fielddesc_setfloat_const(&x->x_fillopacity, 1);
+    fielddesc_setfloat_const(&x->x_fillrule, 0);
+    x->x_stroke = gensym("black");
+    fielddesc_setfloat_const(&x->x_strokelinecap, 0);
+    fielddesc_setfloat_const(&x->x_strokelinejoin, 0);
+    fielddesc_setfloat_const(&x->x_strokemiterlimit, 0);
+    fielddesc_setfloat_const(&x->x_strokeopacity, 1);
+    fielddesc_setfloat_const(&x->x_strokewidth, 1);
+    x->x_x1 = 0;
+    x->x_x2 = 0;
+    x->x_y1 = 0;
+    x->x_y2 = 0;
+
+    x->x_ndash = 0;
+    x->x_strokedasharray = (t_fielddesc *)t_getbytes(1 * sizeof(t_fielddesc));
+    x->x_transform_n = 0;
+    x->x_transform = (t_fielddesc *)t_getbytes(1 * sizeof(t_fielddesc));
+
+    char buf[50];
+    sprintf(buf, ".x%lx", (long unsigned int)x);
+
+    pd_bind(&x->x_obj.ob_pd, gensym(buf));
+    
+    return (x);
+}
+
+void draw_float(t_draw *x, t_floatarg f)
+{
+    int viswas;
+    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)
+    {
+        pd_error(x, "global vis/invis for a template with variable visibility");
+        return;
+    }
+    viswas = (x->x_vis.fd_un.fd_float != 0);
+
+    if ((f != 0 && viswas) || (f == 0 && !viswas))
+        return;
+    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);
+    fielddesc_setfloat_const(&x->x_vis, (f != 0));
+    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);
+}
+
+static char *rgb_to_hex(int r, int g, int b)
+{
+    static char hexc[10];
+    int r1 = r < 0 ? 0 : r;
+    if (r1 > 255) r1 = 255;
+    int g1 = g < 0 ? 0 : g;
+    if (g1 > 255) g1 = 255;
+    int b1 = b < 0 ? 0 : b;
+    if (b1 > 255) b1 = 255;
+    sprintf(hexc, "#%.6x", (r1 << 16) + (g1 << 8) + b1);
+    return hexc;
+}
+
+char *get_strokelinecap(int a)
+{
+    static char strokelinecap[8];
+    if (a == 0) sprintf(strokelinecap, "butt");
+    else if (a == 1) sprintf(strokelinecap, "round");
+    else if (a == 2) sprintf(strokelinecap, "square");
+    else sprintf(strokelinecap, "butt");
+    return (strokelinecap);
+}
+
+char *get_strokelinejoin(int a)
+{
+    static char strokelinejoin[8];
+    if (a == 0) sprintf(strokelinejoin, "miter");
+    else if (a == 1) sprintf(strokelinejoin, "round");
+    else if (a == 2) sprintf(strokelinejoin, "bevel");
+    else sprintf(strokelinejoin, "miter");
+    return (strokelinejoin);
+}
+
+void draw_doupdate(t_draw *x, t_canvas *c, t_symbol *s)
+{
+    t_gobj *g;
+    t_template *template;
+    int isgroup = (x->x_drawtype == gensym("group"));
+    for (g = c->gl_list; g; g = g->g_next)
+    {
+        if (glist_isvisible(c) && g->g_pd == scalar_class &&
+            x->x_canvas ==
+            template_findcanvas(template = (template_findbyname(
+                (((t_scalar *)g)->sc_template))))
+           )
+        {
+            t_word *data = ((t_scalar *)g)->sc_vec;
+            char str[MAXPDSTRING];
+            t_canvas *visible = c;
+            while(visible->gl_isgraph && visible->gl_owner)
+                visible = visible->gl_owner;
+            if (s == gensym("fill"))
+            {
+                char *fill;
+                if (x->x_fill)
+                    fill = x->x_fill->s_name;
+                else
+                {
+                    fill = rgb_to_hex(
+                        (int)fielddesc_getfloat(&x->x_fill_r,
+                            template, data, 1),
+                        (int)fielddesc_getfloat(&x->x_fill_g,
+                            template, data, 1),
+                        (int)fielddesc_getfloat(&x->x_fill_b,
+                            template, data, 1));
+                }
+                sprintf(str, "-fill %s", fill);
+            }
+            else if (s == gensym("stroke"))
+            {
+                char *stroke;
+                if (x->x_stroke)
+                    stroke = x->x_stroke->s_name;
+                else
+                {
+                    stroke = rgb_to_hex(
+                        (int)fielddesc_getfloat(&x->x_stroke_r,
+                            template, data, 1),
+                        (int)fielddesc_getfloat(&x->x_stroke_g,
+                            template, data, 1),
+                        (int)fielddesc_getfloat(&x->x_stroke_b,
+                            template, data, 1));
+                }
+                sprintf(str, "-stroke %s", stroke);
+            }
+            else if (s == gensym("fill-opacity"))
+                sprintf(str, "-fillopacity %g",
+                    fielddesc_getcoord(&x->x_fillopacity, template, data, 1));
+            else if (s == gensym("fill-rule"))
+                sprintf(str, "-fillrule %s", (int)fielddesc_getcoord(
+                    &x->x_fillrule, template, data, 1) ?
+                       "evenodd" : "nonzero");
+            else if (s == gensym("stroke-linecap"))
+                sprintf(str, "-strokelinecap %s", get_strokelinecap(
+                    (int)fielddesc_getcoord(&x->x_strokelinecap,
+                        template, data, 1)));
+            else if (s == gensym("stroke-linejoin"))
+                sprintf(str, "-strokelinejoin %s", get_strokelinejoin(
+                    (int)fielddesc_getcoord(&x->x_strokelinejoin,
+                        template, data, 1)));
+            else if (s == gensym("stroke-miterlimit"))
+                sprintf(str, "-strokemiterlimit %g", fielddesc_getcoord(
+                    &x->x_strokemiterlimit, template, data, 1));
+            else if (s == gensym("stroke-opacity"))
+                sprintf(str, "-strokeopacity %g", fielddesc_getcoord(
+                    &x->x_strokeopacity, template, data, 1));
+            else if (s == gensym("stroke-width"))
+                sprintf(str, "-strokewidth %g", fielddesc_getcoord(
+                    &x->x_strokewidth, template, data, 1));
+            else if (s == gensym("rx"))
+                sprintf(str, "-rx %g", fielddesc_getcoord(
+                    &x->x_rx, template, data, 1));
+            else if (s == gensym("ry"))
+                sprintf(str, "-ry %g", fielddesc_getcoord(
+                    &x->x_ry, template, data, 1));
+            else if (s == gensym("stroke-dasharray"))
+            {
+                if (x->x_ndash)
+                {
+                    t_fielddesc *fd = x->x_strokedasharray;
+                    int i;
+                    char *cur = str, * const end = str + sizeof str;
+                    cur += snprintf(cur, end-cur, "-strokedasharray {");
+                    for (i = 0; i < x->x_ndash; i++)
+                        if (cur < end)
+                            cur += snprintf(cur, end-cur, " %g ",
+                                fielddesc_getcoord(fd+i, template, data, 1));
+                    cur += snprintf(cur, end-cur, "}\n");
+                }
+                else return;
+            }
+            if (x->x_drawtype == gensym("group"))
+            {
+                sys_vgui(".x%lx.c itemconfigure .dgroup%lx %s\n",
+                   visible, data, str);
+            }
+            else
+            {
+                sys_vgui(".x%lx.c itemconfigure .draw%lx.%lx %s\n",
+                   visible, x, data, str);
+            }
+        }
+        if (g->g_pd == canvas_class)
+            draw_doupdate(x, (t_glist *)g, s);
+    }
+}
+
+extern t_canvas *canvas_list;
+void draw_update(t_draw *x, t_symbol *s)
+{
+    t_canvas *c;
+    for (c = canvas_list; c; c = c->gl_next)
+        draw_doupdate(x, c, s);
+}
+
+void draw_fillopacity(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_fillopacity, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_strokeopacity(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_strokeopacity, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_strokedasharray(t_draw *x, t_symbol *s, int argc, t_atom *argv)
+{
+    t_fielddesc *fd;
+    x->x_strokedasharray = (t_fielddesc *)t_resizebytes(x->x_strokedasharray,
+        x->x_ndash * sizeof(*x->x_strokedasharray),
+        argc * sizeof(*x->x_strokedasharray));
+    x->x_ndash = argc;
+    fd = x->x_strokedasharray;
+    while (argc)
+        fielddesc_setfloatarg(fd++, argc--, argv++);
+    draw_update(x, s);
+}
+
+void draw_fill(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argc == 1 && argv->a_type == A_SYMBOL)
+        x->x_fill = atom_getsymbolarg(0, argc, argv);
+    else if (argc > 2)
+    {
+        int i, var = 0;
+        /* if there's a color variable field we have to recalculate
+           it each redraw in draw_vis */
+        for(i = 0; i < argc; i++)
+            var = (argv[i].a_type == A_SYMBOL) ? 1 : var;
+        if (var)
+        {
+            fielddesc_setfloatarg(&x->x_fill_r, argc--, argv++);
+            fielddesc_setfloatarg(&x->x_fill_g, argc--, argv++);
+            fielddesc_setfloatarg(&x->x_fill_b, argc--, argv++);
+            x->x_fill = 0;
+        }
+        else
+        {
+            int r = (int)atom_getfloatarg(0, argc--, argv++);
+            int g = (int)atom_getfloatarg(0, argc--, argv++);
+            int b = (int)atom_getfloatarg(0, argc--, argv++);
+            x->x_fill = gensym(rgb_to_hex(r, g, b));
+        }
+        if (argc && (argv->a_type == A_FLOAT || argv->a_type == A_SYMBOL))
+        {
+            draw_fillopacity(x, s, argc, argv);
+            return;
+        }
+    }
+    draw_update(x, s);
+}
+
+void draw_stroke(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argc == 1 && argv->a_type == A_SYMBOL)
+        x->x_stroke = atom_getsymbolarg(0, argc, argv);
+    else if (argc > 2)
+    {
+        int var = 0, i;
+        for(i = 0; i < argc; i++)
+            var = (argv[i].a_type == A_SYMBOL) ? 1 : var;
+        if (var)
+        {
+            fielddesc_setfloatarg(&x->x_stroke_r, argc--, argv++);
+            fielddesc_setfloatarg(&x->x_stroke_g, argc--, argv++);
+            fielddesc_setfloatarg(&x->x_stroke_b, argc--, argv++);
+            x->x_stroke = 0;
+        }
+        else
+        {
+            /* if no variables, then precompute x_stroke so it doesn't
+               have to happen every call to draw_vis */
+            int r = (int)atom_getfloatarg(0, argc--, argv++);
+            int g = (int)atom_getfloatarg(0, argc--, argv++);
+            int b = (int)atom_getfloatarg(0, argc--, argv++);
+            x->x_stroke = gensym(rgb_to_hex(r, g, b));
+        }
+        if (argc && (argv->a_type == A_FLOAT || argv->a_type == A_SYMBOL))
+        {
+            draw_strokeopacity(x, s, argc, argv);
+            return;
+        }
+    }
+    draw_update(x, s);
+}
+
+void draw_strokelinecap(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_strokelinecap, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_strokelinejoin(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_strokelinejoin, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_strokemiterlimit(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_strokemiterlimit, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_strokewidth(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_strokewidth, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_fillrule(t_draw *x, t_symbol *s, t_int argc, t_atom *argv)
+{
+    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&x->x_fillrule, argc, argv);
+        draw_update(x, s);
+    }
+}
+
+void draw_rx(t_draw *x, t_symbol *s, int argc, t_atom *argv)
+{
+    if (x->x_drawtype != gensym("rect"))
+    {
+        pd_error(x, "draw: %s: no method for 'rx'", x->x_drawtype->s_name);
+        return;
+    }
+    if (!argc || argv->a_type != A_FLOAT)
+    {
+        pd_error(x, "draw: rect: bad arguments for method 'rx'");
+        return;
+    }
+    fielddesc_setfloatarg(&x->x_rx, argc, argv);
+    draw_update(x, s);
+}
+
+void draw_ry(t_draw *x, t_symbol *s, int argc, t_atom *argv)
+{
+    if (x->x_drawtype != gensym("rect"))
+    {
+        pd_error(x, "draw: %s: no method for 'ry'", x->x_drawtype->s_name);
+        return;
+    }
+    if (!argc || argv->a_type != A_FLOAT)
+    {
+        pd_error(x, "draw: rect: bad arguments for method 'ry'");
+        return;
+    }
+    fielddesc_setfloatarg(&x->x_ry, argc, argv);
+    draw_update(x, s);
+}
+
+static int draw_minv(t_float a[][3], t_float b[][3])
+{
+    t_float tmp[3][3], determinant = 0;
+    int i, j;
+    for(i = 0; i < 3; i++)
+        determinant = determinant +
+            (a[0][i]*(a[1][(i+1)%3]*a[2][(i+2)%3] -
+            a[1][(i+2)%3]*a[2][(i+1)%3]));
+    if (!determinant) return 0;
+    for(i = 0; i < 3; i++)
+    {
+      for(j=0;j<3;j++)
+           tmp[j][i] = ((a[(i+1)%3][(j+1)%3] * a[(i+2)%3][(j+2)%3]) - (a[(i+1)%3][(j+2)%3]*a[(i+2)%3][(j+1)%3]))/ determinant;
+    }
+    for(i = 0; i < 3; i++)
+        for (j = 0; j < 3; j++)
+            b[i][j] = tmp[i][j];
+    return 1;
+}
+
+/* multiply matrix a by b and put result in c. if desired, c
+   can point to a or b. */
+void draw_mmult(t_float a[][3], t_float b[][3], t_float c[][3])
+{
+    t_float tmp[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} };
+    int i, j, k;
+    for(i = 0; i < 3; i++)
+        for(j = 0; j < 3; j++)
+        {
+            t_float sum = 0;
+            for(k = 0; k < 3; k++)
+                sum = sum + a[i][k] * b[k][j];
+            tmp[i][j] = sum;
+        }
+    for(i = 0; i < 3; i++)
+        for(j = 0; j < 3; j++)
+            c[i][j] = tmp[i][j];
+}
+
+void draw_mset(t_float mtx[][3], t_float m1, t_float m2, t_float m3,
+    t_float m4, t_float m5, t_float m6)
+{
+    mtx[0][0] = m1; mtx[1][0] = m2; mtx[0][1] = m3;
+    mtx[1][1] = m4; mtx[0][2] = m5; mtx[1][2] = m6;
+    mtx[2][0] = 0;  mtx[2][1] = 0;  mtx[2][2] = 1;
+}
+
+/* not sure if this is useful... */
+void draw_mget(t_float mtx[][3], t_float *mp1, t_float *mp2, t_float *mp3,
+    t_float *mp4, t_float *mp5, t_float *mp6)
+{
+    *mp1 = mtx[0][0]; *mp2 = mtx[1][0]; *mp3 = mtx[0][1];
+    *mp4 = mtx[1][1]; *mp5 = mtx[0][2]; *mp6 = mtx[1][2];
+}
+
+void draw_parsetransform(t_draw *x, t_template *template, t_word *data,
+    t_float *mp1, t_float *mp2, t_float *mp3,
+    t_float *mp4, t_float *mp5, t_float *mp6)
+{
+    /* parse the args */
+    t_symbol *type;
+    int i, j;
+    t_float m[3][3];
+    t_float m2[3][3];
+    draw_mset(m, 1, 0, 0, 1, 0, 0); /* init to the identity matrix... */
+    draw_mset(m2, 0, 0, 0, 0, 0, 0);
+    int argc = x->x_transform_n;
+    t_fielddesc *fd = x->x_transform;
+    /* should probably change this to argc > 0 since a screwup
+       could land us in negativeland */
+    while (argc)
+    {
+        if (fd->fd_un.fd_varsym)
+            type = fd->fd_un.fd_varsym;
+        else
+        {
+            pd_error(x, "draw: bad args to 'transform' method");
+            return;
+        }
+        fd++;
+        argc --;
+        if (type == gensym("translate"))
+        {
+           t_float tx = fielddesc_getfloat(fd++, template, data, 0);
+           argc--;
+           t_float ty = fielddesc_getfloat(fd++, template, data, 0);
+           argc--;
+           draw_mset(m2, 1, 0, 0, 1, tx, ty);
+           draw_mmult(m, m2, m);
+        }
+        else if (type == gensym("scale"))
+        {
+           t_float sx = fielddesc_getfloat(fd++, template, data, 0);
+           argc--;
+           t_float sy = sx;
+           if (argc && fd->fd_type == A_FLOAT)
+           {
+               sy = fielddesc_getfloat(fd++, template, data, 0);
+               argc--;
+           }
+           draw_mset(m2, sx, 0, 0, sy, 0, 0);
+           draw_mmult(m, m2, m);
+        }
+        /* cx and cy are optional */
+        /* this doesn't jibe with glist_xtopixels, ytopixels, etc. */
+        else if (type == gensym("rotate"))
+        {
+            t_float a = fielddesc_getfloat(fd++, template, data, 0);
+            argc--;
+            t_float cx = 0, cy = 0;
+            if (argc && fd->fd_type == A_FLOAT)
+            {
+                cx = fielddesc_getfloat(fd++, template, data, 0);
+                argc--;
+            }
+            if (argc && fd->fd_type == A_FLOAT)
+            {
+                cy = fielddesc_getfloat(fd++, template, data, 0);
+                argc--;
+            }
+            draw_mset(m2, cos(a), sin(a), sin(a) * -1, cos(a),
+                      sin(a) * cy + cx * -1 * cos(a) + cx,
+                      sin(a) * cx * -1 + cos(a) * cy * -1 + cy);
+            draw_mmult(m, m2, m);
+        }
+        else if (type == gensym("skewx"))
+        {
+            t_float a = fielddesc_getfloat(fd++, template, data, 0);
+            argc--;
+            draw_mset(m2, 1, 0, tan(a), 1, 0, 0);
+            draw_mmult(m, m2, m);
+        }
+        else if (type == gensym("skewy"))
+        {
+            t_float a = fielddesc_getfloat(fd++, template, data, 0);
+            argc--;
+            draw_mset(m2, 1, tan(a), 0, 1, 0, 0);
+            draw_mmult(m, m2, m);
+        }
+        else if (type == gensym("matrix"))
+        {
+            t_float a, b, c, d, e, f;
+            a = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            b = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            c = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            d = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            e = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            f = fielddesc_getfloat(fd++, template, data, 0); argc--;
+            draw_mset(m2, a, b, c, d, e, f);
+            draw_mmult(m, m2, m);
+        }
+    }
+    t_float a1, a2, a3, a4, a5, a6;
+    draw_mget(m, &a1, &a2, &a3, &a4, &a5, &a6);
+    *mp1 = a1;
+    *mp2 = a2;
+    *mp3 = a3;
+    *mp4 = a4;
+    *mp5 = a5;
+    *mp6 = a6;
+}
+
+void draw_group_pathrect_cache(t_draw *x, int state)
+{
+    t_gobj *y;
+    for (y = x->x_canvas->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == draw_class &&
+            ((t_draw *)y)->x_pathrect_cache != -1)
+                ((t_draw *)y)->x_pathrect_cache = state;
+    } 
+}
+
+extern void scalar_select(t_gobj *z, t_glist *owner, int state);
+extern void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state);
+void draw_doupdatetransform(t_draw *x, t_canvas *c)
+{
+    t_float mtx1[3][3];
+    t_float mtx2[3][3];
+    t_float mtx3[3][3];
+    draw_mset(mtx1, 0, 0, 0, 0, 0, 0);
+    draw_mset(mtx2, 0, 0, 0, 0, 0, 0);
+    t_gobj *g;
+    t_template *template;
+
+    /* we'll probably get a different bbox now, so we
+       will calculate a new one the next time we call
+       draw_getrect for this draw command. For groups
+       we need to do it for all of the draw commands.
+    */
+    if (x->x_drawtype == gensym("group"))
+        draw_group_pathrect_cache(x, 0);
+    else if (x->x_pathrect_cache != -1)
+        x->x_pathrect_cache = 0;
+    for (g = c->gl_list; g; g = g->g_next)
+    {
+        if (glist_isvisible(c) && g->g_pd == scalar_class &&
+            x->x_canvas ==
+            template_findcanvas((template = template_findbyname(
+                (((t_scalar *)g)->sc_template))))
+           )
+        {
+            t_float m1, m2, m3, m4, m5, m6;
+            draw_parsetransform(x, template, ((t_scalar *)g)->sc_vec,
+                &m1, &m2, &m3, &m4, &m5, &m6);
+        t_canvas *visible = c;
+        while(visible->gl_isgraph && visible->gl_owner)
+            visible = visible->gl_owner;
+        if (x->x_drawtype == gensym("group"))
+            sys_vgui(".x%lx.c itemconfigure .dgroup%lx -matrix { {%g %g} {%g %g} {%g %g} }\n",
+                visible, ((t_scalar *)g)->sc_vec, m1, m2, m3, m4, m5, m6);
+        else
+            sys_vgui(".x%lx.c itemconfigure .draw%lx.%lx -matrix { {%g %g} {%g %g} {%g %g} }\n",
+                visible, x, ((t_scalar*)g)->sc_vec,
+                m1, m2, m3, m4, m5, m6);
+        }
+        if (glist_isselected(c, &((t_scalar *)g)->sc_gobj))
+        {
+            scalar_select(g, c, 1);
+            scalar_drawselectrect((t_scalar *)g, c, 0);
+            scalar_drawselectrect((t_scalar *)g, c, 1);
+        }
+        if (g->g_pd == canvas_class)
+            draw_doupdatetransform(x, (t_glist *)g);
+    }
+}
+
+void draw_queueupdatetransform(t_gobj *g, t_glist *glist)
+{
+    t_draw *x = (t_draw *)g;
+    draw_doupdatetransform(x, glist);
+}
+
+extern t_canvas *canvas_list;
+void draw_updatetransform(t_draw *x)
+{
+    t_canvas *c;
+    int i;
+    for (c = canvas_list; c; c = c->gl_next)
+        draw_doupdatetransform(x, c);
+    /* Attempted to use sys_queuegui above, but
+       it actually ended up being slightly less
+       efficient. Maybe I was using it wrong...
+       sys_queuegui((t_gobj *)x, c, draw_queueupdatetransform);
+    */
+}
+
+void draw_transform(t_draw *x, t_symbol *s, int argc, t_atom *argv)
+{
+    /* probably need to do error checking here */
+    t_fielddesc *fd;
+    x->x_transform = (t_fielddesc *)t_resizebytes(x->x_transform,
+        x->x_ndash * sizeof(*x->x_transform), argc * sizeof(*x->x_transform));
+    x->x_transform_n = argc;
+    fd = x->x_transform;
+    while (argc)
+    {
+        if (argv->a_type == A_SYMBOL
+            && (argv->a_w.w_symbol == gensym("translate")
+                || argv->a_w.w_symbol == gensym("rotate")
+                || argv->a_w.w_symbol == gensym("scale")
+                || argv->a_w.w_symbol == gensym("skewx")
+                || argv->a_w.w_symbol == gensym("skewy")))
+        {
+            fielddesc_setsymbolarg(fd++, argc--, argv++);
+        }
+        else
+        {
+            if (argv->a_type == A_SYMBOL &&
+                x->x_drawtype == gensym("path"))
+                    x->x_pathrect_cache = -1;
+            fielddesc_setfloatarg(fd++, argc--, argv++);
+        }
+    }
+    draw_updatetransform(x);
+}
+
+/* -------------------- widget behavior for draw ------------ */
+
+/* from Raphael.js lib */
+static void draw_q2c(t_float x1, t_float y1, t_float *cx1, t_float *cy1,
+    t_float *cx2, t_float *cy2, t_float *cx, t_float *cy)
+{
+    t_float _13 = 1.0 / 3.0;
+    t_float _23 = 2.0 / 3.0;
+    t_float ax = *cx1, ay = *cy1, x2 = *cx2, y2 = *cy2;
+
+    *cx1 = _13 * x1 + _23 * ax;
+    *cy1 = _13 * y1 + _23 * ay;
+    *cx2 = _13 * x2 + _23 * ax;
+    *cy2 = _13 * y2 + _23 * ay;
+    *cx = x2;
+    *cy = y2;
+}
+
+/* from Raphael.js lib */
+static void draw_findDotAtSegment(t_float p1x, t_float p1y,
+    t_float c1x, t_float c1y, t_float c2x, t_float c2y,
+    t_float p2x, t_float p2y, t_float t, t_float *dotx, t_float *doty)
+{
+    t_float t1 = 1 - t;
+    *dotx = pow(t1, 3) * p1x +
+        pow(t1, 2) * 3 * t * c1x +
+        t1 * 3 * t * t * c2x +
+        pow(t, 3) * p2x;
+    *doty = pow(t1, 3) * p1y +
+        pow(t1, 2) * 3 * t * c1y +
+        t1 * 3 * t * t * c2y +
+        pow(t, 3) * p2y;
+}
+
+/* from Raphael.js lib */
+static void draw_curvedim(t_float p1x, t_float p1y,
+    t_float c1x, t_float c1y, t_float c2x, t_float c2y,
+    t_float p2x, t_float p2y, t_float *xmin, t_float *ymin,
+    t_float *xmax, t_float *ymax, t_float mtx1[][3])
+{
+    t_float mtx2[3][3];
+    //post("inside curvedim...");
+    int i;
+    t_float a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x);
+    t_float        b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
+            c = p1x - c1x;
+    //post("a is %g", a);
+    //post("b is %g", b);
+    //post("c is %g", c);
+    t_float        syntax_sanity_check = 42,
+            t1 = (a ? ((-b + sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0),
+            t2 = (a ? ((-b - sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0);
+    //post("t1 is %g", t1);
+    //post("t2 is %g", t2);
+    //post("syntax_sanity_check %g", syntax_sanity_check);
+    t_float xy[12];
+    xy[0] = p1x; xy[1] = p1y; xy[2] = p2x; xy[3] = p2y;
+
+/* mtx mult */
+    draw_mset(mtx2, p1x, p1y, p2x, p2y, 0, 0);
+    mtx2[2][0] = 1; mtx2[2][1] = 1;
+    draw_mmult(mtx1, mtx2, mtx2);
+    xy[0] = mtx2[0][0]; xy[1] = mtx2[1][0]; xy[2] = mtx2[0][1]; xy[3] = mtx2[1][1];
+    int xyc = 4;
+    t_float dotx, doty;
+    t_float dot;
+    if (abs(t1) > 1e12) t1 = 0.5;
+    if (abs(t2) > 1e12) t2 = 0.5;
+    if (t1 > 0 && t1 < 1)
+    {
+    //post("found a dot");
+        draw_findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1, &dotx, &doty);
+        draw_mset(mtx2, dotx, doty, 0, 0, 0, 0);
+        mtx2[2][0] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        dotx = mtx2[0][0];
+        doty = mtx2[1][0];
+        xy[xyc++] = dotx;
+        xy[xyc++] = doty;
+    }
+    if (t2 > 0 && t2 < 1)
+    {
+    //post("found a second dot");
+        draw_findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2, &dotx, &doty);
+        draw_mset(mtx2, dotx, doty, 0, 0, 0, 0);
+        mtx2[2][0] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        dotx = mtx2[0][0];
+        doty = mtx2[1][0];
+        xy[xyc++] = dotx;
+        xy[xyc++] = doty;
+    }
+    a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
+    b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
+    c = p1y - c1y;
+    //post("a is %g and b is %g and c is %g", a, b, c);
+    t1 = (a ? ((-b + sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0);
+    t2 = (a ? ((-b - sqrt(abs(b * b - 4 * a * c))) / 2.0 / a) : 0);
+    //post("t1 is %g and t2 is %g", t1, t2);
+    if (abs(t1) > 1e12) t1 = 0.5;
+    if (abs(t2) > 1e12) t2 = 0.5;
+    if (t1 > 0 && t1 < 1)
+    {
+    //post("found 3rd dot");
+        draw_findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1, &dotx, &doty);
+        draw_mset(mtx2, dotx, doty, 0, 0, 0, 0);
+        mtx2[2][0] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        dotx = mtx2[0][0];
+        doty = mtx2[1][0];
+        xy[xyc++] = dotx;
+        xy[xyc++] = doty;
+    }
+    if (t2 > 0 && t2 < 1)
+    {
+    //post("found 4th dot");
+        draw_findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2, &dotx, &doty);
+        draw_mset(mtx2, dotx, doty, 0, 0, 0, 0);
+        mtx2[2][0] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        dotx = mtx2[0][0];
+        doty = mtx2[1][0];
+        xy[xyc++] = dotx;
+        xy[xyc++] = doty;
+    }
+
+    *xmin = *ymin = 0x7fffffff;
+    *xmax = *ymax = -0x7fffffff;
+    for (i = 0; i < xyc; i+=2)
+    {
+    //post("final points:");
+    //post("x %g", xy[i]);
+    //post("y %g", xy[i+1]);
+        if (xy[i] < *xmin) *xmin = xy[i];
+        if (xy[i] > *xmax) *xmax = xy[i];
+        if (xy[i+1] < *ymin) *ymin = xy[i+1];
+        if (xy[i+1] > *ymax) *ymax = xy[i+1];
+    }
+    //post("end of curvedim:");
+    //post("xmin %g ymin %g xmax %g ymax %g", *xmin, *ymin, *xmax, *ymax);
+}
+
+static t_float draw_getangle(t_float bx, t_float by)
+{
+  t_float pi = (t_float)3.14159265358979323846;
+  t_float divisor = sqrt(bx * bx + by * by);
+  return fmod(2*pi + (by > 0.0 ? 1.0 : -1.0) *
+              acos( divisor? (bx / divisor) : 0 ), 2*pi);
+}
+
+void draw_arc2bbox(t_float x1, t_float y1, t_float rx, t_float ry,
+   t_float phi, t_float large_arc, t_float sweep,
+   t_float x2, t_float y2, t_float *xmin, t_float *ymin,
+   t_float *xmax, t_float *ymax)
+{
+    t_float pi = (t_float)3.14159265358979323846;
+
+    if (rx == 0 || ry == 0)
+    {
+        *xmin = (x1 < x2 ? x1 : x2);
+        *xmax = (x1 > x2 ? x1 : x2);
+        *ymin = (y1 < y2 ? y1 : y2);
+        *ymax = (y1 > y2 ? y1 : y2);
+        return;
+    }
+
+    if (rx < 0) rx *= -1;
+    if (ry < 0) ry *= -1;
+
+    const double x1prime = cos(phi)*(x1 - x2)/2 + sin(phi)*(y1 - y2)/2;
+    const double y1prime = -sin(phi)*(x1 - x2)/2 + cos(phi)*(y1 - y2)/2;
+
+    double radicant = (rx*rx*ry*ry - rx*rx*y1prime*y1prime - ry*ry*x1prime*x1prime);
+
+    double divisor = (rx*rx*y1prime*y1prime + ry*ry*x1prime*x1prime);
+    radicant = divisor ? radicant / divisor : 0;
+    t_float cxprime = 0.0;
+    t_float cyprime = 0.0;
+    if (radicant < 0.0)
+    {
+        t_float ratio =  (ry ? rx/ry : 0);
+        t_float radicant = y1prime*y1prime +
+            (ratio ? x1prime*x1prime/(ratio*ratio) : 0);
+        if (radicant < 0.0)
+        {
+            *xmin = (x1 < x2 ? x1 : x2);
+            *xmax = (x1 > x2 ? x1 : x2);
+            *ymin = (y1 < y2 ? y1 : y2);
+            *ymax = (y1 > y2 ? y1 : y2);
+            return;
+        }
+        ry = sqrt(radicant);
+        rx = ratio * ry;
+    }
+    else
+    {
+        t_float factor = (large_arc==sweep ? -1.0 : 1.0)*sqrt(radicant);
+
+        cxprime = factor*rx*y1prime/ry;
+        cyprime = -factor*ry*x1prime/rx;
+    }
+
+    t_float cx = cxprime*cos(phi) - cyprime*sin(phi) + (x1 + x2)/2;
+    t_float cy = cxprime*sin(phi) + cyprime*cos(phi) + (y1 + y2)/2;
+
+    t_float txmin, txmax, tymin, tymax;
+
+    if (phi == 0 || phi == pi)
+    {
+        *xmin = cx - rx;
+        txmin = draw_getangle(-rx, 0);
+        *xmax = cx + rx;
+        txmax = draw_getangle(rx, 0);
+        *ymin = cy - ry;
+        tymin = draw_getangle(0, -ry);
+        *ymax = cy + ry;
+        tymax = draw_getangle(0, ry);
+    }
+    else if (phi == pi / 2.0 || phi == 3.0*pi/2.0)
+    {
+        *xmin = cx - ry;
+        txmin = draw_getangle(-ry, 0);
+        *xmax = cx + ry;
+        txmax = draw_getangle(ry, 0);
+        *ymin = cy - rx;
+        tymin = draw_getangle(0, -rx);
+        *ymax = cy + rx;
+        tymax = draw_getangle(0, rx);
+    }
+    else
+    {
+        txmin = -atan(ry*tan(phi)/rx);
+        txmax = pi - atan (ry*tan(phi)/rx);
+        *xmin = cx + rx*cos(txmin)*cos(phi) - ry*sin(txmin)*sin(phi);
+        *xmax = cx + rx*cos(txmax)*cos(phi) - ry*sin(txmax)*sin(phi);
+        if (*xmin > *xmax)
+        {
+            t_float tmp = *xmax;
+            *xmax = *xmin;
+            *xmin = tmp;
+            tmp = txmax;
+            txmax = txmin;
+            txmin = tmp;
+        }
+        t_float tmpY = cy + rx*cos(txmin)*sin(phi) + ry*sin(txmin)*cos(phi);
+        txmin = draw_getangle(*xmin - cx, tmpY - cy);
+        tmpY = cy + rx*cos(txmax)*sin(phi) + ry*sin(txmax)*cos(phi);
+        txmax = draw_getangle(*xmax - cx, tmpY - cy);
+
+        tymin = atan(ry/(tan(phi)*rx));
+        tymax = atan(ry/(tan(phi)*rx))+pi;
+        *ymin = cy + rx*cos(tymin)*sin(phi) + ry*sin(tymin)*cos(phi);
+        *ymax = cy + rx*cos(tymax)*sin(phi) + ry*sin(tymax)*cos(phi);
+        if (*ymin > *ymax)
+        {
+            t_float tmp = *ymax;
+            *ymax = *ymin;
+            *ymin = tmp;
+            tmp = tymax;
+            tymax = tymin;
+            tymin = tmp;
+        }
+        t_float tmpX = cx + rx*cos(tymin)*cos(phi) - ry*sin(tymin)*sin(phi);
+        tymin = draw_getangle(tmpX - cx, *ymin - cy);
+        tmpX = cx + rx*cos(tymax)*cos(phi) - ry*sin(tymax)*sin(phi);
+        tymax = draw_getangle(tmpX - cx, *ymax - cy);
+    }
+
+    t_float angle1 = draw_getangle(x1 - cx, y1 - cy);
+    t_float angle2 = draw_getangle(x2 - cx, y2 - cy);
+
+    if (!sweep)
+    {
+        t_float tmp = angle1;
+        angle1 = angle2;
+        angle2 = tmp;
+    }
+
+    int other_arc = 0;
+    if (angle1 > angle2)
+    {
+        t_float tmp = angle1;
+        angle1 = angle2;
+        angle2 = tmp;
+        other_arc = 1;
+    }
+
+    if ((!other_arc && (angle1 > txmin || angle2 < txmin)) || (other_arc && !(angle1 > txmin || angle2 < txmin)))
+    *xmin = x1 < x2 ? x1 : x2;
+  if ((!other_arc && (angle1 > txmax || angle2 < txmax)) || (other_arc && !(angle1 > txmax || angle2 < txmax)))
+    *xmax = x1 > x2 ? x1 : x2;
+  if ((!other_arc && (angle1 > tymin || angle2 < tymin)) || (other_arc && !(angle1 > tymin || angle2 < tymin)))
+    *ymin = y1 < y2 ? y1 : y2;
+  if ((!other_arc && (angle1 > tymax || angle2 < tymax)) || (other_arc && !(angle1 > tymax || angle2 < tymax)))
+    *ymax = y1 > y2 ? y1 : y2;
+}
+
+/* get bbox for a path, based very roughly on
+   Raphael.js "pathbbox" function.  Too complex to finish
+   here, but maybe this could eventually get merged in to
+   tkpath-- it will probably give a more accurate result... */
+static void draw_getpathrect(t_draw *x, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2)
+{
+    /* todo: revisit the attr from Raphael for processPath. */ 
+
+    t_float path2_vec[x->x_nargs];
+    char path2cmds[x->x_npathcmds];
+    t_float mtx1[3][3];
+    t_float mtx2[3][3];
+    t_float m1, m2, m3, m4, m5, m6;
+    t_draw *dgroup = draw_getgroup(x);
+    if (dgroup)
+    {
+        draw_parsetransform(dgroup, template, data, &m1, &m2, &m3,
+            &m4, &m5, &m6);
+        draw_mset(mtx1, m1, m2, m3, m4, m5, m6);
+    }
+    else
+        draw_mset(mtx1, 1, 0, 0, 1, 0, 0);
+    draw_parsetransform(x, template, data, &m1, &m2, &m3,
+        &m4, &m5, &m6);
+    draw_mset(mtx2, m1, m2, m3, m4, m5, m6);
+    draw_mmult(mtx1, mtx2, mtx1);
+    
+    /* first let's get absolute path */
+
+    /* check if we even have path data-- if not just return with
+       some default bbox */
+    /* to be filled in ... */ 
+    t_float xx = 0, yy = 0, mx = 0, my = 0;
+    int start = 0, tally = 0;
+    if (x->x_pathcmds[0] == 'M' && x->x_nargs_per_cmd[0] == 2)
+    {
+        xx = fielddesc_getcoord(x->x_vec, template, data, 1);
+        yy = fielddesc_getcoord((x->x_vec+1), template, data, 1);
+        mx = xx;
+        my = yy;
+        start++;
+        tally = 2;
+        path2cmds[0] = 'M';
+        path2_vec[0] = xx;
+        path2_vec[1] = yy;
+    }
+    
+    /* loop through and get absolute values. I don't handle
+       transformations yet */
+    int i = 0;
+    for(i = start; i < x->x_npathcmds; i++)
+    {
+        int j;
+        char cmd = x->x_pathcmds[i];
+        t_float *ia = path2_vec+tally;
+        t_fielddesc *fd = x->x_vec + tally;
+        int rel = cmd > 96 && cmd < 123;
+        if (rel) cmd -= 32;
+        path2cmds[i] = cmd;
+        switch (cmd)
+        {
+        case 'A':
+            *ia = fielddesc_getcoord(fd, template, data, 1);
+            *(ia+1) = fielddesc_getcoord(fd+1, template, data, 1);
+            *(ia+2) = fielddesc_getfloat(fd+2, template, data, 1);
+            *(ia+3) = fielddesc_getfloat(fd+3, template, data, 1);
+            *(ia+4) = fielddesc_getfloat(fd+4, template, data, 1);
+            *(ia+5) = fielddesc_getcoord(fd+5, template, data, 1) + (rel? xx : 0);
+            *(ia+6) = fielddesc_getcoord(fd+6, template, data, 1) + (rel? yy : 0);
+            break;
+        case 'V':
+            *ia = fielddesc_getcoord(fd, template, data, 1) + (rel? yy : 0);
+            break;
+        case 'H':
+            *ia = fielddesc_getcoord(fd, template, data, 1) + (rel? xx : 0);
+            break;
+        case 'L':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
+            {
+                if (j%2 == 0)
+                    xx = *(ia+j) = fielddesc_getcoord(fd+j, template, data, 1)
+                        + (rel? xx : 0);
+                else
+                    yy = *(ia+j) = fielddesc_getcoord(fd+j, template, data, 1)
+                        + (rel? yy : 0);
+            }      
+            break;
+        case 'M':
+            mx = fielddesc_getcoord(fd, template, data, 1) + (rel? xx : 0);
+            my = fielddesc_getcoord(fd+1, template, data, 1) + (rel? yy : 0);
+        default:
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
+            {
+                if (j%2 == 0)
+                {
+                    *(ia+j) = fielddesc_getcoord(fd+j, template, data, 1) + (rel? xx : 0);
+                }
+                else
+                {
+                   *(ia+j) = fielddesc_getcoord(fd+j, template, data, 1) + (rel? yy : 0);
+                }
+            }
+            break;
+        }
+        tally += x->x_nargs_per_cmd[i];
+        switch (cmd)
+        {
+        case 'Z':
+            xx = mx;
+            yy = my;
+            break;
+        case 'H':
+            xx = *ia;
+            break;
+        case 'V':
+            yy = *ia;
+            break;
+        case 'M':
+            mx = *(ia+(x->x_nargs_per_cmd[i] - 2));
+            my = *(ia+(x->x_nargs_per_cmd[i] - 1));
+        default:
+            xx = *(ia+(x->x_nargs_per_cmd[i] - 2));
+            yy = *(ia+(x->x_nargs_per_cmd[i] - 1));
+        }
+    }
+    /* need to normalize everything to curves next ... */
+    /* but for now, a cheap hack... */
+    int finalx1 = 0x7fffffff, finaly1 = 0x7fffffff,
+        finalx2 = -0x7fffffff, finaly2 = -0x7fffffff;
+    tally = 0;
+    int j;
+    t_float tmpx, tmpy;
+    t_float xprev = 0, yprev = 0, qxprev = 0, qyprev = 0, nx = 0, ny = 0,
+        bx = 0, by = 0;
+    t_float x1, x2, y1, y2;
+    char pcmd = 'X';
+    for (i = 0; i < x->x_npathcmds; i++)
+    {
+        char cmd = path2cmds[i];
+        t_float *ia = path2_vec+tally;
+        switch (cmd)
+        {
+        case 'A':
+            draw_arc2bbox(xprev, yprev, *ia, *(ia+1), *(ia+2), *(ia+3),
+                *(ia+4), *(ia+5), *(ia+6), &x1, &y1, &x2, &y2);
+            xprev = *(ia+5);
+            yprev = *(ia+6);
+            draw_mset(mtx2, x1, y1, x2, y2, 0, 0);
+            mtx2[2][0] = 1; mtx2[2][1] = 1;
+            draw_mmult(mtx1, mtx2, mtx2);
+            tmpx = mtx2[0][0]; tmpy = mtx2[1][0];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            tmpx = mtx2[0][1]; tmpy = mtx2[1][1];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            draw_mset(mtx2, x1, y2, x2, y1, 0, 0);
+            mtx2[2][0] = 1; mtx2[2][1] = 1;
+            draw_mmult(mtx1, mtx2, mtx2);
+            tmpx = mtx2[0][0]; tmpy = mtx2[1][0];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            tmpx = mtx2[0][1]; tmpy = mtx2[1][1];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            break;
+        case 'S':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j += 4)
+            {
+                    /* hack */
+                    if ((j + 3) >= x->x_nargs_per_cmd[i])
+                        break;
+                if (pcmd == 'C' || pcmd == 'S')
+                {
+                    /* this is wrong... we need a
+                       "bx" variable at the end instead
+                       of nx... see Raphael... */
+                    nx = xprev * 2 - bx;
+                    ny = yprev * 2 - by;
+                }
+                else
+                {
+                    nx = xprev;
+                    ny = yprev;
+                }
+                draw_curvedim(xprev, yprev,
+                    nx, ny, *ia, *(ia+1), *(ia+2), *(ia+3),
+                    &x1, &y1, &x2, &y2, mtx1);
+                xprev = *(ia+2);
+                yprev = *(ia+3);
+                bx = *ia;
+                by = *(ia + 1);
+
+                if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
+                x2 == -0x7fffffff && y2 == -0x7fffffff)
+                    break;
+                finalx1 = x1 < finalx1 ? x1 : finalx1;
+                finalx2 = x2 > finalx2 ? x2 : finalx2;
+                finaly1 = y1 < finaly1 ? y1 : finaly1;
+                finaly2 = y2 > finaly2 ? y2 : finaly2;
+                pcmd = cmd;
+            }
+            break;
+        case 'T':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j += 2)
+            {
+                    /* hack */
+                    if ((j + 1) >= x->x_nargs_per_cmd[i])
+                        break;
+                if (pcmd == 'Q' || pcmd == 'T')
+                {
+                    qxprev = xprev * 2 - qxprev;
+                    qyprev = yprev * 2 - qyprev;
+                }
+                else
+                {
+                    qxprev = xprev;
+                    qyprev = yprev;
+                }
+                t_float cx1 = qxprev, cy1 = qyprev, cx2 = *ia, cy2 = *(ia+1),
+                    cx, cy;
+                draw_q2c(xprev, yprev, &cx1, &cy1, &cx2, &cy2, &cx, &cy);
+                draw_curvedim(xprev, yprev, cx1, cy1, cx2, cy2, cx, cy,
+                    &x1, &y1, &x2, &y2, mtx1);
+                xprev = *ia;
+                yprev = *(ia+1);
+
+                if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
+                x2 == -0x7fffffff && y2 == -0x7fffffff)
+                    break;
+                finalx1 = x1 < finalx1 ? x1 : finalx1;
+                finalx2 = x2 > finalx2 ? x2 : finalx2;
+                finaly1 = y1 < finaly1 ? y1 : finaly1;
+                finaly2 = y2 > finaly2 ? y2 : finaly2;
+                pcmd = cmd;
+            }
+            break;
+        case 'Q':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j += 4)
+            {
+                    /* hack */
+                    if ((j + 3) >= x->x_nargs_per_cmd[i])
+                        break;
+                t_float cx1 = *ia, cy1 = *(ia+1),
+                    cx2 = *(ia+2), cy2 = *(ia+3), cx, cy;
+                draw_q2c(xprev, yprev, &cx1, &cy1, &cx2, &cy2, &cx, &cy);
+                draw_curvedim(xprev, yprev, cx1, cy1, cx2, cy2, cx, cy,
+                    &x1, &y1, &x2, &y2, mtx1);
+                xprev = *(ia+2);
+                yprev = *(ia+3);
+
+                if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
+                    x2 == -0x7fffffff && y2 == -0x7fffffff)
+                        break;
+                finalx1 = x1 < finalx1 ? x1 : finalx1;
+                finalx2 = x2 > finalx2 ? x2 : finalx2;
+                finaly1 = y1 < finaly1 ? y1 : finaly1;
+                finaly2 = y2 > finaly2 ? y2 : finaly2;
+            }
+            break;
+        case 'C':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j += 6)
+            {
+                /* hack */
+                if ((j + 5) >= x->x_nargs_per_cmd[i])
+                    break;
+                draw_curvedim(xprev, yprev, *ia, *(ia+1), *(ia+2), *(ia+3),
+                    *(ia+4), *(ia+5), &x1, &y1, &x2, &y2, mtx1);
+                xprev = *(ia+4);
+                yprev = *(ia+5);
+                bx = *(ia+2);
+                by = *(ia+3);
+
+                if (x1 == 0x7fffffff && y1 == 0x7fffffff &&
+                    x2 == -0x7fffffff && y2 == -0x7fffffff)
+                        break;
+                finalx1 = x1 < finalx1 ? x1 : finalx1;
+                finalx2 = x2 > finalx2 ? x2 : finalx2;
+                finaly1 = y1 < finaly1 ? y1 : finaly1;
+                finaly2 = y2 > finaly2 ? y2 : finaly2;
+            }
+/*
+            draw_mset(mtx2, x1, y1, x2, y2, 0, 0);
+            mtx2[2][0] = 1; mtx2[2][1] = 1;
+            draw_mmult(mtx1, mtx2, mtx2);
+            tmpx = mtx2[0][0]; tmpy = mtx2[1][0];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            tmpx = mtx2[0][1]; tmpy = mtx2[1][1];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            draw_mset(mtx2, x1, y2, x2, y1, 0, 0);
+            mtx2[2][0] = 1; mtx2[2][1] = 1;
+            draw_mmult(mtx1, mtx2, mtx2);
+            tmpx = mtx2[0][0]; tmpy = mtx2[1][0];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+            tmpx = mtx2[0][1]; tmpy = mtx2[1][1];
+            finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+            finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+            finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+            finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+*/
+            break;
+        case 'V':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
+            {
+                draw_mset(mtx2, xprev, *ia, 0, 0, 0, 0);
+                mtx2[2][0] = 1;
+                draw_mmult(mtx1, mtx2, mtx2);
+                tmpy = mtx2[1][0];
+                finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+                finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+                yprev = *ia;
+            }
+            break;
+        case 'H':
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
+            {
+                draw_mset(mtx2, *ia, yprev, 0, 0, 0, 0);
+                mtx2[2][0] = 1;
+                draw_mmult(mtx1, mtx2, mtx2);
+                tmpx = mtx2[0][0];  
+                finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+                finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+                xprev = *ia;
+            }
+            break;
+        default:
+            for (j = 0; j < x->x_nargs_per_cmd[i]; j += 2)
+            {
+                    /* hack */
+                    if ((j + 1) >= x->x_nargs_per_cmd[i])
+                        break;
+                    draw_mset(mtx2, *(ia+j), *(ia+j+1), 0, 0, 0, 0);
+                    mtx2[2][0] = 1;
+                    draw_mmult(mtx1, mtx2, mtx2);
+                    tmpx = mtx2[0][0]; tmpy = mtx2[1][0];
+                    finalx1 = tmpx < finalx1 ? tmpx : finalx1;
+                    finalx2 = tmpx > finalx2 ? tmpx : finalx2;
+                    finaly1 = tmpy < finaly1 ? tmpy : finaly1;
+                    finaly2 = tmpy > finaly2 ? tmpy : finaly2;
+                    xprev = *(ia+j); yprev = *(ia+j+1);
+            }
+    }
+        tally += x->x_nargs_per_cmd[i];
+        pcmd = cmd;
+    }
+    x->x_x1 = finalx1;
+    x->x_x2 = finalx2;
+    x->x_y1 = finaly1;
+    x->x_y2 = finaly2;
+    if (x->x_pathrect_cache != -1)
+        x->x_pathrect_cache = 1;
+    finalx1 = glist_xtopixels(glist, basex + finalx1);
+    finalx2 = glist_xtopixels(glist, basex + finalx2);
+    finaly1 = glist_ytopixels(glist, basey + finaly1);
+    finaly2 = glist_ytopixels(glist, basey + finaly2);
+    *xp1 = (int)finalx1;
+    *xp2 = (int)finalx2;
+    *yp1 = (int)finaly1;
+    *yp2 = (int)finaly2;
+}
+
+static void draw_setrect(t_draw *x, t_floatarg x1,
+    t_floatarg y1, t_floatarg x2, t_floatarg y2)
+{
+    /* todo: grab from getpathrect to allow caching the
+       auto-calculation */
+    x->x_x1 = x1 < 0 ? 0 : x1;
+    x->x_x2 = x2 < 0 ? 0 : x2;
+    x->x_y1 = y1 < 0 ? 0 : y1;
+    x->x_y2 = y2 < 0 ? 0 : y2;
+}
+
+static int call_from_vis = 0;
+static void draw_getrect(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2)
+{
+    t_draw *x = (t_draw *)z;
+    if (x->x_pathrect_cache == 1)
+    {
+        *xp1 = glist_xtopixels(glist, basex + x->x_x1);
+        *xp2 = glist_xtopixels(glist, basex + x->x_x2);
+        *yp1 = glist_ytopixels(glist, basey + x->x_y1);
+        *yp2 = glist_ytopixels(glist, basey + x->x_y2);
+        return;
+    }
+
+    t_float mtx1[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} };
+    t_float mtx2[3][3] = { {1, 0, 0}, {0, 1, 0}, {1, 0, 1} };
+    t_float m1, m2, m3, m4, m5, m6;
+    
+    //fprintf(stderr,"draw_getrect\n");
+    t_draw *dgroup = draw_getgroup(x);
+    int i, n = x->x_nargs;
+    t_fielddesc *f = x->x_vec;
+    int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
+    if (!fielddesc_getfloat(&x->x_vis, template, data, 0) ||
+        (x->x_flags & NOMOUSE) || (x->x_drawtype == gensym("group")))
+    {
+        *xp1 = *yp1 = 0x7fffffff;
+        *xp2 = *yp2 = -0x7fffffff;
+        return;
+    }
+
+    if (dgroup)
+    {
+        draw_parsetransform(dgroup, template, data, &m1, &m2,
+            &m3, &m4, &m5, &m6);
+        draw_mset(mtx1, m1, m2, m3, m4, m5, m6);
+    }
+    else
+        draw_mset(mtx1, 1, 0, 0, 1, 0, 0);
+
+    if (x->x_drawtype == gensym("path"))
+    {
+        /* this could get damned expensive with complex paths 
+        so a caching mechanism would be nice */
+        draw_getpathrect(x, glist, data, template, basex, basey,
+            &x1, &y1, &x2, &y2);
+    }
+    else if (x->x_drawtype == gensym("polyline") ||
+             x->x_drawtype == gensym("line") ||
+             x->x_drawtype == gensym("polygon"))
+    {
+        int nxy = n >> 1;
+        draw_parsetransform(x, template, data, &m1, &m2, &m3,
+            &m4, &m5, &m6);
+        draw_mset(mtx2, m1, m2, m3, m4, m5, m6);
+        draw_mmult(mtx1, mtx2, mtx1);
+
+        for (i = 0, f = x->x_vec; i < n; i+=2, f+=2)
+        {
+            t_float xloc = fielddesc_getcoord(f, template, data, 0);
+            t_float yloc = fielddesc_getcoord(f+1, template, data, 0);
+
+            draw_mset(mtx2, xloc, yloc, 0, 0, 0, 0);
+            mtx2[2][0] = 1;
+            draw_mmult(mtx1, mtx2, mtx2);
+            xloc = mtx2[0][0];
+            yloc = mtx2[1][0];
+
+            if (xloc < x1) x1 = xloc;
+            if (xloc > x2) x2 = xloc;
+            if (yloc < y1) y1 = yloc;
+            if (yloc > y2) y2 = yloc;
+        }
+        if (n)
+        {
+            x1 = glist_xtopixels(glist, basex + x1);
+            x2 = glist_xtopixels(glist, basex + x2);
+            y1 = glist_ytopixels(glist, basey + y1);
+            y2 = glist_ytopixels(glist, basey + y2);
+        }
+    }
+    else if (x->x_drawtype == gensym("rect") ||
+             x->x_drawtype == gensym("circle") ||
+             x->x_drawtype == gensym("ellipse"))
+    {
+        t_float m1, m2, m3, m4, m5, m6; /* matrix */
+        t_float xx1, yy1, xx2, yy2;
+        t_float tx1, ty1, tx2, ty2, t5, t6; /* transformed points */
+        if (x->x_drawtype == gensym("rect"))
+        {
+            xx1 = fielddesc_getcoord(x->x_vec, template, data, 0);
+            yy1 = fielddesc_getcoord(x->x_vec+1, template, data, 0);
+            t_float rwidth = fielddesc_getcoord(x->x_vec+2, template, data, 0);
+            t_float rheight = fielddesc_getcoord(x->x_vec+3, template, data, 0);
+            xx2 = xx1 + rwidth;
+            yy2 = yy1 + rheight;
+        }
+        else
+        {
+            /* Yes, I realize this isn't the tightest-fitting bbox but it's
+               late and I'm losing steam... */
+            t_float cx = fielddesc_getcoord(x->x_vec, template, data, 0);
+            t_float cy = fielddesc_getcoord(x->x_vec+1, template, data, 0);
+            t_float rx = fielddesc_getcoord(x->x_vec+2, template, data, 0);
+            t_float ry = fielddesc_getcoord(x->x_vec +
+                (x->x_drawtype == gensym("ellipse")? 3 : 2), template,
+                data, 0);
+            xx1 = cx - rx;
+            yy1 = cy - ry;
+            xx2 = cx + rx;
+            yy2 = cy + ry;
+        }
+        draw_parsetransform(x, template, data, &m1, &m2, &m3, &m4, &m5, &m6);
+        draw_mset(mtx2, m1, m2, m3, m4, m5, m6);
+        draw_mmult(mtx1, mtx2, mtx1);
+        /* There's probably a much easier way to do this.  I'm just
+           setting the first two columns to x/y points to get them
+           transformed.  Since the shapes could be crazy skewed/rotated
+           I have to check each coordinate of the rect, so I do it again
+           below. */
+        draw_mset(mtx2, xx1, yy1, xx2, yy2, 0, 0);
+        mtx2[2][0] = 1; mtx2[2][1] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        draw_mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+        if (tx1 < x1) x1 = tx1;
+        if (tx2 < x1) x1 = tx2;
+        if (ty1 < y1) y1 = ty1;
+        if (ty2 < y1) y1 = ty2;
+        if (tx1 > x2) x2 = tx1;
+        if (tx2 > x2) x2 = tx2;
+        if (ty1 > y2) y2 = ty1;
+        if (ty2 > y2) y2 = ty2;
+        draw_mset(mtx2, xx1, yy2, xx2, yy1, 0, 0);
+        mtx2[2][0] = 1; mtx2[2][1] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        draw_mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+        if (tx1 < x1) x1 = tx1;
+        if (tx2 < x1) x1 = tx2;
+        if (ty1 < y1) y1 = ty1;
+        if (ty2 < y1) y1 = ty2;
+        if (tx1 > x2) x2 = tx1;
+        if (tx2 > x2) x2 = tx2;
+        if (ty1 > y2) y2 = ty1;
+        if (ty2 > y2) y2 = ty2;
+        x1 = glist_xtopixels(glist, basex + x1);
+        x2 = glist_xtopixels(glist, basex + x2);
+        y1 = glist_ytopixels(glist, basey + y1);
+        y2 = glist_ytopixels(glist, basey + y2);
+    }
+    if (fielddesc_getfloat(&x->x_strokewidth, template, data, 0))
+    {
+        int padding = fielddesc_getcoord(&x->x_strokewidth,
+            template, data, 0) * 0.5;
+        x1 -= padding;
+        y1 -= padding;
+        x2 += padding;
+        y2 += padding;
+    }
+    //fprintf(stderr,"FINAL draw_getrect %d %d %d %d\n", x1, y1, x2, y2);
+    *xp1 = x1;
+    *yp1 = y1;
+    *xp2 = x2;
+    *yp2 = y2; 
+}
+
+static void draw_displace(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int dx, int dy)
+{
+    /* refuse */
+}
+
+static void draw_select(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int state)
+{
+    /* fill in later */
+}
+
+static void draw_activate(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int state)
+{
+    /* fill in later */
+}
+
+/* todo: create the group somewhere in here..., and use the template
+tag on it so that it can get deleted on unvising */
+static void draw_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int vis)
+{
+    t_draw *x = (t_draw *)z;
+    /* As a quick hack we are sending the group matrix to the
+       gui. This is inefficient since the group matrix is the
+       same for all the other drawing instructions. It should
+       instead be sent once inside scalar_vis, but that means
+       searching the templatecanvas for a [draw group] object
+       rather than just a single loop to find all the drawing
+       command parentvisfn functions which it does currently.
+
+       Really, [draw group] needs to be a separate class, but
+       that means putting all the options-- stroke, etc.-- in
+       a separate struct. It would also mean that draw_update
+       and draw_updatetransform need to be made more general.
+    */
+
+    if (x->x_drawtype == gensym("group"))
+    {
+        t_float m1, m2, m3, m4, m5, m6;
+        draw_parsetransform(x, template, data, &m1, &m2, &m3, &m4, &m5, &m6);
+        sys_vgui(".x%lx.c itemconfigure .dgroup%lx -matrix { {%g %g} {%g %g} {%g %g} }\n", glist, data, m1, m2, m3, m4, m5, m6);
+        return;
+    }
+
+    //post("number of points in draw_vis is %d", x->x_nargs);
+    int i, n = x->x_nargs;
+    t_float mtx1[3][3] =  { { 0, 0, 0}, {0, 0, 0}, {0, 0, 1} };
+    t_float mtx2[3][3] =  { { 0, 0, 0}, {0, 0, 0}, {0, 0, 1} };
+    /* need to scale some attributes like radii, widths, etc. */
+    t_float xscale = glist_xtopixels(glist, 1) - glist_xtopixels(glist, 0);
+    t_float yscale = glist_ytopixels(glist, 1) - glist_ytopixels(glist, 0);
+    t_fielddesc *f = x->x_vec;
+
+    /*// get the universal tag for all nested objects
+    t_canvas *tag = x->x_canvas;
+    while (tag->gl_owner) {
+        tag = tag->gl_owner;
+    }*/
+    
+        /* see comment in plot_vis() */
+    if (vis && !fielddesc_getfloat(&x->x_vis, template, data, 0))
+        return;
+    if (vis)
+    {
+        if (n > 2)
+        {
+            /* Hack to figure out whether we're inside an
+               array. See curve_vis comment for more info
+               and feel free to revise this to make it a
+               more sane approach.
+            */
+            int in_array = (sc->sc_vec == data) ? 0 : 1;
+            if (in_array)
+                sys_vgui(".x%lx.c create group -tags {.scelem%lx} "
+                    "-parent .dgroup%lx -matrix { {1 0} {0 1} {%g %g} }\n",
+                    glist_getcanvas(glist), data, sc->sc_vec, basex, basey);
+            int flags = x->x_flags;
+            char *outline;
+            char *fill;
+            char *stroke;
+            char *strokelinecap = get_strokelinecap(
+                (int)fielddesc_getfloat(&x->x_strokelinecap,
+                    template, data, 1));
+            char *strokelinejoin = get_strokelinejoin(
+                (int)fielddesc_getfloat(&x->x_strokelinejoin,
+                    template, data, 1));
+            if (x->x_fill)
+                fill = x->x_fill->s_name;
+            else
+            {
+                fill = rgb_to_hex(
+                    (int)fielddesc_getfloat(&x->x_fill_r, template, data, 1),
+                    (int)fielddesc_getfloat(&x->x_fill_g, template, data, 1),
+                    (int)fielddesc_getfloat(&x->x_fill_b, template, data, 1));
+            }
+            if (x->x_stroke)
+                stroke = x->x_stroke->s_name;
+            else
+            {
+                stroke = rgb_to_hex(
+                    (int)fielddesc_getfloat(&x->x_stroke_r, template, data, 1),
+                    (int)fielddesc_getfloat(&x->x_stroke_g, template, data, 1),
+                    (int)fielddesc_getfloat(&x->x_stroke_b, template, data, 1));
+            }
+            t_float pix[200];
+            if (n > 200)
+                n = 200;
+                /* calculate the pixel values before we start printing
+                out the TK message so that "error" printout won't be
+                interspersed with it.  Only show up to 100 points so we don't
+                have to allocate memory here. */
+            if (x->x_drawtype != gensym("path"))
+            {
+                int nxy = n >> 1;
+                for (i = 0, f = x->x_vec; i < nxy; i++, f += 2)
+                {
+                    pix[2*i] = fielddesc_getcoord(f, template, data, 1);
+                    pix[2*i+1] = fielddesc_getcoord(f+1, template, data, 1);
+                    if (x->x_drawtype == gensym("circle")) break;
+                }
+            }
+            /* begin the gui drawing command */
+            if (x->x_drawtype == gensym("rect"))
+                sys_vgui(".x%lx.c create prect\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("ellipse"))
+                sys_vgui(".x%lx.c create ellipse\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("line"))
+                sys_vgui(".x%lx.c create pline\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("polyline"))
+                sys_vgui(".x%lx.c create polyline\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("polygon"))
+                sys_vgui(".x%lx.c create ppolygon\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("path"))
+                sys_vgui(".x%lx.c create path {\\\n", glist_getcanvas(glist));
+            else if (x->x_drawtype == gensym("circle"))
+                sys_vgui(".x%lx.c create ellipse\\\n", glist_getcanvas(glist));
+            /* next send the gui drawing arguments: commands and points
+               for paths, points for everything else */
+            if (x->x_drawtype == gensym("path"))
+            {
+                /* let's turn off bbox caching so we can recalculate
+                   the bbox */
+                if (x->x_pathrect_cache != -1)
+                    x->x_pathrect_cache = 0;
+                char *cmd;
+                int totalpoints = 0; /* running tally */
+                /* path parser: no error checking yet */
+                for (i = 0, cmd = x->x_pathcmds; i < x->x_npathcmds; i++, cmd++)
+                {
+                    int j;
+                    int cargs = x->x_nargs_per_cmd[i];
+                    f = (x->x_vec)+totalpoints;
+                    sys_vgui("%c\\\n", *(cmd));
+                    for (j = 0; j < x->x_nargs_per_cmd[i]; j++)
+                        sys_vgui("%g\\\n", fielddesc_getcoord(
+                            f+j, template, data, 1));
+                    totalpoints += x->x_nargs_per_cmd[i];
+                }
+                sys_gui("}\\\n");
+            }
+            else /* all other shapes */
+            {
+                int nxy = n >> 1;
+                for (i = 0; i < nxy; i++)
+                {
+                    sys_vgui("%g %g\\\n", pix[2*i], pix[2*i+1]);
+                    if ((x->x_drawtype == gensym("ellipse") ||
+                         x->x_drawtype == gensym("circle")) && n > 1)
+                    {
+                        sys_vgui("-rx %d -ry %d\\\n",
+                            (int)(fielddesc_getcoord(x->x_vec+2,
+                                template, data, 1)),
+                            (int)(fielddesc_getcoord(x->x_vec +
+                                (x->x_drawtype == gensym("ellipse")?
+                                 3 : 2),
+                                template, data, 1)));
+                        break;
+                    }
+                    else if (x->x_drawtype == gensym("rect") && n > 1)
+                    {
+                        sys_vgui("%g %g\\\n",
+                            fielddesc_getcoord(x->x_vec,
+                                template, data, 1) +
+                                fielddesc_getcoord(x->x_vec+2,
+                                template, data, 1),
+                            fielddesc_getcoord(x->x_vec+1,
+                                template, data, 1) +
+                                fielddesc_getcoord(x->x_vec+3,
+                                template, data, 1));
+                        break;
+                    }
+                    else if (x->x_drawtype == gensym("circle"))
+                    {
+                        sys_vgui("-r %d\\\n",
+                            (t_int)fielddesc_getcoord(x->x_vec+2,
+                                template, data, 1));
+                        break;
+                    }
+                }
+            }
+            sys_vgui("-strokewidth %g\\\n", fielddesc_getfloat(&x->x_strokewidth, template, data, 1));
+            if (x->x_drawtype != gensym("line"))
+                sys_vgui("-fill %s -fillopacity %g -fillrule %s\\\n",
+                    fill,
+                    fielddesc_getfloat(&x->x_fillopacity, template, data, 1),
+                    (int)fielddesc_getfloat(
+                    &x->x_fillrule, template, data, 1) ? "evenodd" : "nonzero");
+            sys_vgui("-stroke %s -strokeopacity %g -strokelinecap %s -strokelinejoin %s -strokemiterlimit %g\\\n",
+                stroke,
+                fielddesc_getfloat(&x->x_strokeopacity, template, data, 1),
+                strokelinecap, strokelinejoin,
+                fielddesc_getfloat(&x->x_strokemiterlimit, template, data, 1)
+            );
+            if (x->x_ndash)
+            {
+                int i;
+                t_fielddesc *fd;
+                sys_gui(" -strokedasharray {\\\n");
+                for (i = 0, fd = x->x_strokedasharray; i < x->x_ndash; i++)
+                {
+                    sys_vgui("%d\\\n", (int)fielddesc_getfloat(fd+i,
+                        template, data, 1));
+                }
+                sys_gui("}\\\n");
+            }
+            if (x->x_transform_n > 0)
+            {
+                /* todo: premultiply this */
+                t_float m1, m2, m3, m4, m5, m6;
+                draw_parsetransform(x, template, data, &m1, &m2, &m3,
+                    &m4, &m5, &m6);
+                sys_vgui("-matrix { {%g %g} {%g %g} {%g %g} }\\\n",
+                    m1, m2, m3, m4, m5, m6);
+            }
+            //if ((flags & BEZ) && !(flags & BBOX)) sys_vgui("-smooth 1\\\n"); //this doesn't work with tkpath
+            if (in_array)
+                sys_vgui(" -parent .scelem%lx \\\n", data);
+            else
+                sys_vgui(" -parent .dgroup%lx \\\n", sc->sc_vec);
+            /* tags - one for this scalar (not sure why the double glist thingy)
+              one for this specific draw item
+            */
+            sys_vgui("-tags {.x%lx.x%lx.template%lx .draw%lx.%lx}\n",
+                glist_getcanvas(glist), glist, data, x, data);
+            if (!glist_istoplevel(glist))
+            {
+                t_canvas *gl = glist_getcanvas(glist);
+                //glist_noselect(gl);
+                //glist_select(gl, (t_gobj *)glist);
+                char objtag[64];
+                sprintf(objtag, ".x%lx.x%lx.template%lx",
+                    (t_int)gl, (t_int)glist, (t_int)data);
+                canvas_restore_original_position(gl, (t_gobj *)glist,
+                    objtag, -1);
+            }
+        }
+        else post("warning: draws need at least two points to be graphed");
+    }
+    else
+    {
+        if (n > 1)
+
+/* if in_array then delete the container group */
+
+sys_vgui(".x%lx.c delete .x%lx.x%lx.template%lx\n",
+            glist_getcanvas(glist), glist_getcanvas(glist), glist,
+            data);      
+    }
+}
+
+static int draw_motion_field;
+static t_float draw_motion_xcumulative;
+static t_float draw_motion_xbase;
+static t_float draw_motion_xper;
+static t_float draw_motion_ycumulative;
+static t_float draw_motion_ybase;
+static t_float draw_motion_yper;
+static t_glist *draw_motion_glist;
+static t_scalar *draw_motion_scalar;
+static t_array *draw_motion_array;
+static t_word *draw_motion_wp;
+static t_template *draw_motion_template;
+static t_gpointer draw_motion_gpointer;
+
+    /* LATER protect against the template changing or the scalar disappearing
+    probably by attaching a gpointer here ... */
+
+static void draw_motion(void *z, t_floatarg dx, t_floatarg dy)
+{
+    //fprintf(stderr,"draw_motion\n");
+    t_draw *x = (t_draw *)z;
+    t_float mtx1[3][3];
+    t_float mtx2[3][3];
+    t_float m1, m2, m3, m4, m5, m6, tdx, tdy;
+    t_draw *g = draw_getgroup(x);
+    if (g)
+    {
+        draw_parsetransform(g, draw_motion_template, draw_motion_wp,
+            &m1, &m2, &m3, &m4, &m5, &m6);
+        draw_mset(mtx1, m1, m2, m3, m4, m5, m6);
+    }
+    else
+        draw_mset(mtx1, 1, 0, 0, 1, 0, 0);
+    draw_parsetransform(x, draw_motion_template, draw_motion_wp,
+        &m1, &m2, &m3, &m4, &m5, &m6);
+    draw_mset(mtx2, m1, m2, m3, m4, m5, m6);
+    draw_mmult(mtx1, mtx2, mtx1);
+    draw_minv(mtx1, mtx1);
+    /* get rid of translation so it doesn't factor
+       in to our deltas */
+    mtx1[0][2] = 0;
+    mtx1[1][2] = 0;
+    draw_mset(mtx2, dx, dy, 0, 0, 0, 0);
+    mtx2[2][0] = 1;
+    draw_mmult(mtx1, mtx2, mtx2);
+    tdx = mtx2[0][0];
+    tdy = mtx2[1][0];
+    t_fielddesc *f = x->x_vec + draw_motion_field;
+    t_atom at;
+    if (!gpointer_check(&draw_motion_gpointer, 0))
+    {
+        post("draw_motion: scalar disappeared");
+        return;
+    }
+    draw_motion_xcumulative += tdx;
+    draw_motion_ycumulative += tdy;
+    if (f->fd_var && (tdx != 0))
+    {
+        fielddesc_setcoord(f, draw_motion_template, draw_motion_wp,
+            draw_motion_xbase + draw_motion_xcumulative * draw_motion_xper,
+                1); 
+    }
+    if ((f+1)->fd_var && (tdy != 0))
+    {
+        fielddesc_setcoord(f+1, draw_motion_template, draw_motion_wp,
+            draw_motion_ybase + draw_motion_ycumulative * draw_motion_yper,
+                1); 
+    }
+        /* LATER figure out what to do to notify for an array? */
+    if (draw_motion_scalar)
+        template_notifyforscalar(draw_motion_template, draw_motion_glist, 
+            draw_motion_scalar, gensym("change"), 1, &at);
+    if (draw_motion_scalar)
+        scalar_redraw(draw_motion_scalar, draw_motion_glist);
+    if (draw_motion_array)
+        array_redraw(draw_motion_array, draw_motion_glist);
+}
+
+static int draw_click(t_gobj *z, t_glist *glist, 
+    t_word *data, t_template *template, t_scalar *sc, t_array *ap,
+    t_float basex, t_float basey,
+    int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+    //fprintf(stderr,"draw_click %f %f %d %d %lx\n", basex, basey, xpix, ypix, (t_int)data);
+    t_draw *x = (t_draw *)z;
+    t_float mtx1[3][3];
+    t_float mtx2[3][3];
+    t_float m1, m2, m3, m4, m5, m6;
+    t_draw *g = draw_getgroup(x);
+    if (g)
+    {
+        draw_parsetransform(g, template, data, &m1, &m2, &m3, &m4, &m5, &m6);
+        draw_mset(mtx1, m1, m2, m3, m4, m5, m6);
+    }
+    else
+        draw_mset(mtx1, 1, 0, 0, 1, 0, 0);
+    draw_parsetransform(x, template, data, &m1, &m2, &m3, &m4, &m5, &m6);
+    draw_mset(mtx2, m1, m2, m3, m4, m5, m6);
+    draw_mmult(mtx1, mtx2, mtx1);
+    int i, n = x->x_nargs;
+    int bestn = -1;
+    int besterror = 0x7fffffff;
+    t_fielddesc *f;
+    if (!fielddesc_getfloat(&x->x_vis, template, data, 0))
+        return (0);
+    int nxy = n >> 1;
+    for (i = 0, f = x->x_vec; i < n; i++, f += 2)
+    {
+        t_float xval = fielddesc_getcoord(f, template, data, 0);
+        t_float yval = fielddesc_getcoord(f+1, template, data, 0);
+        draw_mset(mtx2, xval, yval, 0, 0, 0, 0);
+        mtx2[2][0] = 1;
+        draw_mmult(mtx1, mtx2, mtx2);
+        t_float txval = mtx2[0][0];
+        t_float tyval = mtx2[1][0];
+        int xloc = glist_xtopixels(glist, basex + txval);
+        int yloc = glist_ytopixels(glist, basey + tyval);
+        int xerr = xloc - xpix, yerr = yloc - ypix;
+        if (!f->fd_var && !(f+1)->fd_var)
+            continue;
+        if (xerr < 0)
+            xerr = -xerr;
+        if (yerr < 0)
+            yerr = -yerr;
+        if (yerr > xerr)
+            xerr = yerr;
+        if (xerr < besterror)
+        {
+            draw_motion_xbase = xval;
+            draw_motion_ybase = yval;
+            besterror = xerr;
+            bestn = i;
+        }
+    }
+    if (besterror > 6)
+        return (0);
+    if (doit)
+    {
+        draw_motion_xper = glist_pixelstox(glist, 1)
+            - glist_pixelstox(glist, 0);
+        draw_motion_yper = glist_pixelstoy(glist, 1)
+            - glist_pixelstoy(glist, 0);
+        draw_motion_xcumulative = 0;
+        draw_motion_ycumulative = 0;
+        draw_motion_glist = glist;
+        draw_motion_scalar = sc;
+        draw_motion_array = ap;
+        draw_motion_wp = data;
+        draw_motion_field = 2*bestn;
+        draw_motion_template = template;
+        if (draw_motion_scalar)
+            gpointer_setglist(&draw_motion_gpointer, draw_motion_glist,
+                draw_motion_scalar);
+        else gpointer_setarray(&draw_motion_gpointer,
+                draw_motion_array, draw_motion_wp);
+        glist_grab(glist, z, draw_motion, 0, xpix, ypix);
+    }
+    return (1);
+}
+
+t_parentwidgetbehavior draw_widgetbehavior =
+{
+    draw_getrect,
+    draw_displace,
+    draw_select,
+    draw_activate,
+    draw_vis,
+    draw_click,
+};
+
+static void draw_free(t_draw *x)
+{
+    /* [draw group] has no pts in x_vec, but it looks like
+       t_freebytes allocates a single byte so freeing it
+       should be fine */
+    t_freebytes(x->x_vec, x->x_nargs * sizeof(*x->x_vec));
+    t_freebytes(x->x_strokedasharray,
+        x->x_ndash * sizeof(*x->x_strokedasharray));
+    t_freebytes(x->x_transform,
+        x->x_transform_n * sizeof(*x->x_transform));
+    if (x->x_drawtype == gensym("path"))
+    {
+        t_freebytes(x->x_pathcmds, x->x_npathcmds * sizeof(*x->x_pathcmds));
+        t_freebytes(x->x_nargs_per_cmd, x->x_npathcmds * sizeof(*x->x_nargs_per_cmd));
+    }
+    char buf[50];
+    sprintf(buf, ".x%lx", (long unsigned int)x);
+    pd_unbind(&x->x_obj.ob_pd, gensym(buf));
+}
+
+static void draw_setup(void)
+{
+    draw_class = class_new(gensym("draw"), (t_newmethod)draw_new,
+        (t_method)draw_free, sizeof(t_draw), 0, A_GIMME, 0);
+    class_setdrawcommand(draw_class);
+    class_setparentwidget(draw_class, &draw_widgetbehavior);
+    class_addfloat(draw_class, draw_float);
+    class_addmethod(draw_class, (t_method)draw_fill,
+        gensym("fill"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_fillopacity,
+        gensym("fill-opacity"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_fillrule,
+        gensym("fill-rule"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_stroke,
+        gensym("stroke"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokedasharray,
+        gensym("stroke-dasharray"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokeopacity,
+        gensym("stroke-opacity"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokelinecap,
+        gensym("stroke-linecap"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokelinejoin,
+        gensym("stroke-linejoin"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokemiterlimit,
+        gensym("stroke-miterlimit"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_strokewidth,
+        gensym("stroke-width"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_transform,
+        gensym("transform"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_rx,
+        gensym("rx"), A_GIMME, 0);
+    class_addmethod(draw_class, (t_method)draw_ry,
+        gensym("ry"), A_GIMME, 0);
+}
+
 /* ---------------- curves and polygons (joined segments) ---------------- */
 
 /*
@@ -967,7 +3178,15 @@ typedef struct _curve
     int x_flags;            /* CLOSED and/or BEZ and/or NOMOUSE */
     t_fielddesc x_fillcolor;
     t_fielddesc x_fillopacity;
+    t_fielddesc x_fillrule;
     t_fielddesc x_outlinecolor;
+    t_fielddesc *x_strokedasharray; /* array of lengths */
+    t_fielddesc x_strokelinecap;
+    t_fielddesc x_strokelinejoin;
+    t_fielddesc x_strokemiterlimit;
+    t_fielddesc x_strokeopacity;
+    t_fielddesc x_strokewidth;
+    t_fielddesc *x_matrix;
     t_fielddesc x_width;
     t_fielddesc x_vis;
     int x_npoints;
@@ -1181,6 +3400,17 @@ static void curve_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
     {
         if (n > 1)
         {
+            /* The first variable here is an obscure hack. If
+               "sc->sc_vec" is not the same address as "data"
+               then we're drawing this curve as an element of
+               an array. It's because plot_vis cycles through
+               an array's char *a_vec, casts each element to
+               a t_word* and send it to us as the "data" param.
+               But we send the same "sc" scalar each time so
+               we can use it to do this check. This should be
+               revised so it's done in a more sane fashion
+            */
+            int in_array = (sc->sc_vec == data) ? 0 : 1;
             int flags = x->x_flags, closed = (flags & CLOSED);
             t_float width = fielddesc_getfloat(&x->x_width, template, data, 1);
             char outline[20], fill[20];
@@ -1193,10 +3423,12 @@ static void curve_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
                 have to allocate memory here. */
             for (i = 0, f = x->x_vec; i < n; i++, f += 2)
             {
-                pix[2*i] = glist_xtopixels(glist,
-                    basex + fielddesc_getcoord(f, template, data, 1));
-                pix[2*i+1] = glist_ytopixels(glist,
-                    basey + fielddesc_getcoord(f+1, template, data, 1));
+                //pix[2*i] = glist_xtopixels(glist,
+                //    basex + fielddesc_getcoord(f, template, data, 1));
+                //pix[2*i+1] = glist_ytopixels(glist,
+                //    basey + fielddesc_getcoord(f+1, template, data, 1));
+                pix[2*i] = fielddesc_getcoord(f, template, data, 1);
+                pix[2*i+1] = fielddesc_getcoord(f+1, template, data, 1);
             }
             if (width < 1) width = 1;
             numbertocolor(
@@ -1236,7 +3468,10 @@ static void curve_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
             }
             for (i = 0; i < n; i++)
             {
-                sys_vgui("%d %d \\\n", pix[2*i], pix[2*i+1]);
+                //sys_vgui("%d %d \\\n", pix[2*i], pix[2*i+1]);
+                sys_vgui("%d %d \\\n",
+                    pix[2*i] + (in_array ? (int)basex : 0),
+                    pix[2*i+1] + (in_array ? (int)basey : 0));
                 if ((flags & BEZ) && (flags & BBOX))
                 {
                     sys_vgui("-rx %d -ry %d \\\n",
@@ -1252,6 +3487,7 @@ static void curve_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
                 fill, outline, fielddesc_getfloat(&x->x_fillopacity, template, data, 1));
             else if(flags & BBOX) sys_vgui("-stroke %s \\\n", outline);
             else sys_vgui("-stroke %s \\\n", outline);
+            sys_vgui("-parent .dgroup%lx \\\n", sc->sc_vec);
             //if ((flags & BEZ) && !(flags & BBOX)) sys_vgui("-smooth 1 \\\n"); //this doesn't work with tkpath
             sys_vgui("-tags {.x%lx.x%lx.template%lx scalar%lx}\n", glist_getcanvas(glist), glist,
 				data, sc);
@@ -2087,13 +4323,13 @@ static void plot_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
                 t_float usexloc, useyloc;
                 t_gobj *y;
                 if (xonset >= 0)
-                    usexloc = basex + xloc +
+                    usexloc = /* basex */ + xloc +
                         *(t_float *)((elem + elemsize * i) + xonset);
-                else usexloc = basex + xsum, xsum += xinc;
+                else usexloc = /* basex */ + xsum, xsum += xinc;
                 if (yonset >= 0)
                     yval = *(t_float *)((elem + elemsize * i) + yonset);
                 else yval = 0;
-                useyloc = basey + yloc +
+                useyloc = /* basey */ + yloc +
                     fielddesc_cvttocoord(yfielddesc, yval);
                 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
                 {
@@ -2367,10 +4603,13 @@ static void drawnumber_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
         t_atom at;
 		int fontsize = fielddesc_getfloat(&x->x_fontsize, template, data, 0);
 		if (!fontsize) fontsize = glist_getfont(glist);
-        int xloc = glist_xtopixels(glist,
+        /*int xloc = glist_xtopixels(glist,
             basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));
         int yloc = glist_ytopixels(glist,
-            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));
+            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));*/
+        int xloc = fielddesc_getcoord(&x->x_xloc, template, data, 0);
+        int yloc = fielddesc_getcoord(&x->x_yloc, template, data, 0);
+
         char colorstring[20], buf[DRAWNUMBER_BUFSIZE];
         numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1),
             colorstring);
@@ -2378,10 +4617,16 @@ static void drawnumber_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
             SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
         else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
         drawnumber_sprintf(x, buf, &at);
-        sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}",
+        /*sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}",
                 glist_getcanvas(glist), xloc, yloc, colorstring, buf);
         sys_vgui(" -font {{%s} -%d %s}", sys_font,
-				 sys_hostfontsize(fontsize), sys_fontweight);
+				 sys_hostfontsize(fontsize), sys_fontweight);*/
+        sys_vgui(".x%lx.c create ptext %d [expr {[font metrics {{%s} %d} -ascent] + %d}] -textanchor start -fill %s -text {%s}\\\n",
+                glist_getcanvas(glist), xloc, sys_font, sys_hostfontsize(fontsize), yloc, colorstring, buf);
+        /* have to remove fontweight for the time being... */
+        sys_vgui(" -fontfamily {%s} -fontsize %d", sys_font,
+                sys_hostfontsize(fontsize));
+        sys_vgui(" -parent .scalar%lx", data);
         sys_vgui(" -tags {.x%lx.x%lx.template%lx scalar%lx}\n", 
 			glist_getcanvas(glist), glist, data, sc);
     }
@@ -2732,10 +4977,13 @@ static void drawsymbol_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
         t_atom at;
 		int fontsize = fielddesc_getfloat(&x->x_fontsize, template, data, 0);
 		if (!fontsize) fontsize = glist_getfont(glist);
-        int xloc = glist_xtopixels(glist,
+        /*int xloc = glist_xtopixels(glist,
             basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));
         int yloc = glist_ytopixels(glist,
-            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));
+            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));*/
+        int xloc = fielddesc_getcoord(&x->x_xloc, template, data, 0);
+        int yloc = fielddesc_getcoord(&x->x_yloc, template, data, 0);
+
         char colorstring[20], buf[DRAWSYMBOL_BUFSIZE];
         numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1),
             colorstring);
@@ -2743,10 +4991,15 @@ static void drawsymbol_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
             SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
         else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
         drawsymbol_sprintf(x, buf, &at);
-        sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}",
+        /*sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}",
                 glist_getcanvas(glist), xloc, yloc, colorstring, buf);
         sys_vgui(" -font {{%s} -%d %s}", sys_font,
-				 sys_hostfontsize(fontsize), sys_fontweight);
+				 sys_hostfontsize(fontsize), sys_fontweight);*/
+        sys_vgui(".x%lx.c create ptext %d [expr {[font metrics {{%s} %d} -ascent] + %d}] -textanchor start -fill %s -text {%s}\\\n",
+                glist_getcanvas(glist), xloc, sys_font, sys_hostfontsize(fontsize), yloc, colorstring, buf);
+        sys_vgui(" -fontfamily {%s} -fontsize %d ", sys_font,
+                sys_hostfontsize(fontsize));
+        sys_vgui(" -parent .scalar%lx", data);
         sys_vgui(" -tags {.x%lx.x%lx.template%lx scalar%lx}\n", 
 			glist_getcanvas(glist), glist, data, sc);
     }
@@ -2953,12 +5206,14 @@ typedef struct _drawimage
     t_float x_w;
     t_float x_h;
     int x_flags;
+    int x_deleteme;
     t_canvas *x_canvas;
 } t_drawimage;
 
 static void *drawimage_new(t_symbol *classsym, t_int argc, t_atom *argv)
 {
     t_drawimage *x = (t_drawimage *)pd_new(drawimage_class);
+    x->x_deleteme = 0;
     char *classname = classsym->s_name;
     char buf[50];
     sprintf(buf, ".x%lx", (t_int)x);
@@ -3005,6 +5260,7 @@ static void *drawimage_new(t_symbol *classsym, t_int argc, t_atom *argv)
     */
     sys_vgui("pdtk_drawimage_new .x%lx {%s} {%s} %d\n", (t_int)x,
         x->x_img->s_name, dir->s_name, x->x_flags);
+    post("deleteme is %d", x->x_deleteme);
     return (x);
 }
 
@@ -3105,15 +5361,20 @@ static void drawimage_vis(t_gobj *z, t_glist *glist, t_scalar *sc,
     if (vis)
     {
         t_atom at;
-        int xloc = glist_xtopixels(glist,
+        /*int xloc = glist_xtopixels(glist,
             basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));
         int yloc = glist_ytopixels(glist,
             basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));
-        sys_vgui("pdtk_drawimage_vis .x%lx.c %d %d .x%lx .x%lx.i %d ",
+        sys_vgui("pdtk_drawimage_vis .x%lx.c %d %d .x%lx .x%lx.i %d ",*/
+        int xloc = fielddesc_getcoord(&x->x_xloc, template, data, 0);
+        int yloc = fielddesc_getcoord(&x->x_yloc, template, data, 0);
+        sys_vgui("pdtk_drawimage_vis .x%lx.c %d %d .x%lx .x%lx.i %d \\\n",
             glist_getcanvas(glist), xloc, yloc, x, data,
             (int)fielddesc_getfloat(&x->x_value, template, data, 0));
-        sys_vgui(".x%lx.x%lx.template%lx scalar%lx\n", glist_getcanvas(glist),
-            glist, data, sc);
+        //sys_vgui(".x%lx.x%lx.template%lx scalar%lx\n", glist_getcanvas(glist),
+        //    glist, data, sc);
+        sys_vgui(".x%lx.x%lx.template%lx scalar%lx .scalar%lx\n", glist_getcanvas(glist),
+            glist, data, sc, data);
     }
     else sys_vgui("pdtk_drawimage_unvis .x%lx.c .x%lx.i\n",
         glist_getcanvas(glist), data);
@@ -3285,7 +5546,8 @@ static void drawimage_free(t_drawimage *x)
 {
     /* delete the parent image in the gui */
     char buf[50];
-    sprintf(buf, ".x%lx", (t_int)x);
+    //sprintf(buf, ".x%lx", (t_int)x);
+    sprintf(buf, ".x%lx", (long unsigned int)x);
     pd_unbind(&x->x_obj.ob_pd, gensym(buf));
     sys_vgui("pdtk_drawimage_free .x%lx\n", (t_int)x);
 }
@@ -3304,6 +5566,41 @@ static void drawimage_setup(void)
     class_setparentwidget(drawimage_class, &drawimage_widgetbehavior);
 }
 
+/* ------------- convenience functions for all drawcommands --------------*/
+
+/* works for [draw] and old style curves, drawnumber, etc. */
+t_template *template_findbydrawcommand(t_gobj *g)
+{
+    t_canvas *c;
+    if (g->g_pd == draw_class)
+        c = ((t_draw *)g)->x_canvas;
+    else if (g->g_pd == curve_class)
+        c = ((t_curve *)g)->x_canvas;
+    else if (g->g_pd == drawnumber_class)
+        c = ((t_drawnumber *)g)->x_canvas;
+    else if (g->g_pd == drawsymbol_class)
+        c = ((t_drawsymbol *)g)->x_canvas;
+    else if (g->g_pd == drawimage_class)
+        c = ((t_drawimage *)g)->x_canvas;
+    else return (0);
+    t_template *tmpl;
+    t_symbol *s1 = gensym("struct");
+    for (g = c->gl_list; g; g = g->g_next)
+    {
+        t_object *ob = pd_checkobject(&g->g_pd);
+        t_atom *argv;
+        if (!ob || ob->te_type != T_OBJECT ||
+            binbuf_getnatom(ob->te_binbuf) < 2)
+            continue;
+        argv = binbuf_getvec(ob->te_binbuf);
+        if (argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL
+            || argv[0].a_w.w_symbol != s1)
+                continue;
+        return (template_findbyname(canvas_makebindsym(argv[1].a_w.w_symbol)));
+    }
+    return (0);
+}
+
 /* ---------------------- setup function ---------------------------- */
 
 void g_template_setup(void)
@@ -3311,6 +5608,7 @@ void g_template_setup(void)
     template_setup();
     gtemplate_setup();
     curve_setup();
+    draw_setup();
     plot_setup();
     drawnumber_setup();
     drawsymbol_setup();
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 60c502d07a64b388be474985a965c710e5318dbb..6b93e1a556111b6df47d0bd9aa8d11807bb645c1 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -104,6 +104,7 @@ void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
 /* ----------------- the "object" object.  ------------------ */
 
 extern t_pd *newest;
+extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
 void canvas_getargs(int *argcp, t_atom **argvp);
 
 static void canvas_objtext(t_glist *gl, int xpix, int ypix, int width, int selected,
@@ -127,6 +128,30 @@ static void canvas_objtext(t_glist *gl, int xpix, int ypix, int width, int selec
     {
         if (!newest)
         {
+            /* let's see if there's a scalar by this name... */
+            t_atom *scalar_at = binbuf_getvec(b);
+            if (scalar_at->a_type == A_SYMBOL)
+            {
+                t_symbol *templatesym = 
+                    canvas_makebindsym(atom_getsymbol(scalar_at));
+                if (template_findbyname(templatesym))
+                {
+                    //post("Hmm, we found a scalar from %s... let's try to instantiate it!", templatesym->s_name);
+                    t_binbuf *scalarbuf = binbuf_new();
+                    t_atom coords_at[2];
+                    SETFLOAT(coords_at, (t_float)xpix);
+                    SETFLOAT(coords_at+1, (t_float)ypix);
+                    binbuf_add(scalarbuf, 1, scalar_at);
+                    binbuf_add(scalarbuf, 2, coords_at);
+                    binbuf_add(scalarbuf, binbuf_getnatom(b)-1, scalar_at+1);
+                    t_atom *scalar_create_at = binbuf_getvec(scalarbuf);
+                    glist_scalar(gl, gensym("scalar_from_canvas_objtext"), binbuf_getnatom(b)+2, scalar_create_at);
+                    binbuf_free(scalarbuf);
+                    binbuf_free(b);
+                    canvas_unsetcurrent((t_canvas *)gl);
+                    return;
+                }
+            }
             binbuf_print(b);
             post("... couldn't create");
             x = 0;
diff --git a/pd/src/pdtk_drawimage.tcl b/pd/src/pdtk_drawimage.tcl
index 5ce186bd607db38201ead681d1e3babe9263f552..a9c0d5f4271c6e4b20dc128b98f848b604ea158b 100644
--- a/pd/src/pdtk_drawimage.tcl
+++ b/pd/src/pdtk_drawimage.tcl
@@ -64,12 +64,12 @@ proc pdtk_drawimage_new {obj path canvasdir flags} {
     }
 }
 
-proc pdtk_drawimage_vis {c x y obj tag seqno l2orktag1 l2orktag2} {
+proc pdtk_drawimage_vis {c x y obj tag seqno l2orktag1 l2orktag2 tag3} {
     set img ::drawimage_${obj}
     set len [llength [lsearch -glob -all [image names] ${img}*]]
     if {$len < 1} {return}
     if {$seqno >= $len || $seqno < 0} {set seqno [expr {$seqno % $len}]}
-    $c create image $x $y -image ${img}$seqno -anchor nw -tags [list $tag $l2orktag1 $l2orktag2]
+    $c create pimage $x $y -image ${img}$seqno -tags [list $tag $l2orktag1 $l2orktag2] -parent $tag3
 }
 
 proc pdtk_drawimage_unvis {c tag} {