1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qabstract3daxis_p.h"
5#include "qscatter3dseries_p.h"
6#include "qscatterdataproxy_p.h"
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 * \class QScatterDataProxy
12 * \inmodule QtGraphs
13 * \ingroup graphs_3D
14 * \brief The QScatterDataProxy class is the data proxy for 3D scatter graphs.
15 *
16 * A scatter data proxy handles adding, inserting, changing, and removing data
17 * items. Since data is stored in series, it is necessary
18 * to create a series associated with the proxy before using these functions for
19 * the dataset.
20 *
21 * QScatterDataProxy takes ownership of all
22 * QtGraphs::QScatterDataArray and QScatterDataItem objects passed to
23 * it.
24 *
25 * \sa {Qt Graphs Data Handling with 3D}
26 */
27
28/*!
29 * \typealias QScatterDataArray
30 * \relates QScatterDataProxy
31 *
32 * A list of \l {QScatterDataItem} objects.
33 */
34
35/*!
36 * \qmltype ScatterDataProxy
37 * \inqmlmodule QtGraphs
38 * \ingroup graphs_qml_3D
39 * \nativetype QScatterDataProxy
40 * \inherits AbstractDataProxy
41 * \brief The data proxy for 3D scatter graphs.
42 *
43 * This type handles adding, inserting, changing, and removing data items.
44 *
45 * This type is uncreatable, but contains properties that are exposed via
46 * subtypes.
47 *
48 * \sa ItemModelScatterDataProxy, {Qt Graphs Data Handling with 3D}
49 */
50
51/*!
52 * \qmlproperty int ScatterDataProxy::itemCount
53 * The number of items in the array.
54 */
55
56/*!
57 * \qmlproperty Scatter3DSeries ScatterDataProxy::series
58 *
59 * The series this proxy is attached to.
60 */
61
62/*!
63 \qmlsignal ScatterDataProxy::itemCountChanged(int count)
64
65 This signal is emitted when itemCount changes to \a count.
66*/
67
68/*!
69 \qmlsignal ScatterDataProxy::seriesChanged(Scatter3DSeries series)
70
71 This signal is emitted when \l series changes to \a series.
72*/
73
74/*!
75 * Constructs QScatterDataProxy with the given \a parent.
76 */
77QScatterDataProxy::QScatterDataProxy(QObject *parent)
78 : QAbstractDataProxy(*(new QScatterDataProxyPrivate()), parent)
79{}
80
81/*!
82 * \internal
83 */
84QScatterDataProxy::QScatterDataProxy(QScatterDataProxyPrivate &d, QObject *parent)
85 : QAbstractDataProxy(d, parent)
86{}
87
88/*!
89 * Deletes the scatter data proxy.
90 */
91QScatterDataProxy::~QScatterDataProxy() {}
92
93/*!
94 * \property QScatterDataProxy::series
95 *
96 * \brief The series this proxy is attached to.
97 */
98QScatter3DSeries *QScatterDataProxy::series() const
99{
100 Q_D(const QScatterDataProxy);
101 if (!d->series())
102 qWarning(msg: "Series needs to be created to access data members");
103 return static_cast<QScatter3DSeries *>(d->series());
104}
105
106/*!
107 * Clears the existing array and triggers the arrayReset() signal.
108 */
109void QScatterDataProxy::resetArray()
110{
111 series()->clearArray();
112
113 emit arrayReset();
114 emit itemCountChanged(count: itemCount());
115}
116
117/*!
118 * Sets the array from \a newArray. If the new array is equal to the
119 * existing one, this function simply triggers the arrayReset() signal.
120 */
121void QScatterDataProxy::resetArray(QScatterDataArray newArray)
122{
123 Q_D(QScatterDataProxy);
124 if (!series())
125 return;
126
127 if (series()->dataArray().data() != newArray.data())
128 d->resetArray(newArray: std::move(newArray));
129
130 emit arrayReset();
131 emit itemCountChanged(count: itemCount());
132}
133
134/*!
135 * Replaces the item at the position \a index with the item \a item.
136 */
137void QScatterDataProxy::setItem(qsizetype index, QScatterDataItem item)
138{
139 Q_D(QScatterDataProxy);
140 d->setItem(index, item: std::move(item));
141 emit itemsChanged(startIndex: index, count: 1);
142}
143
144/*!
145 * Replaces the items starting from the position \a index with the items
146 * specified by \a items.
147 */
148void QScatterDataProxy::setItems(qsizetype index, QScatterDataArray items)
149{
150 Q_D(QScatterDataProxy);
151 d->setItems(index, items: std::move(items));
152 emit itemsChanged(startIndex: index, count: items.size());
153}
154
155/*!
156 * Adds the item \a item to the end of the array.
157 *
158 * Returns the index of the added item.
159 */
160qsizetype QScatterDataProxy::addItem(QScatterDataItem item)
161{
162 Q_D(QScatterDataProxy);
163 qsizetype addIndex = d->addItem(item: std::move(item));
164 emit itemsAdded(startIndex: addIndex, count: 1);
165 emit itemCountChanged(count: itemCount());
166 return addIndex;
167}
168
169/*!
170 * Adds the items specified by \a items to the end of the array.
171 *
172 * Returns the index of the first added item.
173 */
174qsizetype QScatterDataProxy::addItems(QScatterDataArray items)
175{
176 Q_D(QScatterDataProxy);
177 qsizetype addIndex = d->addItems(items: std::move(items));
178 emit itemsAdded(startIndex: addIndex, count: items.size());
179 emit itemCountChanged(count: itemCount());
180 return addIndex;
181}
182
183/*!
184 * Inserts the item \a item to the position \a index. If the index is equal to
185 * the data array size, the item is added to the array.
186 */
187void QScatterDataProxy::insertItem(qsizetype index, QScatterDataItem item)
188{
189 Q_D(QScatterDataProxy);
190 d->insertItem(index, item: std::move(item));
191 emit itemsInserted(startIndex: index, count: 1);
192 emit itemCountChanged(count: itemCount());
193}
194
195/*!
196 * Inserts the items specified by \a items to the position \a index. If the
197 * index is equal to data array size, the items are added to the array.
198 */
199void QScatterDataProxy::insertItems(qsizetype index, QScatterDataArray items)
200{
201 Q_D(QScatterDataProxy);
202 d->insertItems(index, items: std::move(items));
203 emit itemsInserted(startIndex: index, count: items.size());
204 emit itemCountChanged(count: itemCount());
205}
206
207/*!
208 * Removes the number of items specified by \a removeCount starting at the
209 * position \a index. Attempting to remove items past the end of
210 * the array does nothing.
211 */
212void QScatterDataProxy::removeItems(qsizetype index, qsizetype removeCount)
213{
214 if (index >= series()->dataArray().size())
215 return;
216
217 Q_D(QScatterDataProxy);
218 d->removeItems(index, removeCount);
219 emit itemsRemoved(startIndex: index, count: removeCount);
220 emit itemCountChanged(count: itemCount());
221}
222
223/*!
224 * \property QScatterDataProxy::itemCount
225 *
226 * \brief The number of items in the array.
227 */
228qsizetype QScatterDataProxy::itemCount() const
229{
230 if (series())
231 return series()->dataArray().size();
232 else
233 return 0;
234}
235
236/*!
237 * Returns the pointer to the item at the index \a index. It is guaranteed to be
238 * valid only until the next call that modifies data.
239 */
240const QScatterDataItem &QScatterDataProxy::itemAt(qsizetype index) const
241{
242 return series()->dataArray().at(i: index);
243}
244
245/*!
246 * \fn void QScatterDataProxy::arrayReset()
247 *
248 * This signal is emitted when the data array is reset.
249 * If the contents of the whole array are changed without calling resetArray(),
250 * this signal needs to be emitted to update the graph.
251 */
252
253/*!
254 * \fn void QScatterDataProxy::itemsAdded(qsizetype startIndex, qsizetype count)
255 *
256 * This signal is emitted when the number of items specified by \a count are
257 * added, starting at the position \a startIndex.
258 * If items are added to the array without calling addItem() or addItems(),
259 * this signal needs to be emitted to update the graph.
260 */
261
262/*!
263 * \fn void QScatterDataProxy::itemsChanged(qsizetype startIndex, qsizetype count)
264 *
265 * This signal is emitted when the number of items specified by \a count are
266 * changed, starting at the position \a startIndex.
267 * If items are changed in the array without calling setItem() or setItems(),
268 * this signal needs to be emitted to update the graph.
269 */
270
271/*!
272 * \fn void QScatterDataProxy::itemsRemoved(qsizetype startIndex, qsizetype count)
273 *
274 * This signal is emitted when the number of rows specified by \a count are
275 * removed, starting at the position \a startIndex.
276 * The index may be larger than the current array size if items are removed from
277 * the end. If items are removed from the array without calling removeItems(),
278 * this signal needs to be emitted to update the graph.
279 */
280
281/*!
282 * \fn void QScatterDataProxy::itemsInserted(qsizetype startIndex, qsizetype count)
283 *
284 * This signal is emitted when the number of items specified by \a count are
285 * inserted, starting at the position \a startIndex.
286 * If items are inserted into the array without calling insertItem() or
287 * insertItems(), this signal needs to be emitted to update the graph.
288 */
289
290// QScatterDataProxyPrivate
291
292QScatterDataProxyPrivate::QScatterDataProxyPrivate()
293 : QAbstractDataProxyPrivate(QAbstractDataProxy::DataType::Scatter)
294{}
295
296QScatterDataProxyPrivate::~QScatterDataProxyPrivate() {}
297
298void QScatterDataProxyPrivate::resetArray(QScatterDataArray &&newArray)
299{
300 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
301 if (newArray.data() != scatterSeries->dataArray().data())
302 scatterSeries->setDataArray(newArray);
303}
304
305void QScatterDataProxyPrivate::setItem(qsizetype index, QScatterDataItem &&item)
306{
307 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
308 Q_ASSERT(index >= 0 && index < scatterSeries->dataArray().size());
309 QScatterDataArray array = scatterSeries->dataArray();
310 array[index] = item;
311 scatterSeries->setDataArray(array);
312}
313
314void QScatterDataProxyPrivate::setItems(qsizetype index, QScatterDataArray &&items)
315{
316 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
317 Q_ASSERT(index >= 0 && (index + items.size()) <= scatterSeries->dataArray().size());
318 QScatterDataArray array = scatterSeries->dataArray();
319 for (int i = 0; i < items.size(); i++)
320 array[index++] = items[i];
321 scatterSeries->setDataArray(array);
322}
323
324qsizetype QScatterDataProxyPrivate::addItem(QScatterDataItem &&item)
325{
326 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
327 qsizetype currentSize = scatterSeries->dataArray().size();
328 QScatterDataArray array = scatterSeries->dataArray();
329 array.append(t: item);
330 scatterSeries->setDataArray(array);
331 return currentSize;
332}
333
334qsizetype QScatterDataProxyPrivate::addItems(QScatterDataArray &&items)
335{
336 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
337 qsizetype currentSize = 0;
338 if (scatterSeries) {
339 currentSize = scatterSeries->dataArray().size();
340 QScatterDataArray array = scatterSeries->dataArray();
341 array += items;
342 scatterSeries->setDataArray(array);
343 }
344 return currentSize;
345}
346
347void QScatterDataProxyPrivate::insertItem(qsizetype index, QScatterDataItem &&item)
348{
349 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
350 Q_ASSERT(index >= 0 && index <= scatterSeries->dataArray().size());
351 QScatterDataArray array = scatterSeries->dataArray();
352 array.insert(i: index, t: item);
353 scatterSeries->setDataArray(array);
354}
355
356void QScatterDataProxyPrivate::insertItems(qsizetype index, QScatterDataArray &&items)
357{
358 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
359 Q_ASSERT(index >= 0 && index <= scatterSeries->dataArray().size());
360 QScatterDataArray array = scatterSeries->dataArray();
361 for (int i = 0; i < items.size(); i++)
362 array.insert(i: index++, t: items.at(i));
363 scatterSeries->setDataArray(array);
364}
365
366void QScatterDataProxyPrivate::removeItems(qsizetype index, qsizetype removeCount)
367{
368 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
369 Q_ASSERT(index >= 0);
370 qsizetype maxRemoveCount = scatterSeries->dataArray().size() - index;
371 removeCount = qMin(a: removeCount, b: maxRemoveCount);
372 QScatterDataArray array = scatterSeries->dataArray();
373 array.remove(i: index, n: removeCount);
374 scatterSeries->setDataArray(array);
375}
376
377void QScatterDataProxyPrivate::limitValues(QVector3D &minValues,
378 QVector3D &maxValues,
379 QAbstract3DAxis *axisX,
380 QAbstract3DAxis *axisY,
381 QAbstract3DAxis *axisZ) const
382{
383 auto *scatterSeries = static_cast<QScatter3DSeries *>(series());
384 if (scatterSeries->dataArray().isEmpty())
385 return;
386
387 QVector3D firstPos = scatterSeries->dataArray().at(i: 0).position();
388
389 float minX = firstPos.x();
390 float maxX = minX;
391 float minY = firstPos.y();
392 float maxY = minY;
393 float minZ = firstPos.z();
394 float maxZ = minZ;
395
396 if (scatterSeries->dataArray().size() > 1) {
397 for (int i = 1; i < scatterSeries->dataArray().size(); i++) {
398 QVector3D pos = scatterSeries->dataArray().at(i).position();
399
400 float value = pos.x();
401 if (qIsNaN(f: value) || qIsInf(f: value))
402 continue;
403 if (isValidValue(axisValue: minX, value, axis: axisX))
404 minX = value;
405 if (maxX < value)
406 maxX = value;
407
408 value = pos.y();
409 if (qIsNaN(f: value) || qIsInf(f: value))
410 continue;
411 if (isValidValue(axisValue: minY, value, axis: axisY))
412 minY = value;
413 if (maxY < value)
414 maxY = value;
415
416 value = pos.z();
417 if (qIsNaN(f: value) || qIsInf(f: value))
418 continue;
419 if (isValidValue(axisValue: minZ, value, axis: axisZ))
420 minZ = value;
421 if (maxZ < value)
422 maxZ = value;
423 }
424 }
425
426 minValues.setX(minX);
427 minValues.setY(minY);
428 minValues.setZ(minZ);
429
430 maxValues.setX(maxX);
431 maxValues.setY(maxY);
432 maxValues.setZ(maxZ);
433}
434
435bool QScatterDataProxyPrivate::isValidValue(float axisValue,
436 float value,
437 QAbstract3DAxis *axis) const
438{
439 return (axisValue > value
440 && (value > 0.0f || (value == 0.0f && axis->d_func()->allowZero())
441 || (value < 0.0f && axis->d_func()->allowNegatives())));
442}
443
444void QScatterDataProxyPrivate::setSeries(QAbstract3DSeries *series)
445{
446 Q_Q(QScatterDataProxy);
447 QAbstractDataProxyPrivate::setSeries(series);
448 QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series);
449 emit q->seriesChanged(series: scatterSeries);
450}
451
452QT_END_NAMESPACE
453

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtgraphs/src/graphs3d/data/qscatterdataproxy.cpp