1 /*
2     This file is part of the KDE project
3 
4     SPDX-FileCopyrightText: 2014 Fredrik Höglund <fredrik@kde.org>
5     SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org>
6 
7     SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #ifndef PLASMA_PLOTTER_H
11 #define PLASMA_PLOTTER_H
12 
13 #include <epoxy/gl.h>
14 
15 // qopengl.h declares GLdouble as a typedef of float when Qt is built
16 // with GLES support.  This conflicts with the epoxy/gl_generated.h
17 // declaration, so we have to prevent the Qt header from being #included.
18 #define QOPENGL_H
19 
20 #ifndef QOPENGLF_APIENTRY
21 #define QOPENGLF_APIENTRY GLAPIENTRY
22 #endif
23 
24 #ifndef QOPENGLF_APIENTRYP
25 #define QOPENGLF_APIENTRYP GLAPIENTRYP
26 #endif
27 
28 #include <QMutex>
29 #include <QPointer>
30 #include <QQmlListProperty>
31 #include <QQuickItem>
32 #include <QQuickWindow>
33 
34 class PlotSGNode;
35 
36 /**
37  * a Plotter can draw a graph of values arriving from an arbitrary number of data sources
38  * to show their evolution in time.
39  * an example can be a plot of the network transfer speed or CPU temperature over time.
40  * Multiple plots can be fitted in the same graph, either stacked or intersected.
41  */
42 class PlotData : public QObject
43 {
44     Q_OBJECT
45     /**
46      * text Label of the data set: note this is purely a model, it will need a Label somewhere to be actually painted
47      */
48     Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
49 
50     /**
51      * Color to plot this data set
52      */
53     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
54 
55     /**
56      * All the values currently in this data set
57      */
58     Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
59 
60     /**
61      * Maximum value of this data set
62      */
63     Q_PROPERTY(qreal max READ max NOTIFY maxChanged)
64 
65     /**
66      * Minimum value of this data set
67      */
68     Q_PROPERTY(qreal min READ min NOTIFY minChanged)
69 
70 public:
71     PlotData(QObject *parent = nullptr);
72 
73     void setColor(const QColor &color);
74     QColor color() const;
75 
76     void addSample(qreal value);
77 
78     QList<qreal> values() const;
79 
80     QVector<qreal> m_normalizedValues;
81 
82     qreal max() const;
83     qreal min() const;
84 
85     void setSampleSize(int size);
86 
87     QString label() const;
88     void setLabel(const QString &label);
89 
90 Q_SIGNALS:
91     void colorChanged();
92     void valuesChanged();
93     void maxChanged();
94     void minChanged();
95     void labelChanged();
96 
97 private:
98     QString m_label;
99     QColor m_color;
100     QList<qreal> m_values;
101 
102     qreal m_min;
103     qreal m_max;
104     int m_sampleSize;
105 };
106 
107 class Plotter : public QQuickItem
108 {
109     Q_OBJECT
110     Q_PROPERTY(QQmlListProperty<PlotData> dataSets READ dataSets)
111 
112     /**
113      * maximum value among all graphs
114      */
115     Q_PROPERTY(qreal max READ max NOTIFY maxChanged)
116 
117     /**
118      * minimum value among all graphs
119      */
120     Q_PROPERTY(qreal min READ min NOTIFY minChanged)
121 
122     /**
123      * draw at most n samples, if new samples are pushed old values are started to be thrown away
124      */
125     Q_PROPERTY(int sampleSize READ sampleSize WRITE setSampleSize NOTIFY sampleSizeChanged)
126 
127     /**
128      * if true stack the graphs one on top of each other instead of just painting one on top of each other
129      */
130     Q_PROPERTY(bool stacked READ isStacked WRITE setStacked NOTIFY stackedChanged)
131 
132     /**
133      * If true, the graph is automatically scaled to always fit in the Plotter area
134      */
135     Q_PROPERTY(bool autoRange READ isAutoRange WRITE setAutoRange NOTIFY autoRangeChanged)
136 
137     /**
138      * Always represents the values scaled between this and rangeMin
139      * If autoRange is true, this property just holds the minimum value
140      */
141     Q_PROPERTY(qreal rangeMax READ rangeMax WRITE setRangeMax NOTIFY rangeMaxChanged)
142 
143     /**
144      * Always represents the values scaled between this and rangeMax
145      * If autoRange is true, this property just holds the maximum value
146      */
147     Q_PROPERTY(qreal rangeMin READ rangeMin WRITE setRangeMin NOTIFY rangeMinChanged)
148 
149     /**
150      * Color of the grid lines
151      */
152     Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor NOTIFY gridColorChanged)
153 
154     /**
155      * The number of horizontal lines drawn across the view between 0 and rangeMax at the top of the plotter at rangeMax
156      * This does not include the bottom line
157      * Setting this to 0 will disable grid lines
158      *
159      * The default value is 5
160      */
161     Q_PROPERTY(int horizontalGridLineCount READ horizontalGridLineCount WRITE setHorizontalGridLineCount NOTIFY horizontalGridLineCountChanged)
162 
163     // Q_CLASSINFO("DefaultProperty", "dataSets")
164 
165 public:
166     Plotter(QQuickItem *parent = nullptr);
167     ~Plotter() override;
168 
169     qreal max() const;
170     qreal min() const;
171 
172     int sampleSize() const;
173     void setSampleSize(int size);
174 
175     bool isStacked() const;
176     void setStacked(bool stacked);
177 
178     bool isAutoRange() const;
179     void setAutoRange(bool autorange);
180 
181     qreal rangeMax() const;
182     void setRangeMax(qreal max);
183 
184     qreal rangeMin() const;
185     void setRangeMin(qreal min);
186 
187     void setHorizontalGridLineCount(int count);
188     int horizontalGridLineCount();
189 
190     void setGridColor(const QColor &color);
191     QColor gridColor() const;
192 
193     QQmlListProperty<PlotData> dataSets();
194     static void dataSet_append(QQmlListProperty<PlotData> *list, PlotData *item);
195     static int dataSet_count(QQmlListProperty<PlotData> *list);
196     static PlotData *dataSet_at(QQmlListProperty<PlotData> *list, int pos);
197     static void dataSet_clear(QQmlListProperty<PlotData> *list);
198 
199     Q_INVOKABLE void addSample(qreal value);
200     Q_INVOKABLE void addSample(const QList<qreal> &value);
201 
202 protected:
203     void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
204 
205 private:
206     QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override final;
207     QPainterPath interpolate(const QVector<qreal> &p, qreal x0, qreal x1) const;
208     void normalizeData();
209 
210 Q_SIGNALS:
211     void maxChanged();
212     void minChanged();
213     void sampleSizeChanged();
214     void stackedChanged();
215     void autoRangeChanged();
216     void rangeMaxChanged();
217     void rangeMinChanged();
218     void gridColorChanged();
219     void horizontalGridLineCountChanged();
220 
221 private Q_SLOTS:
222     void render();
223 
224 private:
225     QList<PlotData *> m_plotData;
226 
227     GLuint m_fbo = 0;
228     PlotSGNode *m_node = nullptr;
229     qreal m_min;
230     qreal m_max;
231     qreal m_rangeMax;
232     qreal m_rangeMin;
233     int m_sampleSize;
234     int m_horizontalLineCount;
235     bool m_stacked;
236     bool m_autoRange;
237     QColor m_gridColor;
238 
239     QMatrix4x4 m_matrix;
240     bool m_initialized = false;
241     bool m_haveMSAA;
242     bool m_haveFramebufferBlit;
243     bool m_haveInternalFormatQuery;
244     GLenum m_internalFormat;
245     int m_samples;
246     QPointer<QQuickWindow> m_window;
247     QMutex m_mutex;
248 };
249 
250 #endif
251