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