1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
5
6#include "private/glxyseriesdata_p.h"
7#include "private/abstractdomain_p.h"
8#if QT_CONFIG(charts_scatter_chart)
9#include <QtCharts/QScatterSeries>
10#endif
11
12QT_BEGIN_NAMESPACE
13
14GLXYSeriesDataManager::GLXYSeriesDataManager(QObject *parent)
15 : QObject(parent),
16 m_mapDirty(false)
17{
18}
19
20GLXYSeriesDataManager::~GLXYSeriesDataManager()
21{
22 cleanup();
23}
24
25void GLXYSeriesDataManager::setPoints(QXYSeries *series, const AbstractDomain *domain)
26{
27 GLXYSeriesData *data = m_seriesDataMap.value(key: series);
28 if (!data) {
29 data = new GLXYSeriesData;
30 data->type = series->type();
31 data->visible = series->isVisible();
32 QColor sc;
33#if QT_CONFIG(charts_scatter_chart)
34 if (data->type == QAbstractSeries::SeriesTypeScatter) {
35 QScatterSeries *scatter = static_cast<QScatterSeries *>(series);
36 data->width = float(scatter->markerSize());
37 sc = scatter->color(); // Scatter overwrites color property
38 connect(sender: scatter, signal: &QScatterSeries::colorChanged, context: this,
39 slot: &GLXYSeriesDataManager::handleScatterColorChange);
40 connect(sender: scatter, signal: &QScatterSeries::markerSizeChanged, context: this,
41 slot: &GLXYSeriesDataManager::handleScatterMarkerSizeChange);
42 } else
43#endif
44 {
45 data->width = float(series->pen().widthF());
46 sc = series->color();
47 connect(sender: series, signal: &QXYSeries::penChanged, context: this,
48 slot: &GLXYSeriesDataManager::handleSeriesPenChange);
49 }
50 data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
51 connect(sender: series, signal: &QXYSeries::useOpenGLChanged, context: this,
52 slot: &GLXYSeriesDataManager::handleSeriesOpenGLChange);
53 connect(sender: series, signal: &QXYSeries::visibleChanged, context: this,
54 slot: &GLXYSeriesDataManager::handleSeriesVisibilityChange);
55 m_seriesDataMap.insert(key: series, value: data);
56 m_mapDirty = true;
57 }
58 QList<float> &array = data->array;
59
60 bool logAxis = false;
61 bool reverseX = false;
62 bool reverseY = false;
63 const auto attached = series->attachedAxes();
64 for (QAbstractAxis *axis : attached) {
65 if (axis->type() == QAbstractAxis::AxisTypeLogValue) {
66 logAxis = true;
67 break;
68 }
69 if (axis->isReverse()) {
70 if (axis->orientation() == Qt::Horizontal)
71 reverseX = true;
72 else
73 reverseY = true;
74 if (reverseX && reverseY)
75 break;
76 }
77 }
78 int count = series->count();
79 int index = 0;
80 array.resize(size: count * 2);
81 QMatrix4x4 matrix;
82 if (logAxis) {
83 // Use domain to resolve geometry points. Not as fast as shaders, but simpler that way
84 QList<QPointF> geometryPoints = domain->calculateGeometryPoints(list: series->points());
85 const float height = domain->size().height();
86 if (geometryPoints.size()) {
87 for (int i = 0; i < count; i++) {
88 const QPointF &point = geometryPoints.at(i);
89 array[index++] = float(point.x());
90 array[index++] = float(height - point.y());
91 }
92 } else {
93 // If there are invalid log values, geometry points generation fails
94 for (int i = 0; i < count; i++) {
95 array[index++] = 0.0f;
96 array[index++] = 0.0f;
97 }
98 }
99 data->min = QVector2D(0, 0);
100 data->delta = QVector2D(domain->size().width() / 2.0f, domain->size().height() / 2.0f);
101 } else {
102 // Regular value axes, so we can optimize it a bit.
103 if (reverseX)
104 matrix.scale(x: -1.0, y: 1.0);
105 if (reverseY)
106 matrix.scale(x: 1.0, y: -1.0);
107
108 const qreal mx = domain->minX();
109 const qreal my = domain->minY();
110 const qreal xd = domain->maxX() - mx;
111 const qreal yd = domain->maxY() - my;
112
113 if (!qFuzzyIsNull(d: xd) && !qFuzzyIsNull(d: yd)) {
114 const QList<QPointF> seriesPoints = series->points();
115 for (const QPointF &point : seriesPoints) {
116 array[index++] = float((point.x() - mx) / xd);
117 array[index++] = float((point.y() - my) / yd);
118 }
119 }
120 data->min = QVector2D(0.0f, 0.0f);
121 data->delta = QVector2D(0.5f, 0.5f);
122 }
123 data->matrix = matrix;
124 data->dirty = true;
125}
126
127void GLXYSeriesDataManager::removeSeries(const QXYSeries *series)
128{
129 GLXYSeriesData *data = m_seriesDataMap.take(key: series);
130 if (data) {
131 disconnect(sender: series, signal: 0, receiver: this, member: 0);
132 delete data;
133 emit seriesRemoved(series);
134 m_mapDirty = true;
135 }
136}
137
138void GLXYSeriesDataManager::cleanup()
139{
140 foreach (GLXYSeriesData *data, m_seriesDataMap.values())
141 delete data;
142 m_seriesDataMap.clear();
143 m_mapDirty = true;
144 // Signal all series removal by using zero as parameter
145 emit seriesRemoved(series: 0);
146}
147
148void GLXYSeriesDataManager::handleSeriesPenChange()
149{
150 QXYSeries *series = qobject_cast<QXYSeries *>(object: sender());
151 if (series) {
152 GLXYSeriesData *data = m_seriesDataMap.value(key: series);
153 if (data) {
154 QColor sc = series->color();
155 data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
156 data->width = float(series->pen().widthF());
157 data->dirty = true;
158 }
159 }
160}
161
162void GLXYSeriesDataManager::handleSeriesOpenGLChange()
163{
164 QXYSeries *series = qobject_cast<QXYSeries *>(object: sender());
165 if (!series->useOpenGL())
166 removeSeries(series);
167}
168
169void GLXYSeriesDataManager::handleSeriesVisibilityChange()
170{
171 QXYSeries *series = qobject_cast<QXYSeries *>(object: sender());
172 if (series) {
173 GLXYSeriesData *data = m_seriesDataMap.value(key: series);
174 if (data) {
175 data->visible = series->isVisible();
176 data->dirty = true;
177 }
178 }
179}
180
181#if QT_CONFIG(charts_scatter_chart)
182void GLXYSeriesDataManager::handleScatterColorChange()
183{
184 QScatterSeries *series = qobject_cast<QScatterSeries *>(object: sender());
185 if (series) {
186 GLXYSeriesData *data = m_seriesDataMap.value(key: series);
187 if (data) {
188 QColor sc = series->color();
189 data->color = QVector3D(float(sc.redF()), float(sc.greenF()), float(sc.blueF()));
190 data->dirty = true;
191 }
192 }
193}
194
195void GLXYSeriesDataManager::handleScatterMarkerSizeChange()
196{
197 QScatterSeries *series = qobject_cast<QScatterSeries *>(object: sender());
198 if (series) {
199 GLXYSeriesData *data = m_seriesDataMap.value(key: series);
200 if (data) {
201 data->width =float(series->markerSize());
202 data->dirty = true;
203 }
204 }
205}
206#endif
207
208void GLXYSeriesDataManager::handleAxisReverseChanged(const QList<QAbstractSeries *> &seriesList)
209{
210 bool reverseX = false;
211 bool reverseY = false;
212 foreach (QAbstractSeries *series, seriesList) {
213 if (QXYSeries *xyseries = qobject_cast<QXYSeries *>(object: series)) {
214 GLXYSeriesData *data = m_seriesDataMap.value(key: xyseries);
215 if (data) {
216 foreach (QAbstractAxis* axis, xyseries->attachedAxes()) {
217 if (axis->isReverse()) {
218 if (axis->orientation() == Qt::Horizontal)
219 reverseX = true;
220 else
221 reverseY = true;
222 }
223 if (reverseX && reverseY)
224 break;
225 }
226 QMatrix4x4 matrix;
227 if (reverseX)
228 matrix.scale(x: -1.0, y: 1.0);
229 if (reverseY)
230 matrix.scale(x: 1.0, y: -1.0);
231 data->matrix = matrix;
232 data->dirty = true;
233 }
234 }
235 }
236}
237
238QT_END_NAMESPACE
239
240

source code of qtcharts/src/charts/xychart/glxyseriesdata.cpp