Skip to content
Snippets Groups Projects
g_array.c 70.8 KiB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
                    }
                }
                else
                {
                    fielddesc_setcoord(array_motion_yfield,
                        array_motion_template, thisword,
                            ywas + dy * array_motion_yperpix, 1);
                }
            }
        }
    }
    else if (array_motion_yfield)
    {
            /* a y-only plot. */
        int thisx = array_motion_initx + array_motion_xcumulative + 0.5, x2;
        int increment, i, nchange;
        t_float newy = array_motion_ycumulative,
Miller Puckette's avatar
Miller Puckette committed
            oldy = fielddesc_getcoord(array_motion_yfield,
                array_motion_template,
                    (t_word *)(((char *)array_motion_wp) +
                        array_motion_elemsize * array_motion_lastx),
                            1);
        // here we block scalar from exceeding the array GOP edges
        // LATER: see if we need to do the same for an x plot
        if (graph)
        {
            if (graph->gl_y1 > graph->gl_y2)
            {
                if (newy > graph->gl_y1) newy = graph->gl_y1;
                if (newy < graph->gl_y2) newy = graph->gl_y2;
            }
            else
            {
                if (newy < graph->gl_y1) newy = graph->gl_y1;
                if (newy > graph->gl_y2) newy = graph->gl_y2;
            }
        }
        //fprintf(stderr, "y = %f\n", newy);
        t_float ydiff = newy - oldy;
Miller Puckette's avatar
Miller Puckette committed
        if (thisx < 0) thisx = 0;
        else if (thisx >= array_motion_npoints)
            thisx = array_motion_npoints - 1;
        increment = (thisx > array_motion_lastx ? -1 : 1);
        nchange = 1 + increment * (array_motion_lastx - thisx);

        for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment)
        {
            fielddesc_setcoord(array_motion_yfield,
                array_motion_template,
                    (t_word *)(((char *)array_motion_wp) +
                        array_motion_elemsize * x2), newy, 1);
            if (nchange > 1)
                newy -= ydiff * (1./(nchange - 1));
         }
         array_motion_lastx = thisx;
    }
    //fprintf(stderr, "%f %f\n", graph->gl_y1, graph->gl_y2);
Miller Puckette's avatar
Miller Puckette committed
    if (array_motion_scalar)
        scalar_redraw(array_motion_scalar, array_motion_glist);
    if (array_motion_array)
        array_redraw(array_motion_array, array_motion_glist);
    /* send a bang to the associated send to reflect the change
       via mouse click/drag */
    if (graph && array_garray->x_send->s_thing)
        pd_bang(array_garray->x_send->s_thing);
Miller Puckette's avatar
Miller Puckette committed
}

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,
Miller Puckette's avatar
Miller Puckette committed
    int shift, int alt, int dbl, int doit);
    /* try clicking on an element of the array as a scalar (if clicking
    on the trace of the array failed) */
static int array_doclick_element(t_array *array, t_glist *glist,
    t_scalar *sc, t_array *ap,
    t_symbol *elemtemplatesym,
    t_float linewidth, t_float xloc, t_float xinc, t_float yloc,
Miller Puckette's avatar
Miller Puckette committed
    t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield,
    int xpix, int ypix, int shift, int alt, int dbl, int doit)
{
    //fprintf(stderr,"array_doclick_element linewidth%f xloc%f xinc%f yloc%f xpix%d ypix%d doit%d\n", linewidth, xloc, xinc, yloc, xpix, ypix, doit);
Miller Puckette's avatar
Miller Puckette committed
    t_canvas *elemtemplatecanvas;
    t_template *elemtemplate;
    int elemsize, yonset, wonset, xonset, i, incr, hit;
Miller Puckette's avatar
Miller Puckette committed

    if (elemtemplatesym == &s_float)
        return (0);
    if (array_getfields(elemtemplatesym, &elemtemplatecanvas,
        &elemtemplate, &elemsize, xfield, yfield, wfield,
            &xonset, &yonset, &wonset))
                return (0);
        /* if it has more than 2000 points, just check 300 of them. */
    if (array->a_n < 2000)
        incr = 1;
    else incr = array->a_n / 300;
    for (i = 0, xsum = 0; i < array->a_n; i += incr)
    {
        t_float usexloc, useyloc;
Miller Puckette's avatar
Miller Puckette committed
        if (xonset >= 0)
            usexloc = xloc + fielddesc_cvttocoord(xfield, 
                *(t_float *)(((char *)(array->a_vec) + elemsize * i) + xonset));
Miller Puckette's avatar
Miller Puckette committed
        else usexloc = xloc + xsum, xsum += xinc;
        useyloc = yloc + (yonset >= 0 ? fielddesc_cvttocoord(yfield,
            *(t_float *)(((char *)(array->a_vec) + elemsize * i) + yonset)) : 0);
Miller Puckette's avatar
Miller Puckette committed
        
        if (hit = scalar_doclick(
            (t_word *)((char *)(array->a_vec) + i * elemsize),
            elemtemplate, 0, array,
            glist, usexloc, useyloc,
            xpix, ypix, shift, alt, dbl, doit))
                return (hit);
    }
    return (0);
}

    /* LATER move this and others back into plot parentwidget code, so
    they can be static (look in g_canvas.h for candidates). */
int array_doclick(t_array *array, t_glist *glist, t_scalar *sc, t_array *ap,
    t_symbol *elemtemplatesym,
    t_float linewidth, t_float xloc, t_float xinc, t_float yloc, t_float scalarvis,
Miller Puckette's avatar
Miller Puckette committed
    t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield,
    int xpix, int ypix, int shift, int alt, int dbl, int doit)
{
    //fprintf(stderr,"array_doclick linewidth%f xloc%f xinc%f yloc%f xpix%d ypix%d doit%d\n", linewidth, xloc, xinc, yloc, xpix, ypix, doit);
Miller Puckette's avatar
Miller Puckette committed
    t_canvas *elemtemplatecanvas;
    t_template *elemtemplate;
    int elemsize, yonset, wonset, xonset, i;

    if (!array_getfields(elemtemplatesym, &elemtemplatecanvas,
        &elemtemplate, &elemsize, xfield, yfield, wfield,
        &xonset, &yonset, &wonset))
    {
Miller Puckette's avatar
Miller Puckette committed
            /* if it has more than 2000 points, just check 1000 of them. */
        int incr = (array->a_n <= 2000 ? 1 : array->a_n / 1000);
        t_float pxpix1, pxpix2, pypix, pwpix, dx, dy, dy2, dy3;
Miller Puckette's avatar
Miller Puckette committed
        for (i = 0; i < array->a_n; i += incr)
        {
            array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
                xonset, yonset, wonset, i, xloc, yloc, xinc,
                xfield, yfield, wfield, &pxpix1, &pxpix2, &pypix, &pwpix, 1);
            //fprintf(stderr,"    array_getcoordinate %d: pxpix1:%f pxpix2:%f pypix:%f pwpix:%f dx:%f dy:%f elemsize:%d yonset:%d wonset:%d xonset:%d xloc:%f yloc:%f xinc:%f\n", i, pxpix1, pxpix2, pypix, pwpix, dx, dy, elemsize, yonset, wonset, xonset, xloc, yloc, xinc);
            // increased following on 20140830 to 8 and updated array_getcoordinate
            // so that the smallest hitbox is always at least 8x8--check with
            // all_about_arrays.pd inside custom scalars in an array
            if (pwpix < 8)
                pwpix = 8;
            if (xpix >= (int)pxpix1-pwpix && xpix <= (int)pxpix2+pwpix &&
                ((array_joc) ||
                 (ypix >= pypix-pwpix && ypix <= pypix+pwpix)))
            {
                best = i;
                break;
            }
Miller Puckette's avatar
Miller Puckette committed
            if (dx < 0) dx = -dx;
            if (dx > pxpix2-pxpix1) //this is the arbitrary radius away from the actual object's center, originally 8
Miller Puckette's avatar
Miller Puckette committed
                continue;   
            dy = pypix - ypix;
            if (dy < 0) dy = -dy;
            if (dx + dy < best)
                best = dx + dy;
            if (wonset >= 0)
            {
                dy = (pypix + pwpix) - ypix;
                if (dy < 0) dy = -dy;
                if (dx + dy < best)
                    best = dx + dy;
                dy = (pypix - pwpix) - ypix;
                if (dy < 0) dy = -dy;
                if (dx + dy < best)
                    best = dx + dy;
            }
            //fprintf(stderr,"    1st %f %f %f %f %f %d %d %d %d %d\n", pxpix, pypix, pwpix, dx, dy, elemsize, yonset, wonset, xonset, i);*/
        //fprintf(stderr,"    best = %f\n", best);
        if (best == -1 && (array_joc == 0)) //this is the arbitrary radius away from the actual object's center, originally 8
            //fprintf(stderr,"    best > 8\n");
            if (scalarvis != 0)
            {
                //fprintf(stderr,"    array_doclick_element\n");
Miller Puckette's avatar
Miller Puckette committed
                return (array_doclick_element(array, glist, sc, ap,
                    elemtemplatesym, linewidth, xloc, xinc, yloc,
                        xfield, yfield, wfield,
                        xpix, ypix, shift, alt, dbl, doit));
            }
            else
            {
                //fprintf(stderr,"    return 0\n");
                return (0);
            }
        //best += 0.001;  /* add truncation error margin */
        //for (i = 0; i < array->a_n; i += incr)
        //{
            //array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
            //    xonset, yonset, wonset, i, xloc, yloc, xinc,
            //    xfield, yfield, wfield, &pxpix1, &pxpix2, &pypix, &pwpix, 1);
            //dx = pxpix1 - xpix;
            //if (dx < 0) dx = -dx;
Miller Puckette's avatar
Miller Puckette committed
            dy = pypix - ypix;
            if (dy < 0) dy = -dy;
            if (wonset >= 0)
            {
                dy2 = (pypix + pwpix) - ypix;
                if (dy2 < 0) dy2 = -dy2;
                dy3 = (pypix - pwpix) - ypix;
                if (dy3 < 0) dy3 = -dy3;
                if (yonset < 0)
                    dy = 100;
            }
            else dy2 = dy3 = 100;
            //fprintf(stderr,"    2nd %f %f %f %f %f %f %f %d %d %d %d %d\n", pxpix, pypix, pwpix, dx, dy, dy2, dy3, elemsize, yonset, wonset, xonset, i);
            //if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best)
            //{
            //fprintf(stderr, "dy=%f dy2=%f dy3=%f\n", dy, dy2, dy3);

/* from array-rev */

            int hit = 0;
            if(array_joc)
            {
                    hit = (xpix >= pxpix1) && (xpix < pxpix2);
            }
            else
                hit = dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best;
/* end array-rev */

Miller Puckette's avatar
Miller Puckette committed
                    array_motion_fatten = 0;
                    //fprintf(stderr,"A\n");
                }
                else if (dy2 < dy3)
                {
Miller Puckette's avatar
Miller Puckette committed
                    array_motion_fatten = -1;
                    //fprintf(stderr,"B\n");
                }
                else if (!array_joc)
                {
                    array_motion_fatten = 1;
                    //fprintf(stderr,"C\n");
                }
                if (doit || (glob_lmclick && array_joc))
Miller Puckette's avatar
Miller Puckette committed
                {
                    char *elem = (char *)array->a_vec;
                    array_motion_elemsize = elemsize;
                    array_motion_glist = glist;
                    array_motion_scalar = sc;
                    array_motion_array = ap;
                    array_motion_template = elemtemplate;
                    array_motion_xperpix = glist_dpixtodx(glist, 1);
                    array_motion_yperpix = glist_dpixtody(glist, 1);
Miller Puckette's avatar
Miller Puckette committed
                    {
                        if (array->a_n <= 1)
                            return (0);
                        memmove((char *)(array->a_vec) + elemsize * i, 
                            (char *)(array->a_vec) + elemsize * (i+1),
                                (array->a_n - 1 - i) * elemsize);
                        //array_resize_and_redraw(array, glist, array->a_n - 1);
                        garray_resize(array_garray, array->a_n - 1);
                        canvas_setcursor(glist_getcanvas(glist), 0);
Miller Puckette's avatar
Miller Puckette committed
                        return (0);
                    }
Miller Puckette's avatar
Miller Puckette committed
                    {
                        /* add a point (after the clicked-on one) */
                        //fprintf(stderr,"add a point\n");
                        //array_resize_and_redraw(array, glist, array->a_n + 1);
Miller Puckette's avatar
Miller Puckette committed
                        elem = (char *)array->a_vec;
                        memmove(elem + elemsize * (i+1), 
                            elem + elemsize * i,
                                (array->a_n - i - 1) * elemsize);
                        i++;
                        garray_resize(array_garray, array->a_n + 1);
                        canvas_setcursor(glist_getcanvas(glist), 0);
Miller Puckette's avatar
Miller Puckette committed
                    }
                    if (xonset >= 0)
                    {
Miller Puckette's avatar
Miller Puckette committed
                        array_motion_xfield = xfield;
                        array_motion_xcumulative = 
                            fielddesc_getcoord(xfield, array_motion_template,
                                (t_word *)(elem + i * elemsize), 1);
                            array_motion_wp = (t_word *)(elem + i * elemsize);
                        if (shift)
                            array_motion_npoints = array->a_n - i;
                        else array_motion_npoints = 1;
                    }
                    else
                    {
Miller Puckette's avatar
Miller Puckette committed
                        array_motion_xfield = 0;
                        array_motion_xcumulative = 0;
                        array_motion_wp = (t_word *)elem;
                        array_motion_npoints = array->a_n;

                        array_motion_initx = i;
                        array_motion_lastx = i;
                        array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc);
                    }
                    if (array_motion_fatten)
                    {
Miller Puckette's avatar
Miller Puckette committed
                        array_motion_yfield = wfield;
                        array_motion_ycumulative = 
                            fielddesc_getcoord(wfield, array_motion_template,
                                (t_word *)(elem + i * elemsize), 1);
                        array_motion_yperpix *= -array_motion_fatten;
                    }
                    else if (yonset >= 0)
                    {
Miller Puckette's avatar
Miller Puckette committed
                        array_motion_yfield = yfield;
Miller Puckette's avatar
Miller Puckette committed
                            fielddesc_getcoord(yfield, array_motion_template,
                                (t_word *)(elem + i * elemsize), 1);
                            /* *(t_float *)((elem + elemsize * i) + yonset); */
                        //if (array_joc) {
                        // we do cursor detection based on the kind of a graph.
                        // (e.g. 3-point PLOYSTYLE_POINTS creates a graph that has 4 delimiting points,
                        // while a 3-point PLOTSTYLE_POLY creates a graph that has only 3 delimiting points)
                        // This, therefore takes into account whether we should count the center point
                        // of a bar or the starting point (poly) as our reference
                        // TODO: see if we can reimplement Bezier curves
                        if (array_garray != NULL && (array_garray->x_style == PLOTSTYLE_POLY || array_garray->x_style == PLOTSTYLE_BEZ))
                            array_motion(0, xpix - pxpix1, ypix - pypix);
                        else
                            array_motion(0, (xpix - (pxpix1 + (pxpix2 - pxpix1)/2)), ypix - pypix);
                        //}
                        //else {
                        //    array_motion(0, (xpix - (pxpix1 + (pxpix2 - pxpix1)/2)), ypix - pypix);
                        //}
                        //fprintf(stderr, "xpix:%d pxpix1:%f half:%f result:%f\n", xpix, pxpix1, (pxpix2-pxpix1)/2, xpix - (pxpix1 + (pxpix2 - pxpix1)/2));
Miller Puckette's avatar
Miller Puckette committed
                        array_motion_yfield = 0;
                        array_motion_ycumulative = 0;
                    }
                    //fprintf(stderr,"    glist_grab %d %d\n", xpix, ypix);
Miller Puckette's avatar
Miller Puckette committed
                    glist_grab(glist, 0, array_motion, 0, xpix, ypix);
                    //fprintf(stderr,"    VALUES: array_motion_initx:%f array_motion_lastx:%d array_motion_xperpix:%f array_motion_xcumulative:%f\n", array_motion_initx, array_motion_lastx, array_motion_xperpix, array_motion_xcumulative);
                    //fprintf(stderr,"    array_getcoordinate %d: pxpix1:%f pxpix2:%f pypix:%f pwpix:%f dx:%f dy:%f elemsize:%d yonset:%d wonset:%d xonset:%d xloc:%f yloc:%f xinc:%f\n", i, pxpix1, pxpix2, pypix, pwpix, dx, dy, elemsize, yonset, wonset, xonset, xloc, yloc, xinc);

Miller Puckette's avatar
Miller Puckette committed
                }
                if (alt)
                {
                    return (CURSOR_EDITMODE_DISCONNECT);
                else if (shift)
                {
                    return (CURSOR_RUNMODE_ADDPOINT);
                }
Miller Puckette's avatar
Miller Puckette committed
                else return (array_motion_fatten ?
                    CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME);
Miller Puckette's avatar
Miller Puckette committed
    }
    return (0);
}

static void array_getrect(t_array *array, t_glist *glist,
    int *xp1, int *yp1, int *xp2, int *yp2)
{
    //fprintf(stderr,"array getrect %d %d\n", glist_istoplevel(glist), (array_joc != 0 ? 1 : 0));
    t_float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
Miller Puckette's avatar
Miller Puckette committed
    t_canvas *elemtemplatecanvas;
    t_template *elemtemplate;
    int elemsize, yonset, wonset, xonset, i;
Miller Puckette's avatar
Miller Puckette committed
    if (!array_getfields(array->a_templatesym, &elemtemplatecanvas,
        &elemtemplate, &elemsize, 0, 0, 0, &xonset, &yonset, &wonset))
    {
        int incr;
            /* if it has more than 2000 points, just check 300 of them. */
        if (array->a_n < 2000)
            incr = 1;
        else incr = array->a_n / 300;
        for (i = 0; i < array->a_n; i += incr)
        {
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
            t_float pxpix1, pxpix2, pypix, pwpix;
Miller Puckette's avatar
Miller Puckette committed
            array_getcoordinate(glist, (char *)(array->a_vec) +
                i * elemsize,
                xonset, yonset, wonset, i, 0, 0, 1,
                0, 0, 0,
                &pxpix1, &pxpix2, &pypix, &pwpix, 1);
Miller Puckette's avatar
Miller Puckette committed
            if (pwpix < 2)
                pwpix = 2;
            if (pxpix1 < x1)
                x1 = pxpix1;
            if (pxpix2 > x2)
                x2 = pxpix2;
Miller Puckette's avatar
Miller Puckette committed
            if (pypix - pwpix < y1)
                y1 = pypix - pwpix;
            if (pypix + pwpix > y2)
                y2 = pypix + pwpix;
        }
    }
    //fprintf(stderr,"array_getrect %f %f %f %f\n", x1, y1, x2, y2);
Miller Puckette's avatar
Miller Puckette committed
    *xp1 = x1;
    *yp1 = y1;
    *xp2 = x2;
    *yp2 = y2;
}

/* -------------------- widget behavior for garray ------------ */

static void garray_getrect(t_gobj *z, t_glist *glist,
    int *xp1, int *yp1, int *xp2, int *yp2)
{
Miller Puckette's avatar
Miller Puckette committed
    t_garray *x = (t_garray *)z;
    gobj_getrect(&x->x_scalar->sc_gobj, glist, xp1, yp1, xp2, yp2);
}

static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy)
{
    /* refuse */
}

static void garray_select(t_gobj *z, t_glist *glist, int state)
{
    //fprintf(stderr,">>>>>>>>>>>>garray_select %d\n", state);
    t_garray *x = (t_garray *)z;
    sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
        glist_getcanvas(glist), x->x_glist, state);

    scalar_select((t_gobj *)x->x_scalar, glist, state);
Miller Puckette's avatar
Miller Puckette committed
}

static void garray_activate(t_gobj *z, t_glist *glist, int state)
{
}

static void garray_delete(t_gobj *z, t_glist *glist)
{
    /* nothing to do */
}

Miller Puckette's avatar
Miller Puckette committed
static void garray_vis(t_gobj *z, t_glist *glist, int vis)
{
    //fprintf(stderr,"garray_vis %d\n", vis);
Miller Puckette's avatar
Miller Puckette committed
    t_garray *x = (t_garray *)z;

    if (vis) {
        t_array *a = garray_getarray(x);
        int ne = a->a_n;
        int n = (x->x_style == PLOTSTYLE_POINTS ||
            x->x_style == PLOTSTYLE_BARS || ne == 1 ? ne : ne-1);
        //fprintf(stderr,"garray_vis glist->gl_x2=%g n=%d a->a_n=%d\n", glist->gl_x2, n, a->a_n);
        //if (glist->gl_x2 != n) {
        glist->gl_x2 = n;
        do_not_redraw = 1;
        garray_fittograph(x, n, -1);
        do_not_redraw = 0;
    gobj_vis(&x->x_scalar->sc_gobj, glist, vis);
    //if (((t_glist *)z)->gl_isgraph)
    //    fprintf(stderr,"garray_vis am_graph\n");
Miller Puckette's avatar
Miller Puckette committed
}

static int garray_click(t_gobj *z, t_glist *glist,
    int xpix, int ypix, int shift, int alt, int dbl, int doit)
{
    t_garray *x = (t_garray *)z;
Miller Puckette's avatar
Miller Puckette committed
    return (gobj_click(&x->x_scalar->sc_gobj, glist,
        xpix, ypix, shift, alt, dbl, doit));
}

#define ARRAYWRITECHUNKSIZE 1000

static void garray_save(t_gobj *z, t_binbuf *b)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int filestyle;
Miller Puckette's avatar
Miller Puckette committed
    t_garray *x = (t_garray *)z;
    t_array *array = garray_getarray(x);
    t_template *scalartemplate;
    if (x->x_scalar->sc_template != gensym("pd-_float_array"))
    {
            /* LATER "save" the scalar as such */ 
        pd_error(x, "can't save arrays of type %s yet", 
            x->x_scalar->sc_template->s_name);
        return;
    }
    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))
    {
        error("array: no template of type %s",
            x->x_scalar->sc_template->s_name);
        return;
    }
    /* style = template_getfloat(scalartemplate, gensym("style"),
            x->x_scalar->sc_vec, 0); */
    filestyle = (x->x_style == PLOTSTYLE_POINTS ? 1 : 
        (x->x_style == PLOTSTYLE_POLY ? 0 : x->x_style)); 

    binbuf_addv(b, "sssisiss;", gensym("#X"), gensym("array"),
Miller Puckette's avatar
Miller Puckette committed
        x->x_name, array->a_n, &s_float,
        x->x_saveit + 2 * filestyle + 8*x->x_hidename +
        16 * x->x_joc, x->x_fillcolor, x->x_outlinecolor);
Miller Puckette's avatar
Miller Puckette committed
    if (x->x_saveit)
    {
        int n = array->a_n, n2 = 0;
        if (n > 200000)
            post("warning: I'm saving an array with %d points!\n", n);
        while (n2 < n)
        {
            int chunk = n - n2, i;
            if (chunk > ARRAYWRITECHUNKSIZE)
                chunk = ARRAYWRITECHUNKSIZE;
            binbuf_addv(b, "si", gensym("#A"), n2);
            for (i = 0; i < chunk; i++)
                binbuf_addv(b, "f", ((t_word *)(array->a_vec))[n2+i].w_float);
            binbuf_addv(b, ";");
            n2 += chunk;
        }
    }
}

t_widgetbehavior garray_widgetbehavior =
{
    garray_getrect,
    garray_displace,
    garray_select,
    garray_activate,
    garray_delete,
    garray_vis,
    garray_click,
Miller Puckette's avatar
Miller Puckette committed
};

/* ----------------------- public functions -------------------- */

void garray_usedindsp(t_garray *x)
{
    x->x_usedindsp = 1;
}

static void garray_doredraw(t_gobj *client, t_glist *glist)
{
    //fprintf(stderr,"garray_doredraw\n");
Miller Puckette's avatar
Miller Puckette committed
    t_garray *x = (t_garray *)client;
    if (glist_isvisible(x->x_glist))
    {
        garray_vis(&x->x_gobj, x->x_glist, 0);
Miller Puckette's avatar
Miller Puckette committed
        garray_vis(&x->x_gobj, x->x_glist, 1);

        /* we do this to reposition objects back where they belong */
        if (!glist_istoplevel(glist))
        {
            canvas_restore_original_position(glist_getcanvas(glist),
                (t_gobj *)glist, 0, -1);
        }
        //fprintf(stderr,"check if we need to reselect %lx %lx %lx\n",
        //    glist_getcanvas(glist), (t_gobj *)glist, glist->gl_owner);
        int selected = 0;
//        t_glist *sel = glist->gl_owner;
        t_glist *sel = glist;
        while (sel && sel != glist_getcanvas(glist))
        {
            if (glist_isselected(glist_getcanvas(glist), (t_gobj *)sel))
            {
                selected = 1;
                break;
            }
            sel = sel->gl_owner;
        }
        if (selected)
        {
            //fprintf(stderr,"garray_doredraw isselected\n");
            sys_vgui("pdtk_select_all_gop_widgets .x%lx %lx %d\n",
                glist_getcanvas(glist), glist, 1);
            // a giant kludge-- we really just need gop items
            // to be children of their gop <group>
            t_scalar *sc = x->x_scalar;
            char tagbuf[MAXPDSTRING];
            sprintf(tagbuf, "scalar%lx", (long unsigned int)sc->sc_vec);
            gui_vmess("gui_gobj_select", "xs",
Miller Puckette's avatar
Miller Puckette committed
}

void garray_redraw(t_garray *x)
{
    //fprintf(stderr,"garray_redraw\n");
Miller Puckette's avatar
Miller Puckette committed
    if (glist_isvisible(x->x_glist))
        // enqueueing redraw ensures that the array is drawn after its values
        // have been instantiated (instead, we address this in the creating
        // dialog by enqueuing redrawing of the graph garray resides in as
        // this will fix the lack of the array name and other features
        // hence this approach is considered wrong
        //sys_queuegui(&x->x_gobj, x->x_glist, garray_doredraw);

        // this is useful so that things get redrawn before they are selected
        // so that we don't have to fake yet another selection after the fact
        garray_doredraw(&x->x_gobj, x->x_glist);
Miller Puckette's avatar
Miller Puckette committed
    /* jsarlo { */
    /* this happens in garray_vis() when array is visible for
       performance reasons */
    else
    {
        //fprintf(stderr,"garray_redraw_listviewing\n");
        if (x->x_listviewing)
            sys_vgui("pdtk_array_listview_fillpage %s\n",
Miller Puckette's avatar
Miller Puckette committed
                 x->x_realname->s_name);
    }
    /* } jsarlo */
}

   /* This function gets the template of an array; if we can't figure
Miller Puckette's avatar
Miller Puckette committed
   out what template an array's elements belong to we're in grave trouble
   when it's time to free or resize it.  */
t_template *garray_template(t_garray *x)
{
    t_array *array = garray_getarray(x);
    t_template *template = 
        (array ? template_findbyname(array->a_templatesym) : 0);
    if (!template)
        bug("garray_template");
    return (template);
}

int garray_npoints(t_garray *x) /* get the length */
{
    t_array *array = garray_getarray(x);
    return (array->a_n);
}

char *garray_vec(t_garray *x) /* get the contents */
{
    t_array *array = garray_getarray(x);
    return ((char *)(array->a_vec));
}

    /* routine that checks if we're just an array of floats and if
    so returns the goods */

int garray_getfloatwords(t_garray *x, int *size, t_word **vec)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int yonset, elemsize;
Miller Puckette's avatar
Miller Puckette committed
    t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!a)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return (0);
    }
    else if (elemsize != sizeof(t_word))
    {
        error("%s: has more than one field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return (0);
    }
    *size = garray_npoints(x);
    *vec =  (t_word *)garray_vec(x);
    return (1);
}
    /* older, non-64-bit safe version, supplied for older externs */

int garray_getfloatarray(t_garray *x, int *size, t_float **vec)
{
    if (sizeof(t_word) != sizeof(t_float))
Miller Puckette's avatar
Miller Puckette committed
    {
        static int warned;
        if (!warned)
            post(
 "warning: extern using garray_getfloatarray() won't work in 64-bit version");
        warned = 1;
    }
    return (garray_getfloatwords(x, size, (t_word **)vec));
}

    /* set the "saveit" flag */
void garray_setsaveit(t_garray *x, int saveit)
{
    if (x->x_saveit && !saveit)
        post("warning: array %s: clearing save-in-patch flag",
            x->x_name->s_name);
    x->x_saveit = saveit;
}

/*------------------- Pd messages ------------------------ */
static void garray_const(t_garray *x, t_floatarg g)
{
    int yonset, i, elemsize;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
    else for (i = 0; i < array->a_n; i++)
        *((t_float *)((char *)array->a_vec
Miller Puckette's avatar
Miller Puckette committed
            + elemsize * i) + yonset) = g;
    garray_redraw(x);
}

    /* sum of Fourier components; called from routines below */
static void garray_dofo(t_garray *x, int npoints, t_float dcval,
Miller Puckette's avatar
Miller Puckette committed
    int nsin, t_float *vsin, int sineflag)
{
    double phase, phaseincr, fj;
    int yonset, i, j, elemsize;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
    if (npoints == 0)
        npoints = 512;  /* dunno what a good default would be... */
    if (npoints != (1 << ilog2(npoints)))
        post("%s: rounding to %d points", array->a_templatesym->s_name,
Miller Puckette's avatar
Miller Puckette committed
            (npoints = (1<<ilog2(npoints))));
    garray_resize(x, npoints + 3);
    phaseincr = 2. * 3.14159 / npoints;
    for (i = 0, phase = -phaseincr; i < array->a_n; i++, phase += phaseincr)
    {
        double sum = dcval;
        if (sineflag)
            for (j = 0, fj = phase; j < nsin; j++, fj += phase)
                sum += vsin[j] * sin(fj);
        else
            for (j = 0, fj = 0; j < nsin; j++, fj += phase)
                sum += vsin[j] * cos(fj);
        *((t_float *)((array->a_vec + elemsize * i)) + yonset)
Miller Puckette's avatar
Miller Puckette committed
            = sum;
    }
    garray_redraw(x);
}

static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)
{    
    t_float *svec;
Miller Puckette's avatar
Miller Puckette committed
    int npoints, i;
    if (argc < 2)
    {
        error("sinesum: %s: need number of points and partial strengths",
            x->x_realname->s_name);
        return;
    }

    npoints = atom_getfloatarg(0, argc, argv);
    argv++, argc--;
    
    svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
    if (!svec) return;
    
    for (i = 0; i < argc; i++)
        svec[i] = atom_getfloatarg(i, argc, argv);
    garray_dofo(x, npoints, 0, argc, svec, 1);
    t_freebytes(svec, sizeof(t_float) * argc);
}

static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)
{
    t_float *svec;
Miller Puckette's avatar
Miller Puckette committed
    int npoints, i;
    if (argc < 2)
    {
        error("sinesum: %s: need number of points and partial strengths",
            x->x_realname->s_name);
        return;
    }

    npoints = atom_getfloatarg(0, argc, argv);
    argv++, argc--;
    
    svec = (t_float *)t_getbytes(sizeof(t_float) * argc);
    if (!svec) return;

    for (i = 0; i < argc; i++)
        svec[i] = atom_getfloatarg(i, argc, argv);
    garray_dofo(x, npoints, 0, argc, svec, 0);
    t_freebytes(svec, sizeof(t_float) * argc);
}

static void garray_normalize(t_garray *x, t_float f)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int i;
Miller Puckette's avatar
Miller Puckette committed
    double maxv, renormer;
    int yonset, elemsize;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }

    if (f <= 0)
        f = 1;

    for (i = 0, maxv = 0; i < array->a_n; i++)
    {
        double v = *((t_float *)(array->a_vec + elemsize * i)
Miller Puckette's avatar
Miller Puckette committed
            + yonset);
        if (v > maxv)
            maxv = v;
        if (-v > maxv)
            maxv = -v;
    }
    if (maxv > 0)
    {
        renormer = f / maxv;
        for (i = 0; i < array->a_n; i++)
            *((t_float *)(array->a_vec + elemsize * i) + yonset)
Miller Puckette's avatar
Miller Puckette committed
                *= renormer;
    }
    garray_redraw(x);
}

    /* list -- the first value is an index; subsequent values are put in
    the "y" slot of the array.  This generalizes Max's "table", sort of. */
static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv)
{
    int i;
    int yonset, elemsize;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
    if (argc < 2) return;
    else
    {
        int firstindex = atom_getfloat(argv);
        argc--;
        argv++;
            /* drop negative x values */
        if (firstindex < 0)
        {
            argc += firstindex;
            argv -= firstindex;
            firstindex = 0;
            if (argc <= 0) return;
        }
        if (argc + firstindex > array->a_n)
        {
            argc = array->a_n - firstindex;
            if (argc <= 0) return;
        }
        for (i = 0; i < argc; i++)
            *((t_float *)(array->a_vec + elemsize * (i + firstindex)) + yonset)
Miller Puckette's avatar
Miller Puckette committed
                = atom_getfloat(argv + i);
    }
    garray_redraw(x);
}

    /* forward a "bounds" message to the owning graph */
static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1,
    t_floatarg x2, t_floatarg y2)
{
    vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2);
}

    /* same for "xticks", etc */
static void garray_xticks(t_garray *x,
    t_floatarg point, t_floatarg inc, t_floatarg f)
{
    vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f);
}

static void garray_yticks(t_garray *x,
    t_floatarg point, t_floatarg inc, t_floatarg f)
{
    vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f);
}

static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)
{
    typedmess(&x->x_glist->gl_pd, s, argc, argv);
}

static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)
{
    typedmess(&x->x_glist->gl_pd, s, argc, argv);
}
    /* change the name of a garray. */
static void garray_rename(t_garray *x, t_symbol *s)
{
    /* jsarlo { */
    if (x->x_listviewing)
    {
        garray_arrayviewlist_close(x);
    }
    /* } jsarlo */
    pd_unbind(&x->x_gobj.g_pd, x->x_realname);
    x->x_name = s;
    x->x_realname = canvas_realizedollar(x->x_glist, x->x_name);

    pd_bind(&x->x_gobj.g_pd, x->x_realname);

    char buf[MAXPDSTRING];
    sprintf(buf, "%s_changed", x->x_realname->s_name);
    x->x_send = gensym(buf);
Miller Puckette's avatar
Miller Puckette committed
}

static void garray_read(t_garray *x, t_symbol *filename)
{
    int nelem, filedesc, i;
    FILE *fd;
    char buf[FILENAME_MAX], *bufptr;
Miller Puckette's avatar
Miller Puckette committed
    int yonset, elemsize;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
    nelem = array->a_n;
    if ((filedesc = canvas_open(glist_getcanvas(x->x_glist),
            filename->s_name, "", buf, &bufptr, FILENAME_MAX, 0)) < 0 
Miller Puckette's avatar
Miller Puckette committed
                || !(fd = fdopen(filedesc, "r")))
    {
        error("%s: can't open", filename->s_name);
        return;
    }
    for (i = 0; i < nelem; i++)
    {
        float f;
        if (!fscanf(fd, "%f", &f))
Miller Puckette's avatar
Miller Puckette committed
        {
            post("%s: read %d elements into table of size %d",
                filename->s_name, i, nelem);
            break;
        }
        else *((t_float *)(array->a_vec + elemsize * i) + yonset) = f;
Miller Puckette's avatar
Miller Puckette committed
    }
    while (i < nelem)
        *((t_float *)(array->a_vec +
Miller Puckette's avatar
Miller Puckette committed
            elemsize * i) + yonset) = 0, i++;
    fclose(fd);
    garray_redraw(x);
}

static void garray_write(t_garray *x, t_symbol *filename)
{
    FILE *fd;
    char buf[MAXPDSTRING];
    int yonset, elemsize, i;
    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);
    if (!array)
    {
        error("%s: needs floating-point 'y' field", x->x_realname->s_name);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
    canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name,
        buf, MAXPDSTRING);
    sys_bashfilename(buf, buf);
    if (!(fd = fopen(buf, "w")))
    {
        error("%s: can't create", buf);
        return;
    }
    for (i = 0; i < array->a_n; i++)
    {
        if (fprintf(fd, "%g\n",
            *(t_float *)(((array->a_vec + sizeof(t_word) * i)) + yonset)) < 1)
Miller Puckette's avatar
Miller Puckette committed
        {
            post("%s: write error", filename->s_name);
            break;
        }
    }
    fclose(fd);
}


    /* this should be renamed and moved... */
int garray_ambigendian(void)
{
    unsigned short s = 1;
    unsigned char c = *(char *)(&s);
    return (c==0);
}

void garray_resize(t_garray *x, t_floatarg f)
{
    t_array *array = garray_getarray(x);
    int n = (f < 1 ? 1 : f);
    //fprintf(stderr,"garray_resize %d\n", n);
    array_resize(array, n);
    garray_fittograph(x, n, 1);/*template_getfloat(
Miller Puckette's avatar
Miller Puckette committed
        template_findbyname(x->x_scalar->sc_template),
            gensym("style"), x->x_scalar->sc_vec, 1));*/
    //array_resize_and_redraw(array, x->x_glist, n);
Miller Puckette's avatar
Miller Puckette committed
    if (x->x_usedindsp)