/* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include #include #include "m_pd.h" #include "s_stuff.h" /* for sys_hostfontsize */ #include "g_canvas.h" /* This file contains text objects you would put in a canvas to define a template. Templates describe objects of type "array" (g_array.c) and "scalar" (g_scalar.c). */ /* T.Grill - changed the _template.t_pd member to t_pdobj to avoid name clashes with the t_pd type */ /* the structure of a "struct" object (also the obsolete "gtemplate" you get when using the name "template" in a box.) */ struct _gtemplate { t_object x_obj; t_template *x_template; t_canvas *x_owner; t_symbol *x_sym; struct _gtemplate *x_next; int x_argc; t_atom *x_argv; }; /* ---------------- forward definitions ---------------- */ static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a); static void template_conformglist(t_template *tfrom, t_template *tto, t_glist *glist, int *conformaction); /* ---------------------- storage ------------------------- */ static t_class *gtemplate_class; static t_class *template_class; /* there's a pre-defined "float" template. LATER should we bind this to a symbol such as "pd-float"??? */ static t_dataslot template_float_vec = { DT_FLOAT, &s_y, &s_ }; static t_template template_float = { 0, /* class -- fill in in setup routine */ 0, /* list of "struct"/t_gtemplate objects */ &s_float, /* name */ 1, /* number of items */ &template_float_vec }; /* return true if two dataslot definitions match */ static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, int nametoo) { return ((!nametoo || ds1->ds_name == ds2->ds_name) && ds1->ds_type == ds2->ds_type && (ds1->ds_type != DT_ARRAY || ds1->ds_arraytemplate == ds2->ds_arraytemplate)); } /* -- templates, the active ingredient in gtemplates defined below. ------- */ t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) { t_template *x = (t_template *)pd_new(template_class); x->t_n = 0; x->t_vec = (t_dataslot *)t_getbytes(0); while (argc > 0) { int newtype, oldn, newn; t_symbol *newname, *newarraytemplate = &s_, *newtypesym; if (argc < 2 || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL) goto bad; newtypesym = argv[0].a_w.w_symbol; newname = argv[1].a_w.w_symbol; if (newtypesym == &s_float) newtype = DT_FLOAT; else if (newtypesym == &s_symbol) newtype = DT_SYMBOL; else if (newtypesym == &s_list) newtype = DT_LIST; else if (newtypesym == gensym("array")) { if (argc < 3 || argv[2].a_type != A_SYMBOL) { pd_error(x, "array lacks element template or name"); goto bad; } newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol); newtype = DT_ARRAY; argc--; argv++; } else { pd_error(x, "%s: no such type", newtypesym->s_name); return (0); } newn = (oldn = x->t_n) + 1; x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec, oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec)); x->t_n = newn; x->t_vec[oldn].ds_type = newtype; x->t_vec[oldn].ds_name = newname; x->t_vec[oldn].ds_arraytemplate = newarraytemplate; bad: argc -= 2; argv += 2; } if (templatesym->s_name) { x->t_sym = templatesym; pd_bind(&x->t_pdobj, x->t_sym); } else x->t_sym = templatesym; return (x); } int template_size(t_template *x) { return (x->t_n * sizeof(t_word)); } int template_find_field(t_template *x, t_symbol *name, int *p_onset, int *p_type, t_symbol **p_arraytype) { t_template *t; int i, n; if (!x) { bug("template_find_field"); return (0); } n = x->t_n; for (i = 0; i < n; i++) if (x->t_vec[i].ds_name == name) { *p_onset = i * sizeof(t_word); *p_type = x->t_vec[i].ds_type; *p_arraytype = x->t_vec[i].ds_arraytemplate; return (1); } return (0); } t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { int onset, type; t_symbol *arraytype; float val = 0; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_FLOAT) val = *(t_float *)(((char *)wp) + onset); else if (loud) error("%s.%s: not a number", x->t_sym->s_name, fieldname->s_name); } else if (loud) error("%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); return (val); } void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud) { int onset, type; t_symbol *arraytype; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_FLOAT) *(t_float *)(((char *)wp) + onset) = f; else if (loud) error("%s.%s: not a number", x->t_sym->s_name, fieldname->s_name); } else if (loud) error("%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); } t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { int onset, type; t_symbol *arraytype; t_symbol *val = &s_; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_SYMBOL) val = *(t_symbol **)(((char *)wp) + onset); else if (loud) error("%s.%s: not a symbol", x->t_sym->s_name, fieldname->s_name); } else if (loud) error("%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); return (val); } void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud) { int onset, type; t_symbol *arraytype; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_SYMBOL) *(t_symbol **)(((char *)wp) + onset) = s; else if (loud) error("%s.%s: not a symbol", x->t_sym->s_name, fieldname->s_name); } else if (loud) error("%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); } /* stringent check to see if a "saved" template, x2, matches the current one (x1). It's OK if x1 has additional scalar elements but not (yet) arrays or lists. This is used for reading in "data files". */ int template_match(t_template *x1, t_template *x2) { int i; if (x1->t_n < x2->t_n) return (0); for (i = x2->t_n; i < x1->t_n; i++) { if (x1->t_vec[i].ds_type == DT_ARRAY || x1->t_vec[i].ds_type == DT_LIST) return (0); } if (x2->t_n > x1->t_n) post("add elements..."); for (i = 0; i < x2->t_n; i++) if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1)) return (0); return (1); } /* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ /* the following routines handle updating scalars to agree with changes in their template. The old template is assumed to be the "installed" one so we can delete old items; but making new ones we have to avoid scalar_new which would make an old one whereas we will want a new one (but whose array elements might still be old ones. LATER deal with graphics updates too... */ /* conform the word vector of a scalar to the new template */ static void template_conformwords(t_template *tfrom, t_template *tto, int *conformaction, t_word *wfrom, t_word *wto) { int nfrom = tfrom->t_n, nto = tto->t_n, i; for (i = 0; i < nto; i++) { if (conformaction[i] >= 0) { /* we swap the two, in case it's an array or list, so that when "wfrom" is deleted the old one gets cleaned up. */ t_word wwas = wto[i]; wto[i] = wfrom[conformaction[i]]; wfrom[conformaction[i]] = wwas; } } } /* conform a scalar, recursively conforming sublists and arrays */ static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto, int *conformaction, t_glist *glist, t_scalar *scfrom) { t_scalar *x; t_gpointer gp; int nto = tto->t_n, nfrom = tfrom->t_n, i; t_template *scalartemplate; /* post("conform scalar"); */ /* possibly replace the scalar */ if (scfrom->sc_template == tfrom->t_sym) { /* see scalar_new() for comment about the gpointer. */ gpointer_init(&gp); x = (t_scalar *)getbytes(sizeof(t_scalar) + (tto->t_n - 1) * sizeof(*x->sc_vec)); x->sc_gobj.g_pd = scalar_class; x->sc_template = tfrom->t_sym; gpointer_setglist(&gp, glist, x); /* Here we initialize to the new template, but array and list elements will still belong to old template. */ word_init(x->sc_vec, tto, &gp); template_conformwords(tfrom, tto, conformaction, scfrom->sc_vec, x->sc_vec); /* replace the old one with the new one in the list */ if (glist->gl_list == &scfrom->sc_gobj) { glist->gl_list = &x->sc_gobj; x->sc_gobj.g_next = scfrom->sc_gobj.g_next; } else { t_gobj *y, *y2; for (y = glist->gl_list; y2 = y->g_next; y = y2) if (y2 == &scfrom->sc_gobj) { x->sc_gobj.g_next = y2->g_next; y->g_next = &x->sc_gobj; goto nobug; } bug("template_conformscalar"); nobug: ; } /* burn the old one */ pd_free(&scfrom->sc_gobj.g_pd); } else x = scfrom; scalartemplate = template_findbyname(x->sc_template); /* convert all array elements and sublists */ for (i = 0; i < scalartemplate->t_n; i++) { t_dataslot *ds = scalartemplate->t_vec + i; if (ds->ds_type == DT_LIST) { t_glist *gl2 = x->sc_vec[i].w_list; template_conformglist(tfrom, tto, gl2, conformaction); } else if (ds->ds_type == DT_ARRAY) { template_conformarray(tfrom, tto, conformaction, x->sc_vec[i].w_array); } } return (x); } /* conform an array, recursively conforming sublists and arrays */ static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a) { int i; if (a->a_templatesym == tfrom->t_sym) { /* the array elements must all be conformed */ int oldelemsize = sizeof(t_word) * tfrom->t_n, newelemsize = sizeof(t_word) * tto->t_n; char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n); char *oldarray = a->a_vec; if (a->a_elemsize != oldelemsize) bug("template_conformarray"); for (i = 0; i < a->a_n; i++) { t_word *wp = (t_word *)(newarray + newelemsize * i); word_init(wp, tto, &a->a_gp); template_conformwords(tfrom, tto, conformaction, (t_word *)(oldarray + oldelemsize * i), wp); } } bug("template_conformarray: this part not written"); /* go through item by item conforming subarrays and sublists... */ } /* this routine searches for every scalar in the glist that belongs to the "from" template and makes it belong to the "to" template. Descend glists recursively. We don't handle redrawing here; this is to be filled in LATER... */ static void template_conformglist(t_template *tfrom, t_template *tto, t_glist *glist, int *conformaction) { t_gobj *g; /* post("conform glist %s", glist->gl_name->s_name); */ for (g = glist->gl_list; g; g = g->g_next) { if (pd_class(&g->g_pd) == scalar_class) g = &template_conformscalar(tfrom, tto, conformaction, glist, (t_scalar *)g)->sc_gobj; else if (pd_class(&g->g_pd) == canvas_class) template_conformglist(tfrom, tto, (t_glist *)g, conformaction); } } /* globally conform all scalars from one template to another */ void template_conform(t_template *tfrom, t_template *tto) { int nto = tto->t_n, nfrom = tfrom->t_n, i, j, *conformaction = (int *)getbytes(sizeof(int) * nto), *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0; for (i = 0; i < nto; i++) conformaction[i] = -1; for (i = 0; i < nfrom; i++) conformedfrom[i] = 0; for (i = 0; i < nto; i++) { t_dataslot *dataslot = &tto->t_vec[i]; for (j = 0; j < nfrom; j++) { t_dataslot *dataslot2 = &tfrom->t_vec[j]; if (dataslot_matches(dataslot, dataslot2, 1)) { conformaction[i] = j; conformedfrom[j] = 1; } } } for (i = 0; i < nto; i++) if (conformaction[i] < 0) { t_dataslot *dataslot = &tto->t_vec[i]; for (j = 0; j < nfrom; j++) if (!conformedfrom[j] && dataslot_matches(dataslot, &tfrom->t_vec[j], 1)) { conformaction[i] = j; conformedfrom[j] = 1; } } if (nto != nfrom) doit = 1; else for (i = 0; i < nto; i++) if (conformaction[i] != i) doit = 1; if (doit) { t_glist *gl; /* post("conforming template '%s' to new structure", tfrom->t_sym->s_name); for (i = 0; i < nto; i++) post("... %d", conformaction[i]); */ for (gl = canvas_list; gl; gl = gl->gl_next) template_conformglist(tfrom, tto, gl, conformaction); } freebytes(conformaction, sizeof(int) * nto); freebytes(conformedfrom, sizeof(int) * nfrom); } t_template *template_findbyname(t_symbol *s) { int i; if (s == &s_float) return (&template_float); else return ((t_template *)pd_findbyclass(s, template_class)); } t_canvas *template_findcanvas(t_template *template) { t_gtemplate *gt; if (!template) bug("template_findcanvas"); if (!(gt = template->t_list)) return (0); return (gt->x_owner); /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */ } /* call this when reading a patch from a file to declare what templates we'll need. If there's already a template, check if it matches. If it doesn't it's still OK as long as there are no "struct" (gtemplate) objects hanging from it; we just conform everyone to the new template. If there are still struct objects belonging to the other template, we're in trouble. LATER we'll figure out how to conform the new patch's objects to the pre-existing struct. */ static void *template_usetemplate(void *dummy, t_symbol *s, int argc, t_atom *argv) { t_template *x; t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); if (!argc) return (0); argc--; argv++; /* check if there's already a template by this name. */ if ((x = (t_template *)pd_findbyclass(templatesym, template_class))) { t_template *y = template_new(&s_, argc, argv); /* If the new template is the same as the old one, there's nothing to do. */ if (!template_match(x, y)) { /* Are there "struct" objects upholding this template? */ if (x->t_list) { /* don't know what to do here! */ error("%s: template mismatch", templatesym->s_name); } else { /* conform everyone to the new template */ template_conform(x, y); pd_free(&x->t_pdobj); template_new(templatesym, argc, argv); } } pd_free(&y->t_pdobj); } /* otherwise, just make one. */ else template_new(templatesym, argc, argv); return (0); } /* here we assume someone has already cleaned up all instances of this. */ void template_free(t_template *x) { if (*x->t_sym->s_name) pd_unbind(&x->t_pdobj, x->t_sym); t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec)); } static void template_setup(void) { template_class = class_new(gensym("template"), 0, (t_method)template_free, sizeof(t_template), CLASS_PD, 0); class_addmethod(pd_canvasmaker, (t_method)template_usetemplate, gensym("struct"), A_GIMME, 0); } /* ---------------- gtemplates. One per canvas. ----------- */ /* this is a "text" object that searches for, and if necessary creates, a "template" (above). Other objects in the canvas then can give drawing instructions for the template. The template doesn't go away when the gtemplate is deleted, so that you can replace it with another one to add new fields, for example. */ static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv) { t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); t_template *t = template_findbyname(sym); int i; t_symbol *sx = gensym("x"); x->x_owner = canvas_getcurrent(); x->x_next = 0; x->x_sym = sym; x->x_argc = argc; x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom)); for (i = 0; i < argc; i++) x->x_argv[i] = argv[i]; /* already have a template by this name? */ if (t) { x->x_template = t; /* if it's already got a "struct" or "gtemplate" object we just tack this one to the end of the list and leave it there. */ if (t->t_list) { t_gtemplate *x2, *x3; for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3) ; x2->x_next = x; post("template %s: warning: already exists.", sym->s_name); } else { /* if there's none, we just replace the template with our own and conform it. */ t_template *y = template_new(&s_, argc, argv); /* Unless the new template is different from the old one, there's nothing to do. */ if (!template_match(t, y)) { /* conform everyone to the new template */ template_conform(t, y); pd_free(&t->t_pdobj); t = template_new(sym, argc, argv); } pd_free(&y->t_pdobj); t->t_list = x; } } else { /* otherwise make a new one and we're the only struct on it. */ x->x_template = t = template_new(sym, argc, argv); t->t_list = x; } return (x); } static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) { t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); t_symbol *sym = atom_getsymbolarg(0, argc, argv); if (argc >= 1) argc--; argv++; return (gtemplate_donew(canvas_makebindsym(sym), argc, argv)); } /* old version (0.34) -- delete 2003 or so */ static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv) { t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name); static int warned; if (!warned) { post("warning -- 'template' (%s) is obsolete; replace with 'struct'", sym->s_name); warned = 1; } return (gtemplate_donew(sym, argc, argv)); } t_template *gtemplate_get(t_gtemplate *x) { return (x->x_template); } static void gtemplate_free(t_gtemplate *x) { /* get off the template's list */ t_template *t = x->x_template; if (x == t->t_list) { if (x->x_next) { /* if we were first on the list, and there are others on the list, make a new template corresponding to the new first-on-list and replace teh existing template with it. */ t_template *z = template_new(&s_, x->x_argc, x->x_argv); template_conform(t, z); pd_free(&t->t_pdobj); pd_free(&z->t_pdobj); z = template_new(x->x_sym, x->x_argc, x->x_argv); z->t_list = x->x_next; } else t->t_list = 0; } else { t_gtemplate *x2, *x3; for (x2 = t->t_list; x3 = x2->x_next; x2 = x3) { if (x == x3) { x2->x_next = x3->x_next; break; } } } freebytes(x->x_argv, sizeof(t_atom) * x->x_argc); } static void gtemplate_setup(void) { gtemplate_class = class_new(gensym("struct"), (t_newmethod)gtemplate_new, (t_method)gtemplate_free, sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0); class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"), A_GIMME, 0); } /* --------------- FIELD DESCRIPTORS ---------------------- */ /* a field descriptor can hold a constant or a variable; if a variable, it's the name of a field in the template we belong to. LATER, we might want to cache the offset of the field so we don't have to search for it every single time we draw the object. */ typedef struct _fielddesc { char fd_type; /* LATER consider removing this? */ char fd_var; union { t_float fd_float; /* the field is a constant float */ t_symbol *fd_symbol; /* the field is a constant symbol */ t_symbol *fd_varsym; /* the field is variable and this is the name */ } fd_un; } t_fielddesc; #define FIELDDESC_SETFLOAT(x, f) \ ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f)) #define FIELDDESC_SETSYMBOL(x, s) \ ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s)) #define FIELDDESC_SETVAR(x, s, type) \ ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s)) #define CLOSED 1 #define BEZ 2 #define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv) { if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); else if (argv->a_type == A_SYMBOL) FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT); else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); } static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv) { if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); else if (argv->a_type == A_SYMBOL) FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY); else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); } static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template, t_word *wp, int loud) { if (f->fd_type == A_FLOAT) { if (f->fd_var) return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud)); else return (f->fd_un.fd_float); } else { if (loud) error("symbolic data field used as number"); return (0); } } static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template, t_word *wp, int loud) { if (f->fd_type == A_SYMBOL) { if (f->fd_var) return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud)); else return (f->fd_un.fd_symbol); } else { if (loud) error("numeric data field used as symbol"); return (&s_); } } /* ---------------- curves and polygons (joined segments) ---------------- */ /* curves belong to templates and describe how the data in the template are to be drawn. The coordinates of the curve (and other display features) can be attached to fields in the template. */ t_class *curve_class; typedef struct _curve { t_object x_obj; int x_flags; /* CLOSED and/or BEZ */ t_fielddesc x_fillcolor; t_fielddesc x_outlinecolor; t_fielddesc x_width; int x_npoints; t_fielddesc *x_vec; } t_curve; static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv) { t_curve *x = (t_curve *)pd_new(curve_class); char *classname = classsym->s_name; int flags = 0; int nxy, i; t_fielddesc *fd; if (classname[0] == 'f') { classname += 6; flags |= CLOSED; if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); } else classname += 4; if (classname[0] == 'c') flags |= BEZ; x->x_flags = flags; if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_width, 1); if (argc < 0) argc = 0; nxy = (argc + (argc & 1)); x->x_npoints = (nxy>>1); x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc)); for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++) fielddesc_setfloatarg(fd, 1, argv); if (argc & 1) FIELDDESC_SETFLOAT(fd, 0); return (x); } /* -------------------- widget behavior for curve ------------ */ static void curve_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; t_fielddesc *f = x->x_vec; int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; for (i = 0, f = x->x_vec; i < n; i++, f += 2) { int xloc = glist_xtopixels(glist, basex + fielddesc_getfloat(f, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getfloat(f+1, template, data, 0)); if (xloc < x1) x1 = xloc; if (xloc > x2) x2 = xloc; if (yloc < y1) y1 = yloc; if (yloc > y2) y2 = yloc; } *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void curve_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int dx, int dy) { /* refuse */ } static void curve_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { /* fill in later */ } static void curve_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { /* fill in later */ } static int rangecolor(int n) /* 0 to 9 in 5 steps */ { int n2 = n/2; /* 0 to 4 */ int ret = (n2 << 6); /* 0 to 256 in 5 steps */ if (ret > 255) ret = 255; return (ret); } static void numbertocolor(int n, char *s) { int red, blue, green; if (n < 0) n = 0; red = n / 100; blue = ((n / 10) % 10); green = n % 10; sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue), rangecolor(green)); } static void curve_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int vis) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; t_fielddesc *f = x->x_vec; if (vis) { if (n > 1) { int flags = x->x_flags, closed = (flags & CLOSED); float width = fielddesc_getfloat(&x->x_width, template, data, 1); char outline[20], fill[20]; if (width < 1) width = 1; numbertocolor( fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), outline); if (flags & CLOSED) { numbertocolor( fielddesc_getfloat(&x->x_fillcolor, template, data, 1), fill); sys_vgui(".x%lx.c create polygon\\\n", glist_getcanvas(glist)); } else sys_vgui(".x%lx.c create line\\\n", glist_getcanvas(glist)); for (i = 0, f = x->x_vec; i < n; i++, f += 2) { float xloc = glist_xtopixels(glist, basex + fielddesc_getfloat(f, template, data, 1)); float yloc = glist_ytopixels(glist, basey + fielddesc_getfloat(f+1, template, data, 1)); sys_vgui("%d %d\\\n", (int)xloc, (int)yloc); } sys_vgui("-width %f\\\n", fielddesc_getfloat(&x->x_width, template, data, 1)); if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n", fill, outline); else sys_vgui("-fill %s\\\n", outline); if (flags & BEZ) sys_vgui("-smooth 1\\\n"); sys_vgui("-tags curve%lx\n", data); } else post("warning: curves need at least two points to be graphed"); } else { if (n > 1) sys_vgui(".x%lx.c delete curve%lx\n", glist_getcanvas(glist), data); } } static int curve_motion_field; static float curve_motion_xcumulative; static float curve_motion_xbase; static float curve_motion_xper; static float curve_motion_ycumulative; static float curve_motion_ybase; static float curve_motion_yper; static t_glist *curve_motion_glist; static t_gobj *curve_motion_gobj; static t_word *curve_motion_wp; static t_template *curve_motion_template; /* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ static void curve_motion(void *z, t_floatarg dx, t_floatarg dy) { t_curve *x = (t_curve *)z; t_fielddesc *f = x->x_vec + curve_motion_field; curve_motion_xcumulative += dx; curve_motion_ycumulative += dy; if (f->fd_var) { template_setfloat(curve_motion_template, f->fd_un.fd_varsym, curve_motion_wp, curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper, 1); } if ((f+1)->fd_var) { template_setfloat(curve_motion_template, (f+1)->fd_un.fd_varsym, curve_motion_wp, curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper, 1); } glist_redrawitem(curve_motion_glist, curve_motion_gobj); } static int curve_click(t_gobj *z, t_glist *glist, t_scalar *sc, t_template *template, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; int bestn = -1; int besterror = 0x7fffffff; t_fielddesc *f = x->x_vec; t_word *data = sc->sc_vec; for (i = 0, f = x->x_vec; i < n; i++, f += 2) { int xloc = glist_xtopixels(glist, basex + fielddesc_getfloat(f, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getfloat(f+1, template, data, 0)); 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) { besterror = xerr; bestn = i; curve_motion_xbase = fielddesc_getfloat(f, template, data, 0); curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0); } } if (besterror > 10) return (0); if (doit) { curve_motion_xper = glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0); curve_motion_yper = glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0); curve_motion_xcumulative = curve_motion_ycumulative = 0; curve_motion_glist = glist; curve_motion_gobj = &sc->sc_gobj; curve_motion_wp = data; curve_motion_field = 2*bestn; curve_motion_template = template; glist_grab(glist, z, curve_motion, 0, xpix, ypix); } return (1); } t_parentwidgetbehavior curve_widgetbehavior = { curve_getrect, curve_displace, curve_select, curve_activate, curve_vis, curve_click, }; static void curve_free(t_curve *x) { t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); } static void curve_setup(void) { curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new, (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0); class_setdrawcommand(curve_class); class_addcreator((t_newmethod)curve_new, gensym("drawcurve"), A_GIMME, 0); class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"), A_GIMME, 0); class_addcreator((t_newmethod)curve_new, gensym("filledcurve"), A_GIMME, 0); class_setparentwidget(curve_class, &curve_widgetbehavior); } /* --------- plots for showing arrays --------------- */ t_class *plot_class; typedef struct _plot { t_object x_obj; int x_flags; t_fielddesc x_outlinecolor; t_fielddesc x_width; t_fielddesc x_xloc; t_fielddesc x_yloc; t_fielddesc x_xinc; t_fielddesc x_data; } t_plot; static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv) { t_plot *x = (t_plot *)pd_new(plot_class); int flags = 0; int nxy, i; t_fielddesc *fd; t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "curve")) { flags |= BEZ; argc--, argv++; } if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_data, 1); if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_width, 1); if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_xloc, 1); if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_yloc, 1); if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_xinc, 1); x->x_flags = flags; return (x); } /* -------------------- widget behavior for plot ------------ */ /* get everything we'll need from the owner template of the array being plotted. Not used for garrays, but see below */ static int plot_readownertemplate(t_plot *x, t_word *data, t_template *ownertemplate, t_symbol **elemtemplatesymp, t_array **arrayp, float *linewidthp, float *xlocp, float *xincp, float *ylocp) { int arrayonset, type; t_symbol *elemtemplatesym; t_array *array; /* find the data and verify it's an array */ if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var) { error("plot: needs an array field"); return (-1); } if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym, &arrayonset, &type, &elemtemplatesym)) { error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name); return (-1); } if (type != DT_ARRAY) { error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name); return (-1); } array = *(t_array **)(((char *)data) + arrayonset); *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1); *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1); *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1); *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1); *elemtemplatesymp = elemtemplatesym; *arrayp = array; return (0); } /* get everything else you could possibly need about a plot, either for plot's own purposes or for plotting a "garray" */ int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, t_template **elemtemplatep, int *elemsizep, int *xonsetp, int *yonsetp, int *wonsetp) { int arrayonset, elemsize, yonset, wonset, xonset, type; t_template *elemtemplate; t_symbol *dummy; t_canvas *elemtemplatecanvas = 0; /* the "float" template is special in not having to have a canvas; template_findbyname is hardwired to return a predefined template. */ if (!(elemtemplate = template_findbyname(elemtemplatesym))) { error("plot: %s: no such template", elemtemplatesym->s_name); return (-1); } if (!((elemtemplatesym == &s_float) || (elemtemplatecanvas = template_findcanvas(elemtemplate)))) { error("plot: %s: no canvas for this template", elemtemplatesym->s_name); return (-1); } elemsize = elemtemplate->t_n * sizeof(t_word); if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy) || type != DT_FLOAT) yonset = -1; if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy) || type != DT_FLOAT) xonset = -1; if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy) || type != DT_FLOAT) wonset = -1; /* fill in slots for return values */ *elemtemplatecanvasp = elemtemplatecanvas; *elemtemplatep = elemtemplate; *elemsizep = elemsize; *xonsetp = xonset; *yonsetp = yonset; *wonsetp = wonset; return (0); } static void plot_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_plot *x = (t_plot *)z; int elemsize, yonset, wonset, xonset; t_canvas *elemtemplatecanvas; t_template *elemtemplate; t_symbol *elemtemplatesym; float linewidth, xloc, xinc, yloc; t_array *array; float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; int i; float xpix, ypix, wpix; if (!plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) && !array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) { for (i = 0; i < array->a_n; i++) { array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc, &xpix, &ypix, &wpix); if (xpix < x1) x1 = xpix; if (xpix > x2) x2 = xpix; if (ypix - wpix < y1) y1 = ypix - wpix; if (ypix + wpix > y2) y2 = ypix + wpix; } } *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void plot_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int dx, int dy) { /* not yet */ } static void plot_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { /* not yet */ } static void plot_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { /* not yet */ } static void plot_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int vis) { t_plot *x = (t_plot *)z; int elemsize, yonset, wonset, xonset; t_canvas *elemtemplatecanvas; t_template *elemtemplate; t_symbol *elemtemplatesym; float linewidth, xloc, xinc, yloc; t_array *array; int nelem; char *elem; if (plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) || array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) return; nelem = array->a_n; elem = (char *)array->a_vec; if (vis) { char outline[20]; int lastpixel = -1, ndrawn = 0; float xsum, yval = 0, wval = 0, xpix; int ixpix = 0, i; /* draw the trace */ numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), outline); if (wonset >= 0) { /* found "w" field which controls linewidth. The trace is a filled polygon with 2n points. */ sys_vgui(".x%lx.c create polygon \\\n", glist_getcanvas(glist)); for (i = 0, xsum = xloc; i < nelem; i++) { float usexloc; if (xonset >= 0) usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); else usexloc = xsum, xsum += xinc; if (yonset >= 0) yval = *(float *)((elem + elemsize * i) + yonset); else yval = 0; wval = *(float *)((elem + elemsize * i) + wonset); xpix = glist_xtopixels(glist, basex + usexloc); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist, basey + yloc + yval - wval)); ndrawn++; } lastpixel = ixpix; if (ndrawn >= 1000) goto ouch; } lastpixel = -1; for (i = nelem-1; i >= 0; i--) { float usexloc; if (xonset >= 0) usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); else xsum -= xinc, usexloc = xsum; if (yonset >= 0) yval = *(float *)((elem + elemsize * i) + yonset); else yval = 0; wval = *(float *)((elem + elemsize * i) + wonset); xpix = glist_xtopixels(glist, basex + usexloc); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist, basey + yloc + yval + wval)); ndrawn++; } lastpixel = ixpix; if (ndrawn >= 1000) goto ouch; } /* TK will complain if there aren't at least 3 points. There should be at least two already. */ if (ndrawn < 4) { sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, basey + yloc + yval + wval)); sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, basey + yloc + yval - wval)); } ouch: sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline); if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); sys_vgui("-tags plot%lx\n", data); } else if (linewidth > 0) { /* no "w" field. If the linewidth is positive, draw a segmented line with the requested width; otherwise don't draw the trace at all. */ sys_vgui(".x%lx.c create line \\\n", glist_getcanvas(glist)); for (xsum = xloc, i = 0; i < nelem; i++) { float usexloc; if (xonset >= 0) usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); else usexloc = xsum, xsum += xinc; if (yonset >= 0) yval = *(float *)((elem + elemsize * i) + yonset); else yval = 0; xpix = glist_xtopixels(glist, basex + usexloc); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist, basey + yloc + yval)); ndrawn++; } lastpixel = ixpix; if (ndrawn >= 1000) break; } /* TK will complain if there aren't at least 2 points... */ if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, basey + yloc + yval)); sys_vgui("-width %f\\\n", linewidth); sys_vgui("-fill %s\\\n", outline); if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); sys_vgui("-tags plot%lx\n", data); } /* We're done with the outline; now draw all the points. This code is inefficient since the template has to be searched for drawing instructions for every last point. */ for (xsum = xloc, i = 0; i < nelem; i++) { float usexloc, useyloc; t_gobj *y; if (xonset >= 0) usexloc = basex + xloc + *(float *)((elem + elemsize * i) + xonset); else usexloc = basex + xsum, xsum += xinc; if (yonset >= 0) yval = *(float *)((elem + elemsize * i) + yonset); else yval = 0; useyloc = basey + yloc + yval; for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) { t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentvisfn)(y, glist, (t_word *)(elem + elemsize * i), elemtemplate, usexloc, useyloc, vis); } } } else { /* un-draw the individual points */ int i; for (i = 0; i < nelem; i++) { t_gobj *y; for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) { t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentvisfn)(y, glist, (t_word *)(elem + elemsize * i), elemtemplate, 0, 0, 0); } } /* and then the trace */ sys_vgui(".x%lx.c delete plot%lx\n", glist_getcanvas(glist), data); } } static int plot_click(t_gobj *z, t_glist *glist, t_scalar *sc, t_template *template, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_plot *x = (t_plot *)z; t_symbol *elemtemplatesym; float linewidth, xloc, xinc, yloc; t_array *array; t_word *data = sc->sc_vec; if (!plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc)) { return (array_doclick(array, glist, &sc->sc_gobj, elemtemplatesym, linewidth, basex + xloc, xinc, basey + yloc, xpix, ypix, shift, alt, dbl, doit)); } else return (0); } t_parentwidgetbehavior plot_widgetbehavior = { plot_getrect, plot_displace, plot_select, plot_activate, plot_vis, plot_click, }; static void plot_setup(void) { plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0, sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0); class_setdrawcommand(plot_class); class_setparentwidget(plot_class, &plot_widgetbehavior); } /* ---------------- drawnumber: draw a number ---------------- */ /* drawnumbers draw numeric fields at controllable locations, with controllable color and label . invocation: (drawnumber|drawsymbol) variable x y color label */ t_class *drawnumber_class; #define DRAW_SYMBOL 1 typedef struct _drawnumber { t_object x_obj; t_fielddesc x_value; t_fielddesc x_xloc; t_fielddesc x_yloc; t_fielddesc x_color; t_symbol *x_label; int x_flags; } t_drawnumber; static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv) { t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class); char *classname = classsym->s_name; int flags = 0; if (classname[4] == 's') flags |= DRAW_SYMBOL; x->x_flags = flags; if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_value, 0); if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_xloc, 0); if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_yloc, 0); if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++); else FIELDDESC_SETFLOAT(&x->x_color, 1); if (argc) x->x_label = atom_getsymbolarg(0, argc, argv); else x->x_label = &s_; return (x); } /* -------------------- widget behavior for drawnumber ------------ */ #define DRAWNUMBER_BUFSIZE 80 static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap) { int nchars; strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE); buf[DRAWNUMBER_BUFSIZE - 1] = 0; nchars = strlen(buf); atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars); } static void drawnumber_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_drawnumber *x = (t_drawnumber *)z; t_atom at; int xloc = glist_xtopixels(glist, basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); int font = glist_getfont(glist); int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); char buf[DRAWNUMBER_BUFSIZE]; if (x->x_flags & DRAW_SYMBOL) 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); *xp1 = xloc; *yp1 = yloc; *xp2 = xloc + fontwidth * strlen(buf); *yp2 = yloc + fontheight; } static void drawnumber_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int dx, int dy) { /* refuse */ } static void drawnumber_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { post("drawnumber_select %d", state); /* fill in later */ } static void drawnumber_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int state) { post("drawnumber_activate %d", state); } static void drawnumber_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, float basex, float basey, int vis) { t_drawnumber *x = (t_drawnumber *)z; if (vis) { t_atom at; int xloc = glist_xtopixels(glist, basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); char colorstring[20], buf[DRAWNUMBER_BUFSIZE]; numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1), colorstring); if (x->x_flags & DRAW_SYMBOL) 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}", glist_getcanvas(glist), xloc, yloc, colorstring, buf); sys_vgui(" -font -*-courier-bold--normal--%d-*", sys_hostfontsize(glist_getfont(glist))); sys_vgui(" -tags drawnumber%lx\n", data); } else sys_vgui(".x%lx.c delete drawnumber%lx\n", glist_getcanvas(glist), data); } static float drawnumber_motion_ycumulative; static t_glist *drawnumber_motion_glist; static t_gobj *drawnumber_motion_gobj; static t_word *drawnumber_motion_wp; static t_template *drawnumber_motion_template; /* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy) { t_drawnumber *x = (t_drawnumber *)z; t_fielddesc *f = &x->x_value; drawnumber_motion_ycumulative -= dy; template_setfloat(drawnumber_motion_template, f->fd_un.fd_varsym, drawnumber_motion_wp, drawnumber_motion_ycumulative, 1); glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj); } static int drawnumber_click(t_gobj *z, t_glist *glist, t_scalar *sc, t_template *template, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_drawnumber *x = (t_drawnumber *)z; int x1, y1, x2, y2; t_word *data = sc->sc_vec; drawnumber_getrect(z, glist, sc->sc_vec, template, basex, basey, &x1, &y1, &x2, &y2); if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 && x->x_value.fd_var) { if (doit) { drawnumber_motion_glist = glist; drawnumber_motion_gobj = &sc->sc_gobj; drawnumber_motion_wp = data; drawnumber_motion_template = template; drawnumber_motion_ycumulative = fielddesc_getfloat(&x->x_value, template, data, 0); glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix); } return (1); } else return (0); } t_parentwidgetbehavior drawnumber_widgetbehavior = { drawnumber_getrect, drawnumber_displace, drawnumber_select, drawnumber_activate, drawnumber_vis, drawnumber_click, }; static void drawnumber_free(t_drawnumber *x) { } static void drawnumber_setup(void) { drawnumber_class = class_new(gensym("drawnumber"), (t_newmethod)drawnumber_new, (t_method)drawnumber_free, sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0); class_setdrawcommand(drawnumber_class); class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"), A_GIMME, 0); class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior); } /* ---------------------- setup function ---------------------------- */ void g_template_setup(void) { template_setup(); gtemplate_setup(); template_float.t_pdobj = template_class; curve_setup(); plot_setup(); drawnumber_setup(); }