/* gSessionTimesChart Header * * Copyright (c) 2011-2014 Mark Watkins * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ #ifndef GSESSIONTIMESCHART_H #define GSESSIONTIMESCHART_H #include "SleepLib/day.h" #include "SleepLib/profiles.h" #include "gGraphView.h" struct TimeSpan { public: TimeSpan():begin(0), end(0) {} TimeSpan(float b, float e) : begin(b), end(e) {} TimeSpan(const TimeSpan & copy) { begin = copy.begin; end = copy.end; } ~TimeSpan() {} float begin; float end; }; struct SummaryCalcItem { SummaryCalcItem() { code = 0; type = ST_CNT; color = Qt::black; wavg_sum = 0; avg_sum = 0; cnt = 0; divisor = 0; min = 0; max = 0; } SummaryCalcItem(const SummaryCalcItem & copy) { code = copy.code; type = copy.type; color = copy.color; wavg_sum = 0; avg_sum = 0; cnt = 0; divisor = 0; min = 0; max = 0; midcalc = p_profile->general->prefCalcMiddle(); } SummaryCalcItem(ChannelID code, SummaryType type, QColor color) :code(code), type(type), color(color) { } inline void update(float value, float weight) { switch (midcalc) { case 0: median_data.append(value); break; case 1: wavg_sum += value * weight; divisor += weight; break; default: avg_sum += value; cnt++; } min = qMin(min, value); max = qMax(max, value); } void reset(int reserve) { midcalc = p_profile->general->prefCalcMiddle(); wavg_sum = 0; avg_sum = 0; divisor = 0; cnt = 0; min = 99999; max = -99999; median_data.clear(); if (midcalc == 0) { median_data.reserve(reserve); } } ChannelID code; SummaryType type; QColor color; double wavg_sum; double divisor; double avg_sum; int cnt; EventDataType min; EventDataType max; static short midcalc; QList median_data; }; struct SummaryChartSlice { SummaryChartSlice() { calc = nullptr; height = 0; value = 0; name = ST_CNT; color = Qt::black; } SummaryChartSlice(const SummaryChartSlice & copy) { calc = copy.calc; value = copy.value; height = copy.height; name = copy.name; color = copy.color; // brush = copy.brush; } SummaryChartSlice(SummaryCalcItem * calc, EventDataType value, EventDataType height, QString name, QColor color) :calc(calc), value(value), height(height), name(name), color(color) { // QLinearGradient gradient(0, 0, 1, 0); // gradient.setCoordinateMode(QGradient::ObjectBoundingMode); // gradient.setColorAt(0,color); // gradient.setColorAt(1,brighten(color)); // brush = QBrush(gradient); } SummaryCalcItem * calc; EventDataType value; EventDataType height; QString name; QColor color; // QBrush brush; }; class gSummaryChart : public Layer { public: gSummaryChart(QString label, MachineType machtype); gSummaryChart(ChannelID code, MachineType machtype); virtual ~gSummaryChart(); //! \brief Renders the graph to the QPainter object virtual void paint(QPainter &, gGraph &, const QRegion &); //! \brief Called whenever data model changes underneath. Day object is not needed here, it's just here for Layer compatability. virtual void SetDay(Day *day = nullptr); //! \brief Returns true if no data was found for this day during SetDay virtual bool isEmpty() { return m_empty; } virtual void populate(Day *, int idx); //! \brief Override to setup custom stuff before main loop virtual void preCalc(); //! \brief Override to call stuff in main loop virtual void customCalc(Day *, QList &); //! \brief Override to call stuff after draw is complete virtual void afterDraw(QPainter &, gGraph &, QRect); //! \brief Return any extra data to show beneath the date in the hover over tooltip virtual QString tooltipData(Day *, int); virtual void dataChanged() { cache.clear(); } void addCalc(ChannelID code, SummaryType type, QColor color) { calcitems.append(SummaryCalcItem(code, type, color)); } void addCalc(ChannelID code, SummaryType type) { calcitems.append(SummaryCalcItem(code, type, schema::channel[code].defaultColor())); } virtual Layer * Clone() { gSummaryChart * sc = new gSummaryChart(m_label, m_machtype); Layer::CloneInto(sc); CloneInto(sc); // copy this here, because only base summary charts need it sc->calcitems = calcitems; return sc; } void CloneInto(gSummaryChart * layer) { layer->m_empty = m_empty; layer->firstday = firstday; layer->lastday = lastday; layer->expected_slices = expected_slices; layer->nousedays = nousedays; layer->totaldays = totaldays; layer->peak_value = peak_value; layer->idx_start = idx_start; layer->idx_end = idx_end; layer->cache.clear(); layer->dayindex = dayindex; layer->daylist = daylist; } protected: //! \brief Key was pressed that effects this layer virtual bool keyPressEvent(QKeyEvent *event, gGraph *graph); //! \brief Mouse moved over this layers area (shows the hover-over tooltips here) virtual bool mouseMoveEvent(QMouseEvent *event, gGraph *graph); //! \brief Mouse Button was pressed over this area virtual bool mousePressEvent(QMouseEvent *event, gGraph *graph); //! \brief Mouse Button was released over this area. (jumps to daily view here) virtual bool mouseReleaseEvent(QMouseEvent *event, gGraph *graph); QString m_label; MachineType m_machtype; bool m_empty; int hl_day; int tz_offset; float tz_hours; QDate firstday; QDate lastday; QMap dayindex; QList daylist; QHash > cache; QList calcitems; int expected_slices; int nousedays; int totaldays; EventDataType peak_value; EventDataType min_value; int idx_start; int idx_end; short midcalc; }; /*! \class gSessionTimesChart \brief Displays a summary of session times */ class gSessionTimesChart : public gSummaryChart { public: gSessionTimesChart() :gSummaryChart("SessionTimes", MT_CPAP) { addCalc(NoChannel, ST_SESSIONS, QColor(64,128,255)); addCalc(NoChannel, ST_SESSIONS, QColor(64,128,255)); addCalc(NoChannel, ST_SESSIONS, QColor(64,128,255)); } virtual ~gSessionTimesChart() {} virtual void SetDay(Day * day = nullptr) { gSummaryChart::SetDay(day); split = p_profile->session->daySplitTime(); m_miny = 0; m_maxy = 28; } virtual void preCalc(); virtual void customCalc(Day *, QList & slices); virtual void afterDraw(QPainter &, gGraph &, QRect); //! \brief Renders the graph to the QPainter object virtual void paint(QPainter &painter, gGraph &graph, const QRegion ®ion); virtual Layer * Clone() { gSessionTimesChart * sc = new gSessionTimesChart(); gSummaryChart::CloneInto(sc); CloneInto(sc); return sc; } void CloneInto(gSessionTimesChart * layer) { layer->split = split; } QTime split; int num_slices; int num_days; int total_slices; double total_length; QList session_data; }; class gUsageChart : public gSummaryChart { public: gUsageChart() :gSummaryChart("Usage", MT_CPAP) { addCalc(NoChannel, ST_HOURS, QColor(64,128,255)); } virtual ~gUsageChart() {} virtual void preCalc(); virtual void customCalc(Day *, QList &); virtual void afterDraw(QPainter &, gGraph &, QRect); virtual void populate(Day *day, int idx); virtual QString tooltipData(Day * day, int); virtual Layer * Clone() { gUsageChart * sc = new gUsageChart(); gSummaryChart::CloneInto(sc); CloneInto(sc); return sc; } void CloneInto(gUsageChart * layer) { layer->incompdays = incompdays; layer->compliance_threshold = compliance_threshold; } private: int incompdays; EventDataType compliance_threshold; }; class gTTIAChart : public gSummaryChart { public: gTTIAChart() :gSummaryChart("TTIA", MT_CPAP) { addCalc(NoChannel, ST_CNT, QColor(255,147,150)); } virtual ~gTTIAChart() {} virtual void preCalc(); virtual void customCalc(Day *, QList &); virtual void afterDraw(QPainter &, gGraph &, QRect); virtual void populate(Day *day, int idx); virtual QString tooltipData(Day * day, int); virtual Layer * Clone() { gTTIAChart * sc = new gTTIAChart(); gSummaryChart::CloneInto(sc); CloneInto(sc); return sc; } void CloneInto(gTTIAChart * /* layer*/) { } private: }; class gAHIChart : public gSummaryChart { public: gAHIChart() :gSummaryChart("AHIChart", MT_CPAP) { addCalc(CPAP_ClearAirway, ST_CPH); addCalc(CPAP_Obstructive, ST_CPH); addCalc(CPAP_Apnea, ST_CPH); addCalc(CPAP_Hypopnea, ST_CPH); if (p_profile->general->calculateRDI()) addCalc(CPAP_RERA, ST_CPH); } virtual ~gAHIChart() {} virtual void preCalc(); virtual void customCalc(Day *, QList &); virtual void afterDraw(QPainter &, gGraph &, QRect); virtual void populate(Day *, int idx); virtual QString tooltipData(Day * day, int); virtual Layer * Clone() { gAHIChart * sc = new gAHIChart(); gSummaryChart::CloneInto(sc); CloneInto(sc); return sc; } void CloneInto(gAHIChart * /* layer */) { // layer->ahicalc = ahicalc; // layer->ahi_wavg = ahi_wavg; // layer->ahi_avg = ahi_avg; // layer->total_hours = total_hours; // layer->max_ahi = max_ahi; // layer->min_ahi = min_ahi; // layer->calc_cnt = calc_cnt; // layer->ahi_data = ahi_data; } // SummaryCalcItem ahicalc; double ahi_wavg; double ahi_avg; double total_hours; float max_ahi; float min_ahi; int calc_cnt; QList ahi_data; }; class gPressureChart : public gSummaryChart { public: gPressureChart(); virtual ~gPressureChart() {} virtual Layer * Clone() { gPressureChart * sc = new gPressureChart(); gSummaryChart::CloneInto(sc); return sc; } // virtual void afterDraw(QPainter &, gGraph &, QRect); virtual void populate(Day * day, int idx); virtual QString tooltipData(Day * day, int idx) { return day->getCPAPMode() + "\n" + day->getPressureSettings() + gSummaryChart::tooltipData(day, idx); } }; #endif // GSESSIONTIMESCHART_H