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} {