OSCAR-code/Graphs/gSummaryChart.cpp

504 lines
15 KiB
C++
Raw Normal View History

2011-07-01 10:10:44 +00:00
/*
2011-06-26 08:30:44 +00:00
gBarChart Implementation
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
2011-07-01 10:10:44 +00:00
*/
2011-06-26 08:30:44 +00:00
#include <math.h>
2011-09-03 12:59:08 +00:00
#include <QLabel>
2011-09-02 02:00:04 +00:00
#include <QDateTime>
#include "gYAxis.h"
2011-09-17 12:39:00 +00:00
#include "gSummaryChart.h"
2011-06-26 08:30:44 +00:00
2011-09-03 12:59:08 +00:00
extern QLabel * qstatus2;
SummaryChart::SummaryChart(QString label,GraphType type)
:Layer(""),m_label(label),m_graphtype(type)
2011-06-26 08:30:44 +00:00
{
QColor color=Qt::black;
addGLBuf(quads=new GLShortBuffer(20000,GL_QUADS));
addGLBuf(lines=new GLShortBuffer(20000,GL_LINES));
2011-09-02 02:00:04 +00:00
quads->forceAntiAlias(true);
lines->setSize(2);
lines->forceAntiAlias(false);
2011-09-02 05:13:07 +00:00
m_empty=true;
hl_day=-1;
m_machinetype=MT_CPAP;
2011-06-26 08:30:44 +00:00
}
SummaryChart::~SummaryChart()
2011-06-26 08:30:44 +00:00
{
}
void SummaryChart::SetDay(Day * nullday)
2011-09-02 05:13:07 +00:00
{
Day * day=nullday;
Layer::SetDay(day);
m_values.clear();
2011-09-12 05:09:53 +00:00
m_days.clear();
m_miny=9999999;
m_maxy=-9999999;
m_minx=0;
m_maxx=0;
int dn;
EventDataType tmp,total;
ChannelID code;
m_fday=0;
qint64 tt;
2011-09-03 04:38:12 +00:00
m_empty=true;
int suboffset;
SummaryType type;
for (QMap<QDate,QVector<Day *> >::iterator d=PROFILE.daylist.begin();d!=PROFILE.daylist.end();d++) {
tt=QDateTime(d.key(),QTime(0,0,0),Qt::UTC).toTime_t();
//tt=QDateTime(d.key(),QTime(12,0,0)).toTime_t();
dn=tt/86400;
tt*=1000L;
if (!m_minx || tt<m_minx) m_minx=tt;
if (!m_maxx || tt>m_maxx) m_maxx=tt;
total=0;
bool fnd=false;
for (int j=0;j<m_codes.size();j++) {
code=m_codes[j];
if (code==CPAP_Leak) suboffset=PROFILE["IntentionalLeak"].toDouble(); else suboffset=0;
type=m_type[j];
for (int i=0;i<d.value().size();i++) {
day=d.value()[i];
if (day->machine_type()!=m_machinetype) continue;
2011-09-18 15:43:14 +00:00
if (type==ST_HOURS || day->channelExists(code) || day->settingExists(code)) { // too many lookups happening here.. stop the crap..
2011-09-12 05:09:53 +00:00
m_days[dn]=day;
switch(m_type[j]) {
case ST_AVG: tmp=day->avg(code); break;
case ST_SUM: tmp=day->sum(code); break;
case ST_WAVG: tmp=day->wavg(code); break;
case ST_90P: tmp=day->p90(code); break;
case ST_MIN: tmp=day->min(code); break;
case ST_MAX: tmp=day->max(code); break;
case ST_CNT: tmp=day->count(code); break;
case ST_CPH: tmp=day->cph(code); break;
case ST_SPH: tmp=day->sph(code); break;
case ST_HOURS: tmp=day->hours(); break;
2011-09-18 15:43:14 +00:00
case ST_SETMIN: tmp=day->settings_min(code); break;
case ST_SETMAX: tmp=day->settings_max(code); break;
case ST_SETAVG: tmp=day->settings_avg(code); break;
case ST_SETWAVG: tmp=day->settings_wavg(code); break;
case ST_SETSUM: tmp=day->settings_sum(code); break;
default: break;
}
if (suboffset>0) {
tmp-=suboffset;
2011-10-05 23:20:56 +00:00
//if (tmp<0) tmp=0;
}
//if (tmp>0) {
fnd=true;
total+=tmp;
m_values[dn][j+1]=tmp;
2011-09-10 06:59:09 +00:00
if (tmp<m_miny) m_miny=tmp;
if (tmp>m_maxy) m_maxy=tmp;
break;
// }
}
}
}
if (fnd) {
if (!m_fday) m_fday=dn;
m_values[dn][0]=total;
2011-09-10 06:59:09 +00:00
if (m_graphtype==GT_BAR) {
if (total<m_miny) m_miny=total;
if (total>m_maxy) m_maxy=total;
}
m_empty=false;
}
}
2011-09-10 06:59:09 +00:00
if (m_graphtype==GT_BAR) {
m_miny=0;
}
// m_minx=qint64(QDateTime(PROFILE.FirstDay(),QTime(0,0,0),Qt::UTC).toTime_t())*1000L;
m_maxx=qint64(QDateTime(PROFILE.LastDay().addDays(1),QTime(0,0,0),Qt::UTC).toTime_t())*1000L;
2011-09-02 05:13:07 +00:00
}
2011-06-26 08:30:44 +00:00
QColor brighten(QColor color)
{
int cr,cg,cb;
cr=color.red();
cg=color.green();
cb=color.blue();
if (cr<64) cr=64;
if (cg<64) cg=64;
if (cb<64) cb=64;
cr*=2;
cg*=2;
cb*=2;
if (cr>255) cr=255;
if (cg>255) cg=255;
if (cb>255) cb=255;
return QColor(cr,cg,cb,255);
}
void SummaryChart::paint(gGraph & w,int left, int top, int width, int height)
2011-06-26 08:30:44 +00:00
{
if (!m_visible) return;
2011-09-02 05:13:07 +00:00
2011-09-03 12:59:08 +00:00
rtop=top;
GLShortBuffer *outlines=w.lines();
2011-09-02 05:13:07 +00:00
QColor blk=Qt::black;
2011-09-10 06:13:56 +00:00
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);
2011-09-02 02:00:04 +00:00
qint64 minx=w.min_x, maxx=w.max_x;
2011-09-02 05:13:07 +00:00
//qint64 minx=m_minx, maxx=m_maxx;
2011-09-02 02:00:04 +00:00
qint64 xx=maxx - minx;
2011-09-03 00:06:08 +00:00
float days=double(xx)/86400000.0;
2011-09-02 05:13:07 +00:00
2011-09-03 02:11:10 +00:00
EventDataType maxy=m_maxy;
EventDataType miny=m_miny;
/*if (m_codes[0]=="HumidSet") {
int i=1;
}*/
2011-09-03 02:11:10 +00:00
2011-09-03 02:51:55 +00:00
w.roundY(miny,maxy);
2011-09-03 02:11:10 +00:00
EventDataType yy=maxy-miny;
EventDataType ymult=float(height-2)/yy;
2011-09-02 02:00:04 +00:00
barw=(float(width)/float(days));
2011-09-02 02:00:04 +00:00
2011-09-02 05:13:07 +00:00
qint64 ts;
2011-09-02 02:00:04 +00:00
graph=&w;
2011-09-02 05:13:07 +00:00
float px=left;
l_left=w.m_marginleft+gYAxis::Margin;
l_top=w.m_margintop;
l_width=width;
l_height=height;
2011-09-02 05:13:07 +00:00
float py;
EventDataType total;
int daynum=0;
EventDataType h,tmp;
2011-09-02 05:13:07 +00:00
2011-09-03 01:24:11 +00:00
l_offset=(minx) % 86400000L;
offset=float(l_offset)/86400000.0;
2011-09-03 00:06:08 +00:00
offset*=barw;
px=left-offset;
l_minx=minx;
l_maxx=maxx+86400000L;
2011-09-03 00:06:08 +00:00
//QHash<short, EventDataType> lastvalues;
2011-09-03 02:11:10 +00:00
int total_days=0;
double total_val=0;
qint64 lastQ=0;
bool lastdaygood=false;
QVector<int> totalcounts;
QVector<EventDataType> totalvalues;
//QVector<EventDataType> lastvalues;
QVector<float> lastX;
QVector<short> lastY;
int numcodes=m_codes.size();
totalcounts.resize(numcodes);
totalvalues.resize(numcodes);
//lastvalues.resize(numcodes);
lastX.resize(numcodes);
lastY.resize(numcodes);
int zd=minx/86400000L;
zd--;
QHash<int,QHash<short,EventDataType> >::iterator d=m_values.find(zd);
2011-09-10 06:59:09 +00:00
// if (d==m_values.end()) {
// d=m_values.find(zd--);
// }
lastdaygood=true;
for (int i=0;i<numcodes;i++) {
totalcounts[i]=0;
if (m_type[i]==ST_MIN) {
totalvalues[i]=maxy;
} else if (m_type[i]==ST_MAX) {
totalvalues[i]=miny;
} else {
totalvalues[i]=0;
}
// lastvalues[i]=0;
lastX[i]=px;
if (d!=m_values.end()) {
tmp=d.value()[i+1];
h=tmp*ymult;
} else {
lastdaygood=false;
h=0;
}
lastY[i]=top+height-1-h;
}
2011-09-12 05:09:53 +00:00
Day * day;
2011-09-03 00:06:08 +00:00
for (qint64 Q=minx;Q<=maxx+86400000L;Q+=86400000L) {
zd=Q/86400000L;
2011-09-10 06:59:09 +00:00
d=m_values.find(zd);
2011-09-12 05:09:53 +00:00
qint64 extra=86400000;
2011-09-03 00:06:08 +00:00
if (Q<minx) continue;
2011-09-02 05:13:07 +00:00
if (d!=m_values.end()) {
2011-09-12 05:09:53 +00:00
day=m_days[zd];
int x1=px;
2011-09-13 02:51:50 +00:00
//x1-=(barw/2.0);
int x2=px+barw;
2011-09-03 02:11:10 +00:00
if (x1<left) x1=left;
if (x2>left+width) x2=left+width;
if (x2<x1) continue;
2011-09-03 00:06:08 +00:00
ChannelID code;
2011-09-03 02:11:10 +00:00
total=d.value()[0];
2011-09-18 07:42:00 +00:00
//if (total>0) {
if (day) {
2011-09-03 02:11:10 +00:00
total_val+=total;
total_days++;
}
2011-09-18 07:42:00 +00:00
//}
2011-09-03 00:06:08 +00:00
py=top+height;
2011-09-03 04:38:12 +00:00
for (QHash<short,EventDataType>::iterator g=d.value().begin();g!=d.value().end();g++) {
short j=g.key();
if (!j) continue;
j--;
QColor col=m_colors[j];
if (zd==hl_day) {
col=QColor("gold");
}
2011-09-03 04:38:12 +00:00
2011-09-18 07:42:00 +00:00
tmp=g.value();
//if (!tmp) continue;
if (m_type[j]==ST_MAX) {
if (tmp>totalvalues[j]) totalvalues[j]=tmp;
} else if (m_type[j]==ST_MIN) {
if (tmp<totalvalues[j]) totalvalues[j]=tmp;
} else {
totalvalues[j]+=tmp;
}
if (tmp)
totalcounts[j]++;
2011-09-18 07:42:00 +00:00
tmp-=miny;
2011-09-10 06:59:09 +00:00
h=tmp*ymult; // height in pixels
if (m_graphtype==GT_BAR) {
QColor col2=brighten(col);
quads->add(x1,py,x1,py-h,col);
quads->add(x2,py-h,x2,py,col2);
if (barw>2) {
outlines->add(x1,py,x1,py-h,blk);
outlines->add(x1,py-h,x2,py-h,blk);
outlines->add(x1,py,x2,py,blk);
outlines->add(x2,py,x2,py-h,blk);
} // if (bar
py-=h;
} else if (m_graphtype==GT_LINE) { // if (m_graphtype==GT_BAR
col.setAlpha(128);
2011-09-10 06:59:09 +00:00
short px2=px+barw;
short py2=top+height-1-h;
py2+=j;
if (lastdaygood) {
2011-09-10 08:26:21 +00:00
lines->add(lastX[j],lastY[j],px,py2,m_colors[j]);
lines->add(px,py2,px2,py2,col);
} else {
lines->add(x1,py2,x2,py2,col);
}
lastX[j]=px2;
lastY[j]=py2;
//}
}
} // for(QHash<short
lastdaygood=true;
if (Q>maxx+extra) break;
} else lastdaygood=false;
2011-09-02 05:13:07 +00:00
px+=barw;
daynum++;
lastQ=Q;
2011-09-02 02:00:04 +00:00
}
lines->scissor(left,w.flipY(top+height+2),width+1,height+1);
2011-09-10 07:18:46 +00:00
// Draw Ledgend
2011-09-18 07:42:00 +00:00
px=left+width-3;
2011-09-10 07:18:46 +00:00
py=top+10;
QString a;
2011-09-10 08:26:21 +00:00
int x,y;
2011-09-18 07:42:00 +00:00
2011-09-10 08:47:29 +00:00
for (int j=0;j<m_codes.size();j++) {
if (totalcounts[j]==0) continue;
2011-09-17 12:39:00 +00:00
a=schema::channel[m_codes[j]].label();
2011-09-10 07:18:46 +00:00
a+=" ";
switch(m_type[j]) {
case ST_WAVG: a+="Avg"; break;
case ST_AVG: a+="Avg"; break;
case ST_90P: a+="90%"; break;
case ST_MIN: a+="Min"; break;
case ST_MAX: a+="Max"; break;
case ST_CPH: a+=""; break;
case ST_SPH: a+="%"; break;
2011-09-10 07:18:46 +00:00
case ST_HOURS: a+="Hours"; break;
2011-09-10 08:47:29 +00:00
default:
break;
2011-09-10 07:18:46 +00:00
}
2011-09-18 07:42:00 +00:00
QString val;
float f=0;
if ((m_type[j]==ST_MIN) || (m_type[j]==ST_MAX)) {
f=totalvalues[j];
} else {
f=totalvalues[j]/float(totalcounts[j]);
}
2011-09-18 07:42:00 +00:00
if (m_type[j]==ST_HOURS) {
int h=f;
int m=int(f*60) % 60;
val.sprintf("%02i:%02i",h,m);
} else {
val=QString::number(f,'f',2);
}
a+="="+val;
2011-09-10 08:26:21 +00:00
GetTextExtent(a,x,y);
2011-09-18 07:42:00 +00:00
px-=20+x;
w.renderText(a,px+20,py+5);
quads->add(px+5,py-3,px+18,py-3,px+18,py+4,px+5,py+4,m_colors[j]);
//lines->add(px,py,px+20,py,m_colors[j]);
//lines->add(px,py+1,px+20,py+1,m_colors[j]);
2011-09-10 07:18:46 +00:00
}
2011-09-18 07:42:00 +00:00
if (m_graphtype==GT_BAR) {
if (m_type.size()>1) {
2011-09-10 07:36:53 +00:00
float val=total_val/float(total_days);
2011-09-18 14:43:15 +00:00
a=m_label+"="+QString::number(val,'f',2)+" ";
GetTextExtent(a,x,y);
px-=20+x;
w.renderText(a,px+24,py+5);
2011-09-18 07:42:00 +00:00
//
2011-09-10 07:36:53 +00:00
}
2011-06-26 08:30:44 +00:00
}
2011-09-18 14:43:15 +00:00
a="";
/*if (m_graphtype==GT_BAR) {
if (m_type.size()>1) {
float val=total_val/float(total_days);
a+=m_label+"="+QString::number(val,'f',2)+" ";
//
}
}*/
a+="Days="+QString::number(total_days,'f',0);
2011-09-18 07:42:00 +00:00
GetTextExtent(a,x,y);
px-=30+x;
//w.renderText(a,px+24,py+5);
w.renderText(a,left,top-3);
2011-06-26 08:30:44 +00:00
}
bool SummaryChart::mouseMoveEvent(QMouseEvent *event)
{
int x=event->x()-l_left;
int y=event->y()-l_top;
if ((x<0 || y<0 || x>l_width || y>l_height)) {
hl_day=-1;
//graph->timedRedraw(2000);
return false;
}
double xx=l_maxx-l_minx;
double xmult=xx/double(l_width+barw);
2011-09-05 13:26:10 +00:00
qint64 mx=ceil(xmult*double(x-offset));
mx+=l_minx;
mx=mx+l_offset;//-86400000L;
int zd=mx/86400000L;
2011-09-12 05:09:53 +00:00
Day * day;
2011-09-12 05:09:53 +00:00
//if (hl_day!=zd) // This line is an optimization
{
hl_day=zd;
QHash<int,QHash<short,EventDataType> >::iterator d=m_values.find(hl_day);
2011-09-13 04:52:30 +00:00
x+=gYAxis::Margin+gGraphView::titleWidth; //graph->m_marginleft+
int y=event->y()+rtop-15;
2011-09-13 05:18:39 +00:00
QDateTime dt1=QDateTime::fromTime_t(hl_day*86400).toLocalTime();
QDateTime dt2=QDateTime::fromTime_t(hl_day*86400).toUTC();
QTime t1=dt1.time();
QTime t2=dt2.time();
QDate dt=dt2.date();
if (d!=m_values.end()) {
2011-09-03 12:59:08 +00:00
day=m_days[zd];
2011-09-18 03:47:53 +00:00
QString z=dt.toString(Qt::SystemLocaleShortDate);
2011-09-10 08:31:28 +00:00
2011-09-12 05:09:53 +00:00
// Day * day=m_days[hl_day];
//EventDataType val;
QString val;
2011-09-10 08:31:28 +00:00
if (m_graphtype==GT_BAR) {
if (m_type[0]==ST_HOURS) {
int t=d.value()[0]*3600.0;
int h=t/3600;
int m=(t / 60) % 60;
int s=t % 60;
2011-09-13 04:45:40 +00:00
val.sprintf("%02i:%02i",h,m);
2011-09-18 03:47:53 +00:00
} else
val=QString::number(d.value()[0],'f',2);
z+="\r\n"+m_label+"="+val;
2011-09-18 15:43:14 +00:00
//z+="\r\nMode="+QString::number(day->settings_min("FlexSet"),'f',0);
2011-09-18 03:47:53 +00:00
2011-09-10 08:31:28 +00:00
} else {
2011-09-18 03:47:53 +00:00
QString a;
for (int i=0;i<m_type.size();i++) {
switch(m_type[i]) {
case ST_WAVG: a="W-avg"; break;
case ST_AVG: a="Avg"; break;
case ST_90P: a="90%"; break;
case ST_MIN: a="Min"; break;
case ST_MAX: a="Max"; break;
case ST_CPH: a=""; break;
case ST_SPH: a="%"; break;
case ST_HOURS: a="Hours"; break;
default:
break;
}
2011-09-18 15:43:14 +00:00
if (day && (day->channelExists(m_codes[i]) || day->settingExists(m_codes[i]))) {
2011-09-18 03:47:53 +00:00
schema::Channel & chan=schema::channel[m_codes[i]];
val=QString::number(d.value()[i+1],'f',2);
z+="\r\n"+chan.label()+" "+a+"="+val;
}
}
2011-09-10 08:31:28 +00:00
}
graph->ToolTip(z,x,y-10,2200);
2011-09-03 12:59:08 +00:00
return true;
2011-09-13 04:52:30 +00:00
} else {
QString z=dt.toString(Qt::SystemLocaleShortDate)+"\r\nNo Data";
graph->ToolTip(z,x,y-10,2200);
2011-09-03 12:59:08 +00:00
}
}
return false;
}
bool SummaryChart::mousePressEvent(QMouseEvent * event)
{
return false;
}
bool SummaryChart::mouseReleaseEvent(QMouseEvent * event)
{
hl_day=-1;
graph->timedRedraw(2000);
return false;
}