From 57045df5fe3ec557e57dc7434ac1a07b5521bffc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 29 Jul 2002 17:06:19 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r58, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=59 --- pd/src/g_graph.c | 1119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1119 insertions(+) create mode 100644 pd/src/g_graph.c (limited to 'pd/src/g_graph.c') diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c new file mode 100644 index 00000000..65a5d056 --- /dev/null +++ b/pd/src/g_graph.c @@ -0,0 +1,1119 @@ +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file deals with the behavior of glists as either "text objects" or +"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c +to this file... */ + +#include +#include "m_pd.h" +#include "t_tk.h" +#include "g_canvas.h" +#include +#include + +/* ---------------------- forward definitions ----------------- */ + +static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis); +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); + +/* -------------------- maintaining the list -------------------- */ + +void glist_add(t_glist *x, t_gobj *y) +{ + y->g_next = 0; + if (!x->gl_list) x->gl_list = y; + else + { + t_gobj *y2; + for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next); + y2->g_next = y; + } + if (glist_isvisible(x)) + gobj_vis(y, x, 1); + if (class_isdrawcommand(y->g_pd)) + canvas_redrawallfortemplate(glist_getcanvas(x)); +} + + /* this is to protect against a hairy problem in which deleting + a sub-canvas might delete an inlet on a box, after the box had + been invisible-ized, so that we have to protect against redrawing it! */ +int canvas_setdeleting(t_canvas *x, int flag) +{ + int ret = x->gl_isdeleting; + x->gl_isdeleting = flag; + return (ret); +} + + /* delete an object from a glist and free it */ +void glist_delete(t_glist *x, t_gobj *y) +{ + t_gobj *g; + t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp")); + t_canvas *canvas = glist_getcanvas(x); + int drawcommand = class_isdrawcommand(y->g_pd); + int wasdeleting; + + wasdeleting = canvas_setdeleting(canvas, 1); + /* LATER decide whether all visible glists must have an editor? */ + if (glist_isvisible(canvas) && x->gl_editor) + { + if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0; + if (glist_isselected(x, y)) glist_deselect(x, y); + + /* HACK -- we had phantom outlets not getting erased on the + screen because the canvas_setdeleting() mechanism is too + crude. LATER carefully set up rules for when the rtexts + should exist, so that they stay around until all the + steps of becoming invisible are done. In the meantime, just + zap the inlets and outlets here... */ + if (pd_class(&y->g_pd) == canvas_class) + { + t_glist *gl = (t_glist *)y; + if (gl->gl_isgraph) + { + char tag[80]; + sprintf(tag, "graph%x", (int)gl); + glist_eraseiofor(x, &gl->gl_obj, tag); + } + else + { + text_eraseborder(&gl->gl_obj, x, + rtext_gettag(glist_findrtext(x, &gl->gl_obj))); + } + } + } + gobj_delete(y, x); + if (glist_isvisible(canvas)) gobj_vis(y, x, 0); + if (x->gl_list == y) x->gl_list = y->g_next; + else for (g = x->gl_list; g; g = g->g_next) + if (g->g_next == y) + { + g->g_next = y->g_next; + break; + } + pd_free(&y->g_pd); + if (chkdsp) canvas_update_dsp(); + if (drawcommand) canvas_redrawallfortemplate(canvas); + canvas_setdeleting(canvas, wasdeleting); + x->gl_valid = ++glist_valid; +} + + /* remove every object from a glist. Experimental. */ +void glist_clear(t_glist *x) +{ + t_gobj *y, *y2; + int dspstate = canvas_suspend_dsp(); + while (y = x->gl_list) + glist_delete(x, y); + canvas_resume_dsp(dspstate); +} + +void glist_retext(t_glist *glist, t_text *y) +{ + t_canvas *c = glist_getcanvas(glist); + /* check that we have built rtexts yet. LATER need a better test. */ + if (glist->gl_editor && glist->gl_editor->e_rtext) + { + t_rtext *rt = glist_findrtext(glist, y); + if (rt) + rtext_retext(rt); + } +} + +void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, + t_glistkeyfn keyfn, int xpos, int ypos) +{ + t_glist *x2 = glist_getcanvas(x); + x2->gl_editor->e_onmotion = MA_PASSOUT; + x2->gl_editor->e_grab = y; + x2->gl_editor->e_motionfn = motionfn; + x2->gl_editor->e_keyfn = keyfn; + x2->gl_editor->e_xwas = xpos; + x2->gl_editor->e_ywas = ypos; +} + +t_canvas *glist_getcanvas(t_glist *x) +{ + while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph) + x = x->gl_owner; + return((t_canvas *)x); +} + +static float gobj_getxforsort(t_gobj *g) +{ + if (pd_class(&g->g_pd) == scalar_class) + { + float x1, y1; + scalar_getbasexy((t_scalar *)g, &x1, &y1); + return(x1); + } + else return (0); +} + +static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2) +{ + t_gobj *g = 0, *g9 = 0; + float f1 = 0, f2 = 0; + if (g1) + f1 = gobj_getxforsort(g1); + if (g2) + f2 = gobj_getxforsort(g2); + while (1) + { + if (g1) + { + if (g2) + { + if (f1 <= f2) + goto put1; + else goto put2; + } + else goto put1; + } + else if (g2) + goto put2; + else break; + put1: + if (g9) + g9->g_next = g1, g9 = g1; + else g9 = g = g1; + if (g1 = g1->g_next) + f1 = gobj_getxforsort(g1); + g9->g_next = 0; + continue; + put2: + if (g9) + g9->g_next = g2, g9 = g2; + else g9 = g = g2; + if (g2 = g2->g_next) + f2 = gobj_getxforsort(g2); + g9->g_next = 0; + continue; + } + return (g); +} + +static t_gobj *glist_dosort(t_glist *x, + t_gobj *g, int nitems) +{ + if (nitems < 2) + return (g); + else + { + int n1 = nitems/2, n2 = nitems - n1, i; + t_gobj *g2, *g3; + for (g2 = g, i = n1-1; i--; g2 = g2->g_next) + ; + g3 = g2->g_next; + g2->g_next = 0; + g = glist_dosort(x, g, n1); + g3 = glist_dosort(x, g3, n2); + return (glist_merge(x, g, g3)); + } +} + +void glist_sort(t_glist *x) +{ + int nitems = 0, foo = 0; + float lastx = -1e37; + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + float x1 = gobj_getxforsort(g); + if (x1 < lastx) + foo = 1; + lastx = x1; + nitems++; + } + if (foo) + x->gl_list = glist_dosort(x, x->gl_list, nitems); +} + +void glist_cleanup(t_glist *x) +{ + freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel))); + freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel))); + gstub_cutoff(x->gl_stub); +} + +void glist_free(t_glist *x) +{ + glist_cleanup(x); + freebytes(x, sizeof(*x)); +} + +/* --------------- inlets and outlets ----------- */ + + +t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortinlets(x); + return (ip); +} + +void canvas_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp); + +void canvas_rminlet(t_canvas *x, t_inlet *ip) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + inlet_free(ip); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_inlet *vinlet_getit(t_pd *x); +extern void obj_moveinletfirst(t_object *x, t_inlet *i); + +void canvas_resortinlets(t_canvas *x) +{ + int ninlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (ninlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) ninlets++; + + if (ninlets < 2) return; + + vec = (t_gobj **)getbytes(ninlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y; + + for (i = ninlets; i--;) + { + t_inlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = vinlet_getit(&y->g_pd); + + obj_moveinletfirst(&x->gl_obj, ip); + } + freebytes(vec, ninlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_outlet *op = outlet_new(&x->gl_obj, s); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortoutlets(x); + return (op); +} + +void canvas_rmoutlet(t_canvas *x, t_outlet *op) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + + outlet_free(op); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_outlet *voutlet_getit(t_pd *x); +extern void obj_moveoutletfirst(t_object *x, t_outlet *i); + +void canvas_resortoutlets(t_canvas *x) +{ + int noutlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (noutlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) noutlets++; + + if (noutlets < 2) return; + + vec = (t_gobj **)getbytes(noutlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y; + + for (i = noutlets; i--;) + { + t_outlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = voutlet_getit(&y->g_pd); + + obj_moveoutletfirst(&x->gl_obj, ip); + } + freebytes(vec, noutlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +/* ----------calculating coordinates and controlling appearance --------- */ + + +static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1, + t_floatarg x2, t_floatarg y2) +{ + x->gl_x1 = x1; + x->gl_x2 = x2; + x->gl_y1 = y1; + x->gl_y2 = y2; + if (x->gl_x2 == x->gl_x1 || + x->gl_y2 == x->gl_y1) + { + error("graph: empty bounds rectangle"); + x1 = y1 = 0; + x2 = y2 = 1; + } + glist_redraw(x); +} + +static void graph_xticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_xtick.k_point = point; + x->gl_xtick.k_inc = inc; + x->gl_xtick.k_lperb = f; + glist_redraw(x); +} + +static void graph_yticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_ytick.k_point = point; + x->gl_ytick.k_inc = inc; + x->gl_ytick.k_lperb = f; + glist_redraw(x); +} + +static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_xlabel: no y value given"); + else + { + x->gl_xlabely = atom_getfloat(argv); + argv++; argc--; + x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel, + x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nxlabels = argc; + for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_ylabel: no x value given"); + else + { + x->gl_ylabelx = atom_getfloat(argv); + argv++; argc--; + x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel, + x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nylabels = argc; + for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +/****** routines to convert pixels to X or Y value and vice versa ******/ + + /* convert an x pixel value to an x coordinate value */ +float glist_pixelstox(t_glist *x, float xpix) +{ + /* if we appear as a text box on parent, our range in our + coordinates (x1, etc.) specifies the coordinate range + of a one-pixel square at top left of the window. */ + if (!x->gl_isgraph) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix); + + /* if we're a graph when shown on parent, but own our own + window right now, our range in our coordinates (x1, etc.) is spread + over the visible window size, given by screenx1, etc. */ + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix) / (x->gl_screenx2 - x->gl_screenx1)); + + /* otherwise, we appear in a graph within a parent glist, + so get our screen rectangle on parent and transform. */ + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix - x1) / (x2 - x1)); + } +} + +float glist_pixelstoy(t_glist *x, float ypix) +{ + if (!x->gl_isgraph) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix) / (x->gl_screeny2 - x->gl_screeny1)); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix - y1) / (y2 - y1)); + } +} + + /* convert an x coordinate value to an x pixel location in window */ +float glist_xtopixels(t_glist *x, float xval) +{ + if (!x->gl_isgraph) + return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screenx2 - x->gl_screenx1) * + (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + } +} + +float glist_ytopixels(t_glist *x, float yval) +{ + if (!x->gl_isgraph) + return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screeny2 - x->gl_screeny1) * + (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + } +} + + /* convert an X screen distance to an X coordinate increment. + This is terribly inefficient; + but probably not a big enough CPU hog to warrant optimizing. */ +float glist_dpixtodx(t_glist *x, float dxpix) +{ + return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0))); +} + +float glist_dpixtody(t_glist *x, float dypix) +{ + return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0))); +} + + /* get the window location in pixels of a "text" object. The + object's x and y positions are in pixels when the glist they're + in is toplevel. If it's not, we convert to pixels on the parent + window. */ +int text_xpix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_xpix); + else return (glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1))); +} + +int text_ypix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_ypix); + else return (glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1))); +} + + /* redraw all the items in a glist. We construe this to mean + redrawing in its own window and on parent, as needed in each case. + This is too conservative -- for instance, when you draw an "open" + rectangle on the parent, you shouldn't have to redraw the window! */ +void glist_redraw(t_glist *x) +{ + if (glist_isvisible(x)) + { + /* LATER fix the graph_vis() code to handle both cases */ + if (glist_istoplevel(x)) + { + t_gobj *g; + t_linetraverser t; + t_outconnect *oc; + for (g = x->gl_list; g; g = g->g_next) + { + gobj_vis(g, x, 0); + gobj_vis(g, x, 1); + } + /* redraw all the lines */ + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + sys_vgui(".x%x.c coords l%x %d %d %d %d\n", + glist_getcanvas(x), oc, + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); + } + if (x->gl_owner) + { + graph_vis(&x->gl_gobj, x->gl_owner, 0); + graph_vis(&x->gl_gobj, x->gl_owner, 1); + } + } +} + +t_class *graph_class; + +/* --------------------------- widget behavior ------------------- */ + +extern t_widgetbehavior text_widgetbehavior; + + /* Note that some code in here would also be useful for drawing + graph decorations in toplevels... */ +static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis) +{ + t_glist *x = (t_glist *)gr; + char tag[50]; + t_gobj *g; + int x1, y1, x2, y2; + /* ordinary subpatches: just act like a text object */ + if (!x->gl_isgraph) + { + text_widgetbehavior.w_visfn(gr, parent_glist, vis); + return; + } + + if (vis) + rtext_new(parent_glist, &x->gl_obj, + parent_glist->gl_editor->e_rtext, + canvas_showtext(x)); + graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2); + if (!vis) + rtext_free(glist_findrtext(parent_glist, &x->gl_obj)); + + sprintf(tag, "graph%x", (int)x); + if (vis) + glist_drawiofor(parent_glist, &x->gl_obj, 1, + tag, x1, y1, x2, y2); + else glist_eraseiofor(parent_glist, &x->gl_obj, tag); + /* if we look like a graph but have been moved to a toplevel, + just show the bounding rectangle */ + if (x->gl_havewindow) + { + if (vis) + { + sys_vgui(".x%x.c create polygon\ + %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + } + return; + } + /* otherwise draw (or erase) us as a graph inside another glist. */ + if (vis) + { + int i; + float f; + + /* draw a rectangle around the graph */ + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + + /* draw ticks on horizontal borders. If lperb field is + zero, this is disabled. */ + if (x->gl_xtick.k_lperb) + { + float upix, lpix; + if (y2 < y1) + upix = y1, lpix = y2; + else upix = y2, lpix = y1; + for (i = 0, f = x->gl_xtick.k_point; + f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++, + f += x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc; + f > 0.99 * x->gl_x1 + 0.01*x->gl_x2; + i++, f -= x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + } + + /* draw ticks in vertical borders*/ + if (x->gl_ytick.k_lperb) + { + float ubound, lbound; + if (x->gl_y2 < x->gl_y1) + ubound = x->gl_y1, lbound = x->gl_y2; + else ubound = x->gl_y2, lbound = x->gl_y1; + for (i = 0, f = x->gl_ytick.k_point; + f < 0.99 * ubound + 0.01 * lbound; + i++, f += x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc; + f > 0.99 * lbound + 0.01 * ubound; + i++, f -= x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + } + /* draw x labels */ + for (i = 0; i < x->gl_nxlabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)), + (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name, + glist_getfont(x), tag); + + /* draw y labels */ + for (i = 0; i < x->gl_nylabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, x->gl_ylabelx), + (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)), + x->gl_ylabel[i]->s_name, + glist_getfont(x), tag); + + /* draw contents of graph as glist */ + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 1); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 0); + } +} + + /* get the graph's rectangle, not counting extra swelling for controls + to keep them inside the graph. This is the "logical" pixel size. */ + +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_glist *x = (t_glist *)z; + int x1 = text_xpix(&x->gl_obj, glist); + int y1 = text_ypix(&x->gl_obj, glist); + int x2, y2; +#if 0 /* this used to adjust graph size when it was in another graph; + now we just preserve the size. */ + /* same logic here as in text_xpix(): */ + if (glist->gl_havewindow) + { + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + } + else + { + x2 = glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + (x->gl_obj.te_xpix + x->gl_pixwidth) / + (glist->gl_screenx2 - glist->gl_screenx1)); + y2 = glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + (x->gl_obj.te_ypix + x->gl_pixheight) / + (glist->gl_screeny2 - glist->gl_screeny1)); + } +#endif + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + + /* get the rectangle, enlarged to contain all the "contents" -- + meaning their formal bounds rectangles. */ +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + t_glist *x = (t_glist *)z; + if (x->gl_isgraph) + { + int hadwindow; + t_gobj *g; + t_text *ob; + int x21, y21, x22, y22; + + graph_graphrect(z, glist, &x1, &y1, &x2, &y2); + if (canvas_showtext(x)) + { + text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + /* lie about whether we have our own window to affect gobj_getrect + calls below. (LATER add argument to gobj_getrect()?) */ + hadwindow = x->gl_havewindow; + x->gl_havewindow = 0; + for (g = x->gl_list; g; g = g->g_next) + if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x)) + { + /* don't do this for arrays, just let them hang outsize the + box. */ + if (pd_class(&g->g_pd) == garray_class) + continue; + gobj_getrect(g, x, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + x->gl_havewindow = hadwindow; + } + else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2); + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_displacefn(z, glist, dx, dy); + else + { + x->gl_obj.te_xpix += dx; + x->gl_obj.te_ypix += dy; + glist_redraw(x); + canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj); + } +} + +static void graph_select(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_selectfn(z, glist, state); + else + { + t_rtext *y = glist_findrtext(glist, &x->gl_obj); + if (canvas_showtext(x)) + rtext_select(y, state); + sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist, + rtext_gettag(y), (state? "blue" : "black")); + sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n", + glist_getcanvas(glist), z, (state? "blue" : "black")); + } +} + +static void graph_activate(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (canvas_showtext(x)) + text_widgetbehavior.w_activatefn(z, glist, state); +} + +#if 0 +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_deletefn(z, glist); + else + { + t_gobj *y; + while (y = x->gl_list) glist_delete(x, y); +#if 0 /* I think this was just wrong. */ + if (glist_isvisible(x)) + sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x); +#endif + } +} +#endif + +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + text_widgetbehavior.w_deletefn(z, glist); + while (y = x->gl_list) + glist_delete(x, y); +} + +static float graph_lastxpix, graph_lastypix; + +static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_glist *x = (t_glist *)z; + float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy; + t_garray *a = (t_garray *)(x->gl_list); + int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix); + int newx = 0.5 + glist_pixelstox(x, newxpix); + t_float *vec; + int nelem, i; + float oldy = glist_pixelstoy(x, graph_lastypix); + float newy = glist_pixelstoy(x, newypix); + graph_lastxpix = newxpix; + graph_lastypix = newypix; + /* verify that the array is OK */ + if (!a || pd_class((t_pd *)a) != garray_class) + return; + if (!garray_getfloatarray(a, &nelem, &vec)) + return; + if (oldx < 0) oldx = 0; + if (oldx >= nelem) + oldx = nelem - 1; + if (newx < 0) newx = 0; + if (newx >= nelem) + newx = nelem - 1; + if (oldx < newx - 1) + { + for (i = oldx + 1; i <= newx; i++) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else if (oldx > newx + 1) + { + for (i = oldx - 1; i >= newx; i--) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else vec[newx] = newy; + garray_redraw(a); +} + +static int graph_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + int clickreturned = 0; + if (!x->gl_isgraph) + return (text_widgetbehavior.w_clickfn(z, glist, + xpix, ypix, shift, alt, dbl, doit)); + else if (x->gl_havewindow) + return (0); + else + { + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpix, ypix, + shift, alt, 0, doit))) + break; + } + if (!doit) + { + if (y) + canvas_setcursor(glist_getcanvas(x), clickreturned); + else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING); + } + return (clickreturned); + } +} + +static void graph_save(t_gobj *z, t_binbuf *b) +{ + t_glist *x = (t_glist *)z; + text_widgetbehavior.w_savefn(z, b); +} + +void garray_properties(t_garray *x); + +static void graph_properties(t_gobj *z, t_glist *owner) +{ + t_glist *x = (t_glist *)z; + { + t_gobj *y; + char graphbuf[200]; + sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n", + x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, + x->gl_pixwidth, x->gl_pixheight); + gfxstub_new(&x->gl_pd, x, graphbuf); + + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == garray_class) + garray_properties((t_garray *)y); + } +} + +t_widgetbehavior graph_widgetbehavior = +{ + graph_getrect, + graph_displace, + graph_select, + graph_activate, + graph_delete, + graph_vis, + graph_click, + graph_save, + graph_properties, +}; + + /* find the graph most recently added to this glist; + if none exists, return 0. */ + +t_glist *glist_findgraph(t_glist *x) +{ + t_gobj *y = 0, *z; + for (z = x->gl_list; z; z = z->g_next) + if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph) + y = z; + return ((t_glist *)y); +} + + /* message back from dialog GUI to set parameters. Args are: + 1-4: bounds in our coordinates; 5-6: size in parent */ +static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + t_float x1 = atom_getfloatarg(0, argc, argv); + t_float y1 = atom_getfloatarg(1, argc, argv); + t_float x2 = atom_getfloatarg(2, argc, argv); + t_float y2 = atom_getfloatarg(3, argc, argv); + t_float xpix = atom_getfloatarg(4, argc, argv); + t_float ypix = atom_getfloatarg(5, argc, argv); + if (x1 != x->gl_x1 || x2 != x->gl_x2 || + y1 != x->gl_y1 || y2 != x->gl_y2) + graph_bounds(x, x1, y1, x2, y2); + if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight) + { + x->gl_pixwidth = xpix; + x->gl_pixheight = ypix; + glist_redraw(x); + if (x->gl_owner) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern void canvas_menuarray(t_glist *canvas); + +void g_graph_setup(void) +{ + class_setwidget(canvas_class, &graph_widgetbehavior); + class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_array, gensym("array"), + A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_menuarray, + gensym("menuarray"), A_NULL); + class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)glist_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_sort, + gensym("sort"), A_NULL); +} -- cgit v1.2.1