/* gSessionTimesChart Header * * Copyright (c) 2019-2024 The Oscar Team * Copyright (C) 2011-2018 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 source code * for more details. */ #if 1 #ifndef GSUMMARYCHART_H #define GSUMMARYCHART_H #include "SleepLib/day.h" #include "SleepLib/profiles.h" #include "Graphs/gGraphView.h" #include "SleepLib/appsettings.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) { } float mid() { float val = 0; switch (midcalc) { case 0: if (median_data.size() > 0) val = median(median_data.begin(), median_data.end()); break; case 1: if (divisor > 0) val = wavg_sum / divisor; break; case 2: if (cnt > 0) val = avg_sum / cnt; } return val; } inline void update(float value, float weight) { if (midcalc == 0) { median_data.append(value); } avg_sum += value; cnt++; wavg_sum += value * weight; divisor += weight; min = qMin(min, value); max = qMax(max, value); } void reset(int reserve, short mc) { midcalc = mc; 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 QObject , public Layer { Q_OBJECT; 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; } //! \brief Allows chart to recalculate empty flag. void reCalculate() {m_empty=false;}; 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 *, QVector &); //! \brief Override to call stuff after draw is complete virtual void afterDraw(QPainter &, gGraph &, QRectF); //! \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(); } virtual int addCalc(ChannelID code, SummaryType type, QColor color); virtual int addCalc(ChannelID code, SummaryType type); 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; } signals: void summaryChartEmpty(gSummaryChart*,qint64,qint64,bool); 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 durationInHoursToHhMmSs(double duration); QString durationInMinutesToHhMmSs(double duration); QString durationInSecondsToHhMmSs(double duration); QString m_label; MachineType m_machtype; bool m_empty; bool m_emptyPrev; int hl_day; int tz_offset; float tz_hours; QDate firstday; QDate lastday; QMap dayindex; QList daylist; QHash > cache; QVector calcitems; int expected_slices; int nousedays; int totaldays; EventDataType peak_value; EventDataType min_value; int idx_start; int idx_end; short midcalc; }; #endif // GSUMMARYCHART_H #endif