예제 #1
0
void gFlagsGroup::paint(gGraph &g, int left, int top, int width, int height)
{
    if (!m_visible) return;
    if (!m_day) return;

    int vis=lvisible.size();
    m_barh=float(height)/float(vis);
    float linetop=top;

    QColor barcol;
    for (int i=0;i<lvisible.size();i++) {
        // Alternating box color
        if (i & 1) barcol=COLOR_ALT_BG1; else barcol=COLOR_ALT_BG2;
        quads->add(left, linetop, left, linetop+m_barh,   left+width-1, linetop+m_barh, left+width-1, linetop, barcol.rgba());

        // Paint the actual flags
        lvisible[i]->m_rect=QRect(left,linetop,width,m_barh);
        lvisible[i]->paint(g,left,linetop,width,m_barh);
        linetop+=m_barh;
    }

    gVertexBuffer *outlines=g.lines();
    outlines->add(left-1, top, left-1, top+height, COLOR_Outline.rgba());
    outlines->add(left-1, top+height, left+width,top+height, COLOR_Outline.rgba());
    outlines->add(left+width,top+height, left+width, top,COLOR_Outline.rgba());
    outlines->add(left+width, top, left-1, top, COLOR_Outline.rgba());

    //lines->add(left-1, top, left-1, top+height);
    //lines->add(left+width, top+height, left+width, top);
}
예제 #2
0
// Time Domain Line Chart
void gLineChart::paint(gGraph & w,int left, int top, int width, int height)
{
    if (!m_visible)
        return;

    if (!m_day)
        return;

    //if (!m_day->channelExists(m_code)) return;

    if (width<0)
        return;



   // lines=w.lines();
    EventDataType miny,maxy;
    double minx,maxx;
    miny=w.min_y, maxy=w.max_y;

    if (w.blockZoom()) {
        minx=w.rmin_x, maxx=w.rmax_x;
    } else {
        maxx=w.max_x, minx=w.min_x;
    }

    // hmmm.. subtract_offset..

    /*if (miny<0) {
        miny=-MAX(fabs(miny),fabs(maxy));
    }*/



    w.roundY(miny,maxy);

    double xx=maxx-minx;
    double xmult=double(width)/xx;

    EventDataType yy=maxy-miny;
    EventDataType ymult=EventDataType(height-3)/yy;   // time to pixel conversion multiplier

    // Return on screwy min/max conditions
    if (xx<0)
        return;
    if (yy<=0) {
        if (miny==0)
            return;
     }

    EventDataType lastpx,lastpy;
    EventDataType px,py;
    int idx;
    bool done;
    double x0,xL;
    double sr;
    int sam;
    int minz,maxz;

    // Draw bounding box
    gVertexBuffer *outlines=w.lines();
    GLuint blk=QColor(Qt::black).rgba();
    outlines->add(left, top, left, top+height, blk);
    outlines->add(left, top+height, left+width,top+height, blk);
    outlines->add(left+width,top+height, left+width, top, blk);
    outlines->add(left+width, top, left,top, blk);
    width--;
    height-=2;

    int num_points=0;
    int visible_points=0;
    int total_points=0;
    int total_visible=0;
    bool square_plot,accel;
    qint64 clockdrift=qint64(PROFILE.cpap->clockDrift()) * 1000L;
    qint64 drift=0;

    QHash<ChannelID,QVector<EventList *> >::iterator ci;

    //m_line_color=schema::channel[m_code].defaultColor();
    int legendx=left+width;

    int codepoints;
    //GLuint color;
    for (int gi=0;gi<m_codes.size();gi++) {
        ChannelID code=m_codes[gi];
        //m_line_color=m_colors[gi];
        lines->setColor(m_colors[gi]);
        //color=m_line_color.rgba();

        codepoints=0;
        for (int svi=0;svi<m_day->size();svi++) {
            Session *sess=(*m_day)[svi];
            if (!sess) {
                qWarning() << "gLineChart::Plot() NULL Session Record.. This should not happen";
                continue;
            }

            drift=(sess->machine()->GetType()==MT_CPAP) ? clockdrift : 0;

            if (!sess->enabled()) continue;
            schema::Channel ch=schema::channel[code];
            bool fndbetter=false;
            for (QList<schema::Channel *>::iterator l=ch.m_links.begin();l!=ch.m_links.end();l++) {
                schema::Channel *c=*l;
                ci=(*m_day)[svi]->eventlist.find(c->id());
                if (ci!=(*m_day)[svi]->eventlist.end()) {
                    fndbetter=true;
                    break;
                }

            }
            if (!fndbetter) {
                ci=(*m_day)[svi]->eventlist.find(code);
                if (ci==(*m_day)[svi]->eventlist.end()) continue;
            }


            QVector<EventList *> & evec=ci.value();
            num_points=0;
            for (int i=0;i<evec.size();i++)
                num_points+=evec[i]->count();
            total_points+=num_points;
            codepoints+=num_points;
            const int num_averages=20;  // Max n umber of samples taken from samples per pixel for better min/max values
            for (int n=0;n<evec.size();n++) { // for each segment
                EventList & el=*evec[n];

                accel=(el.type()==EVL_Waveform); // Turn on acceleration if this is a waveform.
                if (accel) {
                    sr=el.rate();           // Time distance between samples
                    if (sr<=0) {
                        qWarning() << "qLineChart::Plot() assert(sr>0)";
                        continue;
                    }
                }
                if (m_disable_accel) accel=false;


                square_plot=m_square_plot;
                if (accel || num_points>20000) { // Don't square plot if too many points or waveform
                    square_plot=false;
                }

                int siz=evec[n]->count();
                if (siz<=1) continue; // Don't bother drawing 1 point or less.

                x0=el.time(0)+drift;
                xL=el.time(siz-1)+drift;

                if (maxx<x0) continue;
                if (xL<minx) continue;

                if (x0>xL) {
                    if (siz==2) { // this happens on CPAP
                        quint32 t=el.getTime()[0];
                        el.getTime()[0]=el.getTime()[1];
                        el.getTime()[1]=t;
                        EventStoreType d=el.getData()[0];
                        el.getData()[0]=el.getData()[1];
                        el.getData()[1]=d;

                    } else {
                        qDebug() << "Reversed order sample fed to gLineChart - ignored.";
                        continue;
                        //assert(x1<x2);
                    }
                }
                if (accel) {
                    //x1=el.time(1);

                    double XR=xx/sr;
                    double Z1=MAX(x0,minx);
                    double Z2=MIN(xL,maxx);
                    double ZD=Z2-Z1;
                    double ZR=ZD/sr;
                    double ZQ=ZR/XR;
                    double ZW=ZR/(width*ZQ);
                    visible_points+=ZR*ZQ;
                    if (accel && n>0) {
                        sam=1;
                    }
                    if (ZW<num_averages) {
                        sam=1;
                        accel=false;
                    } else {
                        sam=ZW/num_averages;
                        if (sam<1) {
                            sam=1;
                            accel=false;
                        }
                    }
                    // Prepare the min max y values if we still are accelerating this plot
                    if (accel) {
                        for (int i=0;i<width;i++) {
                            m_drawlist[i].setX(height);
                            m_drawlist[i].setY(0);
                        }
                        minz=width;
                        maxz=0;
                    }
                    total_visible+=visible_points;
                } else {
                    sam=1;
                }

                // these calculations over estimate
                // The Z? values are much more accurate

                idx=0;

                if (el.type()==EVL_Waveform)  {
                    // We can skip data previous to minx if this is a waveform

                    if (minx>x0) {
                        double j=minx-x0;  // == starting min of first sample in this segment
                        idx=(j/sr);
                        //idx/=(sam*num_averages);
                        //idx*=(sam*num_averages);
                        // Loose the precision
                        idx+=sam-(idx % sam);

                    } // else just start from the beginning
                }

                int xst=left+1;
                int yst=top+height+1;

                double time;
                EventDataType data;
                EventDataType gain=el.gain();
                //EventDataType nmult=ymult*gain;
                //EventDataType ymin=EventDataType(miny)/gain;

                //const QVector<EventStoreType> & dat=el.getData();
                //const QVector<quint32> & tim=el.getTime();
                //quint32 * tptr;


                //qint64 stime=el.first();

                done=false;

//                if (!accel) {
                lines->setSize(1.5);
//                } else lines->setSize(1);

                if (el.type()==EVL_Waveform) {  // Waveform Plot
                    if (idx > sam) idx-=sam;
                    time=el.time(idx) + drift;
                    double rate=double(sr)*double(sam);
                    EventStoreType * ptr=el.rawData()+idx;

                    if (accel) {
                        //////////////////////////////////////////////////////////////////
                        // Accelerated Waveform Plot
                        //////////////////////////////////////////////////////////////////

//                        qint64 tmax=(maxx-time)/rate;
//                        if ((tmax*sam) < siz) {
//                            siz=idx+tmax*sam;
//                            done=true;
//                        }

                        for (int i=idx;i<siz;i+=sam,ptr+=sam) {
                            time+=rate;
                            // This is much faster than QVector access.
                            data=*ptr;
                            data *= gain;

                            // Scale the time scale X to pixel scale X
                            px=((time - minx) * xmult);

                            // Same for Y scale, with gain factored in nmult
                            py=((data - miny) * ymult);

                            // In accel mode, each pixel has a min/max Y value.
                            // m_drawlist's index is the pixel index for the X pixel axis.
                            int z=round(px); // Hmmm... round may screw this up.

                            if (z<minz)
                                minz=z;  // minz=First pixel

                            if (z>maxz)
                                maxz=z;  // maxz=Last pixel

                            if (minz<0) {
                                qDebug() << "gLineChart::Plot() minz<0  should never happen!! minz =" << minz;
                                minz=0;
                            }
                            if (maxz>max_drawlist_size) {
                                qDebug() << "gLineChart::Plot() maxz>max_drawlist_size!!!! maxz = " << maxz << " max_drawlist_size =" << max_drawlist_size;
                                maxz=max_drawlist_size;
                            }

                            // Update the Y pixel bounds.
                            if (py<m_drawlist[z].x())
                                m_drawlist[z].setX(py);
                            if (py>m_drawlist[z].y())
                                m_drawlist[z].setY(py);

                            if (time>maxx) {
                                done=true;
                                break;
                            }

                        }
                        // Plot compressed accelerated vertex list
                        if (maxz>width) {
                            maxz=width;
                        }
                        float ax1,ay1;
                        QPoint * drl=m_drawlist+minz;
                        // Don't need to cap VertexBuffer here, as it's limited to max_drawlist_size anyway


                        // Cap within VertexBuffer capacity, one vertex per line point
                        int np=(maxz-minz)*2;

                        int j=lines->Max()-lines->cnt();
                        if (np < j) {
                            for (int i=minz;i<maxz;i++, drl++) {
                                ax1=drl->x();
                                ay1=drl->y();
                                lines->unsafe_add(xst+i,yst-ax1,xst+i,yst-ay1);

                                //if (lines->full()) break;
                            }
                        } else {
                            qDebug() << "gLineChart full trying to draw" << schema::channel[code].label();
                            done=true;
                        }

                    } else { // Zoomed in Waveform
                        //////////////////////////////////////////////////////////////////
                        // Normal Waveform Plot
                        //////////////////////////////////////////////////////////////////

                        // Cap within VertexBuffer capacity, one vertex per line point
//                        int np=((siz-idx)/sam)*2;
//                        int j=lines->Max()-lines->cnt();
//                        if (np > j) {
//                            siz=j*sam;
//                        }

                        // Prime first point
                        data=*ptr * gain;
                        lastpx=xst+((time - minx) * xmult);
                        lastpy=yst-((data - miny) * ymult);

                        for (int i=idx;i<siz;i+=sam) {
                            ptr+=sam;
                            time+=rate;

                            data=*ptr * gain;

                            px=xst+((time - minx) * xmult);   // Scale the time scale X to pixel scale X
                            py=yst-((data - miny) * ymult);   // Same for Y scale, with precomputed gain
                            //py=yst-((data - ymin) * nmult);   // Same for Y scale, with precomputed gain

                            lines->add(lastpx,lastpy,px,py);

                            lastpx=px;
                            lastpy=py;

                            if (time>maxx) {
                                done=true;
                                break;
                            }
                            if (lines->full())
                                break;
                        }
                    }

                } else  {
                    //////////////////////////////////////////////////////////////////
                    // Standard events/zoomed in Plot
                    //////////////////////////////////////////////////////////////////

                    double start=el.first() + drift;

                    quint32 * tptr=el.rawTime();

                    int idx=0;

                    if (siz>15) {
                        for (;idx<siz;++idx) {
                            time=start + *tptr++;
                            if (time >= minx) {
                                break;
                            }
                        }

                        if (idx > 0) {
                            idx--;
                            //tptr--;
                        }
                    }

                    // Step one backwards if possible (to draw through the left margin)
                    EventStoreType * dptr=el.rawData() + idx;
                    tptr=el.rawTime() + idx;

                    time=start + *tptr++;
                    data=*dptr++ * gain;

                    idx++;

                    lastpx=xst+((time - minx) * xmult);   // Scale the time scale X to pixel scale X
                    lastpy=yst-((data - miny) * ymult);   // Same for Y scale without precomputed gain

                    siz-=idx;

                    // Check if would overflow lines gVertexBuffer
                    int gs=siz << 1;
                    int j=lines->Max()-lines->cnt();
                    if (square_plot)
                        gs <<= 1;
                    if (gs > j) {
                        qDebug() << "Would overflow line points.. increase default VertexBuffer size in gLineChart";
                        siz=j >> square_plot ? 2 : 1;
                        done=true; // end after this partial draw..
                    }

                    // Unrolling square plot outside of loop to gain a minor speed improvement.
                    EventStoreType *eptr=dptr+siz;
                    if (square_plot) {
                        for (; dptr < eptr; dptr++) {
                            time=start + *tptr++;
                            data=gain * *dptr;

                            px=xst+((time - minx) * xmult);   // Scale the time scale X to pixel scale X
                            py=yst-((data - miny) * ymult);   // Same for Y scale without precomputed gain

                            // Horizontal lines are easy to cap
                            if (py==lastpy) {
                                // Cap px to left margin
                                if (lastpx<xst) lastpx=xst;

                                // Cap px to right margin
                                if (px>xst+width) px=xst+width;

                                lines->unsafe_add(lastpx,lastpy,px,lastpy,px,lastpy,px,py);
                            } else {
                                // Letting the scissor do the dirty work for non horizontal lines
                                // This really should be changed, as it might be cause that weird
                                // display glitch on Linux..
                                lines->unsafe_add(lastpx,lastpy,px,lastpy,px,lastpy,px,py);
                            }

                            lastpx=px;
                            lastpy=py;

                            if (time > maxx) {
                                done=true; // Let this iteration finish.. (This point will be in far clipping)
                                break;
                            }
                        }
                    } else {
                        for (; dptr < eptr; dptr++) {
                        //for (int i=0;i<siz;i++) {
                            time=start + *tptr++;
                            data=gain  * *dptr;

                            px=xst+((time - minx) * xmult);   // Scale the time scale X to pixel scale X
                            py=yst-((data - miny) * ymult);   // Same for Y scale without precomputed gain

                            // Horizontal lines are easy to cap
                            if (py==lastpy) {
                                // Cap px to left margin
                                if (lastpx<xst) lastpx=xst;

                                // Cap px to right margin
                                if (px>xst+width) px=xst+width;

                                lines->unsafe_add(lastpx,lastpy,px,py);
                            } else {
                                // Letting the scissor do the dirty work for non horizontal lines
                                // This really should be changed, as it might be cause that weird
                                // display glitch on Linux..
                                lines->unsafe_add(lastpx,lastpy,px,py);
                            }

                            lastpx=px;
                            lastpy=py;

                            if (time > maxx) { // Past right edge, abort further drawing..
                                done=true;
                                break;
                            }
                        }
                    }
                }

                if (done) break;
            }
        }

        ////////////////////////////////////////////////////////////////////
        // Draw Legends on the top line
        ////////////////////////////////////////////////////////////////////
        if ((codepoints>0)) { //(m_codes.size()>1) &&
            QString text=schema::channel[code].label();
            int wid,hi;
            GetTextExtent(text,wid,hi);
            legendx-=wid;
            w.renderText(text,legendx,top-4);
            int bw=GetXHeight();
            legendx-=bw/2;

            int tp=top-5-bw/2;
            w.quads()->add(legendx-bw,tp+bw/2,legendx,tp+bw/2,legendx,tp-bw/2,legendx-bw,tp-bw/2,m_colors[gi].rgba());
            legendx-=hi+bw/2;
        }
    }
예제 #3
0
void gXGrid::paint(gGraph & w,int left,int top, int width, int height)
{
    int x,y;

    gVertexBuffer * stippled, * lines;

    EventDataType miny,maxy;

    if (w.zoomY()==0 && PROFILE.appearance->allowYAxisScaling()) {
        miny=w.physMinY();
        maxy=w.physMaxY();
    } else {
        miny=w.min_y;
        maxy=w.max_y;

        if (miny<0) { // even it up if it's starts negative
            miny=-MAX(fabs(miny),fabs(maxy));
        }
    }

    w.roundY(miny,maxy);

    //EventDataType dy=maxy-miny;

    if (height<0) return;

    static QString fd="0";
    GetTextExtent(fd,x,y);

    double max_yticks=round(height / (y+14.0)); // plus spacing between lines
    //double yt=1/max_yticks;

    double mxy=MAX(fabs(maxy),fabs(miny));
    double mny=miny;
    if (miny<0) {
        mny=-mxy;
    }
    double rxy=mxy-mny;

    int myt;
    bool fnd=false;
    for (myt=max_yticks;myt>=1;myt--) {
        float v=rxy/float(myt);
        if (float(v)==int(v)) {
            fnd=true;
            break;
        }
    }
    if (fnd) max_yticks=myt;
    else {
        max_yticks=2;
    }
    double yt=1/max_yticks;

    double ymult=height/rxy;

    double min_ytick=rxy*yt;

    float ty,h;

    if (min_ytick<=0) {
        qDebug() << "min_ytick error in gXGrid::paint() in" << w.title();
        return;
    }
    if (min_ytick>=1000000) {
        min_ytick=100;
    }


    stippled=w.backlines();
    lines=w.backlines();
    for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
        ty=(i - miny) * ymult;
        h=top+height-ty;
        if (m_show_major_lines && (i > miny)) {
            stippled->add(left,h,left+width,h,m_major_color.rgba());
        }
        double z=(min_ytick/4)*ymult;
        double g=h;
        for (int i=0;i<3;i++) {
            g+=z;
            if (g>top+height) break;
            //if (vertcnt>=maxverts) {
              //  qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
//                break;
  //          }
            if (m_show_minor_lines) {// && (i > miny)) {
                stippled->add(left,g,left+width,g,m_minor_color.rgba());
            }
            if (stippled->full()) {
                break;
            }
        }
        if (lines->full() || stippled->full()) {
            qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
            break;
        }
    }
}
예제 #4
0
void gFlagsLine::paint(gGraph & w,int left, int top, int width, int height)
{
    if (!m_visible) return;
    if (!m_day) return;
    lines=w.lines();
    double minx;
    double maxx;

    if (w.blockZoom()) {
        minx=w.rmin_x;
        maxx=w.rmax_x;
    } else {
        minx=w.min_x;
        maxx=w.max_x;
    }

    double xx=maxx-minx;
    if (xx<=0) return;

    double xmult=width/xx;

    GetTextExtent(m_label,m_lx,m_ly);

    // Draw text label
    w.renderText(m_label,left-m_lx-10,top+(height/2)+(m_ly/2));

    float x1,x2;

    float bartop=top+2;
    float bottom=top+height-2;
    bool verts_exceeded=false;
    qint64 X,X2,L;
    lines->setColor(schema::channel[m_code].defaultColor());

    qint64 start;
    quint32 * tptr;
    EventStoreType *dptr, * eptr;
    int idx;
    QHash<ChannelID,QVector<EventList *> >::iterator cei;

    qint64 clockdrift=qint64(PROFILE.cpap->clockDrift()) * 1000L;
    qint64 drift=0;

    for (QList<Session *>::iterator s=m_day->begin();s!=m_day->end(); s++) {
        if (!(*s)->enabled())
            continue;
        drift=((*s)->machine()->GetType()==MT_CPAP) ? clockdrift : 0;

        cei=(*s)->eventlist.find(m_code);
        if (cei==(*s)->eventlist.end())
            continue;

        QVector<EventList *> & evlist=cei.value();
        for (int k=0;k<evlist.size();k++) {
            EventList & el=*(evlist[k]);
            start=el.first() + drift;
            tptr=el.rawTime();
            dptr=el.rawData();
            int np=el.count();
            eptr=dptr+np;

            for (idx=0;dptr < eptr; dptr++, tptr++, idx++) {
                X=start + *tptr;
                L=*dptr * 1000;
                if (X >= minx)
                    break;
                X2=X-L;
                if (X2 >= minx)
                    break;

            }
            np-=idx;

            if (m_flt==FT_Bar) {
                ///////////////////////////////////////////////////////////////////////////
                // Draw Event Flag Bars
                ///////////////////////////////////////////////////////////////////////////

                // Check bounds outside of loop is faster..
                // This will have to be reverted if multithreaded drawing is ever brought back

                int rem=lines->Max() - lines->cnt();
                if ((np<<1) > rem) {
                    qDebug() << "gFlagsLine would overfill lines for" << schema::channel[m_code].label();
                    np=rem >> 1;
                    verts_exceeded=true;
                }

                for (int i=0;i<np;i++) {
                    X=start + *tptr++;

                    if (X > maxx)
                        break;

                    x1=(X - minx) * xmult + left;
                    lines->add(x1,bartop,x1,bottom);

                    //if (lines->full()) { verts_exceeded=true; break; }
                }
            } else if (m_flt==FT_Span) {
예제 #5
0
void gYAxis::paint(gGraph & w,int left,int top, int width, int height)
{

    int x,y;//,yh=0;

    //Todo: clean this up as there is a lot of duplicate code between the sections

    if (0) {//w.graphView()->usePixmapCache()) {
/*        if (w.invalidate_yAxisImage) {

            if (!m_image.isNull()) {
                w.graphView()->deleteTexture(m_textureID);
                m_image=QImage();
            }


            if (height<0) return;
            if (height>2000) return;

            int labelW=0;

            EventDataType miny=w.min_y;
            EventDataType maxy=w.max_y;

            if (miny<0) { // even it up if it's starts negative
                miny=-MAX(fabs(miny),fabs(maxy));
            }

            w.roundY(miny,maxy);

            EventDataType dy=maxy-miny;
            static QString fd="0";
            GetTextExtent(fd,x,y);
            yh=y;

            m_image=QImage(width,height+y+4,QImage::Format_ARGB32_Premultiplied);

            m_image.fill(Qt::transparent);
            QPainter paint(&m_image);


            double max_yticks=round(height / (y+14.0)); // plus spacing between lines

            double mxy=MAX(fabs(maxy),fabs(miny));
            double mny=miny;
            if (miny<0) {
                mny=-mxy;
            }

            double rxy=mxy-mny;

            int myt;
            bool fnd=false;
            for (myt=max_yticks;myt>2;myt--) {
                float v=rxy/float(myt);
                if (v==int(v)) {
                    fnd=true;
                    break;
                }
            }
            if (fnd) max_yticks=myt;
            double yt=1/max_yticks;

            double ymult=height/rxy;

            double min_ytick=rxy*yt;

            float ty,h;

            if (min_ytick<=0) {
                qDebug() << "min_ytick error in gYAxis::paint() in" << w.title();
                return;
            }
            if (min_ytick>=1000000) {
                min_ytick=100;
            }

            //lines=w.backlines();

            for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
                ty=(i - miny) * ymult;
                if (dy<5) {
                    fd=Format(i*m_yaxis_scale,2);
                } else {
                    fd=Format(i*m_yaxis_scale,1);
                }

                GetTextExtent(fd,x,y);

                if (x>labelW) labelW=x;
                h=(height-2)-ty;
                h+=yh;
    #ifndef Q_OS_MAC
                // stupid pixel alignment rubbish, I really should be using floats..
                h+=1;
    #endif
                if (h<0)
                    continue;

                paint.setBrush(Qt::black);
                paint.drawText(width-8-x,h+y/2,fd);

                paint.setPen(m_line_color);
                paint.drawLine(width-4,h,width,h);

                double z=(min_ytick/4)*ymult;
                double g=h;
                for (int i=0;i<3;i++) {
                    g+=z;
                    if (g>height+yh) break;
                    paint.drawLine(width-3,g,width,g);
                }
            }
            paint.end();
            m_image=QGLWidget::convertToGLFormat(m_image);
            m_textureID=w.graphView()->bindTexture(m_image,GL_TEXTURE_2D,GL_RGBA,QGLContext::NoBindOption);
            w.invalidate_yAxisImage=false;
        }

        if (!m_image.isNull()) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glEnable(GL_TEXTURE_2D);
            w.graphView()->drawTexture(QPoint(left,(top+height)-m_image.height()+5),m_textureID);
            glDisable(GL_TEXTURE_2D);
            glDisable(GL_BLEND);
        }
*/
    } else {
        if (height<0) return;
        if (height>2000) return;
        int labelW=0;

        EventDataType miny;
        EventDataType maxy;

        if (w.zoomY()==0 && PROFILE.appearance->allowYAxisScaling()) {
            miny=w.physMinY();
            maxy=w.physMaxY();
        } else {

            miny=w.min_y;
            maxy=w.max_y;

            if (miny<0) { // even it up if it's starts negative
                miny=-MAX(fabs(miny),fabs(maxy));
            }
        }
        w.roundY(miny,maxy);

        EventDataType dy=maxy-miny;

        static QString fd="0";
        GetTextExtent(fd,x,y);

        double max_yticks=round(height / (y+14.0)); // plus spacing between lines

        double mxy=MAX(fabs(maxy),fabs(miny));
        double mny=miny;
        if (miny<0) {
            mny=-mxy;
        }

        double rxy=mxy-mny;

        int myt;
        bool fnd=false;
        for (myt=max_yticks;myt>2;myt--) {
            float v=rxy/float(myt);
            if (v==int(v)) {
                fnd=true;
                break;
            }
        }
        if (fnd) max_yticks=myt;
        double yt=1/max_yticks;

        double ymult=height/rxy;

        double min_ytick=rxy*yt;


        //if (dy>5) {
        //    min_ytick=round(min_ytick);
        //} else {

        //}

        float ty,h;

        if (min_ytick<=0) {
            qDebug() << "min_ytick error in gYAxis::paint() in" << w.title();
            return;
        }
        if (min_ytick>=1000000) {
            min_ytick=100;
        }
        lines=w.backlines();

        GLuint line_color=m_line_color.rgba();
        for (double i=miny; i<=maxy+min_ytick-0.00001; i+=min_ytick) {
            ty=(i - miny) * ymult;
            if (dy<5) {
                fd=Format(i*m_yaxis_scale,2);
            } else {
                fd=Format(i*m_yaxis_scale,1);
            }

            GetTextExtent(fd,x,y); // performance bottleneck..

            if (x>labelW) labelW=x;
            h=top+height-ty;
            if (h<top) continue;
            w.renderText(fd,left+width-8-x,(h+(y/2.0)),0,m_text_color,defaultfont);

            lines->add(left+width-4,h,left+width,h,line_color);

            double z=(min_ytick/4)*ymult;
            double g=h;
            for (int i=0;i<3;i++) {
                g+=z;
                if (g>top+height) break;
                lines->add(left+width-3,g,left+width,g,line_color);
                if (lines->full()) {
                    qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
                    break;
                }
            }
            if (lines->full()) {
                qWarning() << "vertarray bounds exceeded in gYAxis for " << w.title() << "graph" << "MinY =" <<miny << "MaxY =" << maxy << "min_ytick=" <<min_ytick;
                break;
            }
        }
    }
}