1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qitemmodelscatterdataproxy_p.h"
5#include "scatteritemmodelhandler_p.h"
6
7QT_BEGIN_NAMESPACE
8
9/*!
10 * \class QItemModelScatterDataProxy
11 * \inmodule QtDataVisualization
12 * \brief Proxy class for presenting data in item models with Q3DScatter.
13 * \since QtDataVisualization 1.0
14 *
15 * QItemModelScatterDataProxy allows you to use QAbstractItemModel derived models as a data source
16 * for Q3DScatter. It maps roles of QAbstractItemModel to the XYZ-values of Q3DScatter points.
17 *
18 * The data is resolved asynchronously whenever the mapping or the model changes.
19 * QScatterDataProxy::arrayReset() is emitted when the data has been resolved. However, inserts,
20 * removes, and single data item changes after the model initialization are resolved synchronously,
21 * unless the same frame also contains a change that causes the whole model to be resolved.
22 *
23 * Mapping ignores rows and columns of the QAbstractItemModel and treats
24 * all items equally. It requires the model to provide roles for the data items
25 * that can be mapped to X, Y, and Z-values for the scatter points.
26 *
27 * For example, assume that you have a custom QAbstractItemModel for storing various measurements
28 * done on material samples, providing data for roles such as "density", "hardness", and
29 * "conductivity". You could visualize these properties on a scatter graph using this proxy:
30 *
31 * \snippet doc_src_qtdatavisualization.cpp 4
32 *
33 * If the fields of the model do not contain the data in the exact format you need, you can specify
34 * a search pattern regular expression and a replace rule for each role to get the value in a
35 * format you need. For more information how the replace using regular expressions works, see
36 * QString::replace(const QRegularExpression &rx, const QString &after) function documentation. Note that
37 * using regular expressions has an impact on the performance, so it's more efficient to utilize
38 * item models where doing search and replace is not necessary to get the desired values.
39 *
40 * For example about using the search patterns in conjunction with the roles, see
41 * ItemModelBarDataProxy usage in \l{Simple Bar Graph} example.
42 *
43 * \sa {Qt Data Visualization Data Handling}
44 */
45
46/*!
47 * \qmltype ItemModelScatterDataProxy
48 * \inqmlmodule QtDataVisualization
49 * \since QtDataVisualization 1.0
50 * \ingroup datavisualization_qml
51 * \instantiates QItemModelScatterDataProxy
52 * \inherits ScatterDataProxy
53 * \brief Proxy class for presenting data in item models with Scatter3D.
54 *
55 * This type allows you to use AbstractItemModel derived models as a data source for Scatter3D.
56 *
57 * The data is resolved asynchronously whenever the mapping or the model changes.
58 * QScatterDataProxy::arrayReset() is emitted when the data has been resolved.
59 *
60 * For more details, see QItemModelScatterDataProxy documentation.
61 *
62 * Usage example:
63 *
64 * \snippet doc_src_qmldatavisualization.cpp 8
65 *
66 * \sa ScatterDataProxy, {Qt Data Visualization Data Handling}
67 */
68
69/*!
70 * \qmlproperty model ItemModelScatterDataProxy::itemModel
71 * The item model to use as a data source for Scatter3D.
72 */
73
74/*!
75 * \qmlproperty string ItemModelScatterDataProxy::xPosRole
76 * The item model role to map into the X position.
77 */
78
79/*!
80 * \qmlproperty string ItemModelScatterDataProxy::yPosRole
81 * The item model role to map into the Y position.
82 */
83
84/*!
85 * \qmlproperty string ItemModelScatterDataProxy::zPosRole
86 * The item model role to map into the Z position.
87 */
88
89/*!
90 * \qmlproperty string ItemModelScatterDataProxy::rotationRole
91 *
92 * The item model role to map into item rotation.
93 * The model may supply the value for rotation as either variant that is directly convertible
94 * to \l quaternion, or as one of the string representations: \c{"scalar,x,y,z"} or
95 * \c{"@angle,x,y,z"}. The first format will construct the \l quaternion directly with given values,
96 * and the second one will construct the \l quaternion using QQuaternion::fromAxisAndAngle() method.
97 */
98
99/*!
100 * \qmlproperty regExp ItemModelScatterDataProxy::xPosRolePattern
101 *
102 * When set, a search and replace is done on the value mapped by the x position
103 * role before it is used as
104 * an item position value. This property specifies the regular expression to find the portion of the
105 * mapped value to replace and xPosRoleReplace property contains the replacement string.
106 *
107 * \sa xPosRole, xPosRoleReplace
108 */
109
110/*!
111 * \qmlproperty regExp ItemModelScatterDataProxy::yPosRolePattern
112 *
113 * When set, a search and replace is done on the value mapped by the y position
114 * role before it is used as
115 * an item position value. This property specifies the regular expression to find the portion of the
116 * mapped value to replace and yPosRoleReplace property contains the replacement string.
117 *
118 * \sa yPosRole, yPosRoleReplace
119 */
120
121/*!
122 * \qmlproperty regExp ItemModelScatterDataProxy::zPosRolePattern
123 *
124 * When set, a search and replace is done on the value mapped by the z position
125 * role before it is used as
126 * an item position value. This property specifies the regular expression to find the portion of the
127 * mapped value to replace and zPosRoleReplace property contains the replacement string.
128 *
129 * \sa zPosRole, zPosRoleReplace
130 */
131
132/*!
133 * \qmlproperty regExp ItemModelScatterDataProxy::rotationRolePattern
134 * When set, a search and replace is done on the value mapped by the rotation
135 * role before it is used
136 * as item rotation. This property specifies the regular expression to find the portion
137 * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
138 *
139 * \sa rotationRole, rotationRoleReplace
140 */
141
142/*!
143 * \qmlproperty string ItemModelScatterDataProxy::xPosRoleReplace
144 *
145 * This property defines the replace content to be used in conjunction with xPosRolePattern.
146 * Defaults to an empty string. For more information on how the search and replace using regular
147 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
148 * function documentation.
149 *
150 * \sa xPosRole, xPosRolePattern
151 */
152
153/*!
154 * \qmlproperty string ItemModelScatterDataProxy::yPosRoleReplace
155 *
156 * This property defines the replace content to be used in conjunction with yPosRolePattern.
157 * Defaults to an empty string. For more information on how the search and replace using regular
158 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
159 * function documentation.
160 *
161 * \sa yPosRole, yPosRolePattern
162 */
163
164/*!
165 * \qmlproperty string ItemModelScatterDataProxy::zPosRoleReplace
166 *
167 * This property defines the replace content to be used in conjunction with zPosRolePattern.
168 * Defaults to an empty string. For more information on how the search and replace using regular
169 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
170 * function documentation.
171 *
172 * \sa zPosRole, zPosRolePattern
173 */
174
175/*!
176 * \qmlproperty string ItemModelScatterDataProxy::rotationRoleReplace
177 * This property defines the replace content to be used in conjunction with rotationRolePattern.
178 * Defaults to an empty string. For more information on how the search and replace using regular
179 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
180 * function documentation.
181 *
182 * \sa rotationRole, rotationRolePattern
183 */
184
185/*!
186 * Constructs QItemModelScatterDataProxy with optional \a parent.
187 */
188QItemModelScatterDataProxy::QItemModelScatterDataProxy(QObject *parent)
189 : QScatterDataProxy(new QItemModelScatterDataProxyPrivate(this), parent)
190{
191 dptr()->connectItemModelHandler();
192}
193
194/*!
195 * Constructs QItemModelScatterDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
196 * ownership of the \a itemModel, as typically item models are owned by other controls.
197 */
198QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
199 QObject *parent)
200 : QScatterDataProxy(new QItemModelScatterDataProxyPrivate(this), parent)
201{
202 dptr()->m_itemModelHandler->setItemModel(itemModel);
203 dptr()->connectItemModelHandler();
204}
205
206/*!
207 * Constructs QItemModelScatterDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
208 * ownership of the \a itemModel, as typically item models are owned by other controls.
209 * The xPosRole property is set to \a xPosRole, yPosRole property to \a yPosRole, and zPosRole property
210 * to \a zPosRole.
211 */
212QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
213 const QString &xPosRole,
214 const QString &yPosRole,
215 const QString &zPosRole,
216 QObject *parent)
217 : QScatterDataProxy(new QItemModelScatterDataProxyPrivate(this), parent)
218{
219 dptr()->m_itemModelHandler->setItemModel(itemModel);
220 dptr()->m_xPosRole = xPosRole;
221 dptr()->m_yPosRole = yPosRole;
222 dptr()->m_zPosRole = zPosRole;
223 dptr()->connectItemModelHandler();
224}
225
226/*!
227 * Constructs QItemModelScatterDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
228 * ownership of the \a itemModel, as typically item models are owned by other controls.
229 * The xPosRole property is set to \a xPosRole, yPosRole property to \a yPosRole, zPosRole property
230 * to \a zPosRole, and rotationRole property to \a rotationRole.
231 */
232QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel,
233 const QString &xPosRole,
234 const QString &yPosRole,
235 const QString &zPosRole,
236 const QString &rotationRole,
237 QObject *parent)
238 : QScatterDataProxy(new QItemModelScatterDataProxyPrivate(this), parent)
239{
240 dptr()->m_itemModelHandler->setItemModel(itemModel);
241 dptr()->m_xPosRole = xPosRole;
242 dptr()->m_yPosRole = yPosRole;
243 dptr()->m_zPosRole = zPosRole;
244 dptr()->m_rotationRole = rotationRole;
245 dptr()->connectItemModelHandler();
246}
247
248/*!
249 * Destroys QItemModelScatterDataProxy.
250 */
251QItemModelScatterDataProxy::~QItemModelScatterDataProxy()
252{
253}
254
255/*!
256 * \property QItemModelScatterDataProxy::itemModel
257 *
258 * \brief The item model to use as a data source for a 3D scatter series.
259 */
260
261/*!
262 * Sets \a itemModel as the item model for Q3DScatter. Does not take
263 * ownership of the model, but does connect to it to listen for changes.
264 */
265void QItemModelScatterDataProxy::setItemModel(QAbstractItemModel *itemModel)
266{
267 dptr()->m_itemModelHandler->setItemModel(itemModel);
268}
269
270QAbstractItemModel *QItemModelScatterDataProxy::itemModel() const
271{
272 return dptrc()->m_itemModelHandler->itemModel();
273}
274
275/*!
276 * \property QItemModelScatterDataProxy::xPosRole
277 *
278 * \brief The item model role to map into the X position.
279 */
280void QItemModelScatterDataProxy::setXPosRole(const QString &role)
281{
282 if (dptr()->m_xPosRole != role) {
283 dptr()->m_xPosRole = role;
284 emit xPosRoleChanged(role);
285 }
286}
287
288QString QItemModelScatterDataProxy::xPosRole() const
289{
290 return dptrc()->m_xPosRole;
291}
292
293/*!
294 * \property QItemModelScatterDataProxy::yPosRole
295 *
296 * \brief The item model role to map into the Y position.
297 */
298void QItemModelScatterDataProxy::setYPosRole(const QString &role)
299{
300 if (dptr()->m_yPosRole != role) {
301 dptr()->m_yPosRole = role;
302 emit yPosRoleChanged(role);
303 }
304}
305
306QString QItemModelScatterDataProxy::yPosRole() const
307{
308 return dptrc()->m_yPosRole;
309}
310
311/*!
312 * \property QItemModelScatterDataProxy::zPosRole
313 *
314 * \brief The item model role to map into the Z position.
315 */
316void QItemModelScatterDataProxy::setZPosRole(const QString &role)
317{
318 if (dptr()->m_zPosRole != role) {
319 dptr()->m_zPosRole = role;
320 emit zPosRoleChanged(role);
321 }
322}
323
324QString QItemModelScatterDataProxy::zPosRole() const
325{
326 return dptrc()->m_zPosRole;
327}
328
329/*!
330 * \property QItemModelScatterDataProxy::rotationRole
331 *
332 * \brief The item model role to map into item rotation.
333 *
334 * The model may supply the value for rotation as either variant that is directly convertible
335 * to QQuaternion, or as one of the string representations: \c{"scalar,x,y,z"} or \c{"@angle,x,y,z"}.
336 * The first will construct the quaternion directly with given values, and the second one will
337 * construct the quaternion using QQuaternion::fromAxisAndAngle() method.
338 */
339void QItemModelScatterDataProxy::setRotationRole(const QString &role)
340{
341 if (dptr()->m_rotationRole != role) {
342 dptr()->m_rotationRole = role;
343 emit rotationRoleChanged(role);
344 }
345}
346
347QString QItemModelScatterDataProxy::rotationRole() const
348{
349 return dptrc()->m_rotationRole;
350}
351
352/*!
353 * \property QItemModelScatterDataProxy::xPosRolePattern
354 *
355 * \brief Whether search and replace is done on the value mapped by the x
356 * position role before it is used as an item position value.
357 *
358 * This property specifies the regular expression to find the portion of the
359 * mapped value to replace and xPosRoleReplace property contains the replacement string.
360 *
361 * \sa xPosRole, xPosRoleReplace
362 */
363void QItemModelScatterDataProxy::setXPosRolePattern(const QRegularExpression &pattern)
364{
365 if (dptr()->m_xPosRolePattern != pattern) {
366 dptr()->m_xPosRolePattern = pattern;
367 emit xPosRolePatternChanged(pattern);
368 }
369}
370
371QRegularExpression QItemModelScatterDataProxy::xPosRolePattern() const
372{
373 return dptrc()->m_xPosRolePattern;
374}
375
376/*!
377 * \property QItemModelScatterDataProxy::yPosRolePattern
378 *
379 * \brief Whether a search and replace is done on the value mapped by the
380 * y position role before it is used as an item position value.
381 *
382 * This property specifies the regular expression to find the portion of the
383 * mapped value to replace and yPosRoleReplace property contains the replacement string.
384 *
385 * \sa yPosRole, yPosRoleReplace
386 */
387void QItemModelScatterDataProxy::setYPosRolePattern(const QRegularExpression &pattern)
388{
389 if (dptr()->m_yPosRolePattern != pattern) {
390 dptr()->m_yPosRolePattern = pattern;
391 emit yPosRolePatternChanged(pattern);
392 }
393}
394
395QRegularExpression QItemModelScatterDataProxy::yPosRolePattern() const
396{
397 return dptrc()->m_yPosRolePattern;
398}
399
400/*!
401 * \property QItemModelScatterDataProxy::zPosRolePattern
402 *
403 * \brief Whether a search and replace is done on the value mapped by the z
404 * position role before it is used as an item position value.
405 *
406 * This property specifies the regular expression to find the portion of the
407 * mapped value to replace and zPosRoleReplace property contains the replacement string.
408 *
409 * \sa zPosRole, zPosRoleReplace
410 */
411void QItemModelScatterDataProxy::setZPosRolePattern(const QRegularExpression &pattern)
412{
413 if (dptr()->m_zPosRolePattern != pattern) {
414 dptr()->m_zPosRolePattern = pattern;
415 emit zPosRolePatternChanged(pattern);
416 }
417}
418
419QRegularExpression QItemModelScatterDataProxy::zPosRolePattern() const
420{
421 return dptrc()->m_zPosRolePattern;
422}
423
424/*!
425 * \property QItemModelScatterDataProxy::rotationRolePattern
426 *
427 * \brief Whether a search and replace is done on the value mapped by the
428 * rotation role before it is used as item rotation.
429 *
430 * This property specifies the regular expression to find the portion
431 * of the mapped value to replace and rotationRoleReplace property contains the replacement string.
432 *
433 * \sa rotationRole, rotationRoleReplace
434 */
435void QItemModelScatterDataProxy::setRotationRolePattern(const QRegularExpression &pattern)
436{
437 if (dptr()->m_rotationRolePattern != pattern) {
438 dptr()->m_rotationRolePattern = pattern;
439 emit rotationRolePatternChanged(pattern);
440 }
441}
442
443QRegularExpression QItemModelScatterDataProxy::rotationRolePattern() const
444{
445 return dptrc()->m_rotationRolePattern;
446}
447
448/*!
449 * \property QItemModelScatterDataProxy::xPosRoleReplace
450 *
451 * \brief The replace content to be used in conjunction with the x position role
452 * pattern.
453 *
454 * Defaults to an empty string. For more information on how the search and replace using regular
455 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
456 * function documentation.
457 *
458 * \sa xPosRole, xPosRolePattern
459 */
460void QItemModelScatterDataProxy::setXPosRoleReplace(const QString &replace)
461{
462 if (dptr()->m_xPosRoleReplace != replace) {
463 dptr()->m_xPosRoleReplace = replace;
464 emit xPosRoleReplaceChanged(replace);
465 }
466}
467
468QString QItemModelScatterDataProxy::xPosRoleReplace() const
469{
470 return dptrc()->m_xPosRoleReplace;
471}
472
473/*!
474 * \property QItemModelScatterDataProxy::yPosRoleReplace
475 *
476 * \brief The replace content to be used in conjunction with the y position role
477 * pattern.
478 *
479 * Defaults to an empty string. For more information on how the search and replace using regular
480 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
481 * function documentation.
482 *
483 * \sa yPosRole, yPosRolePattern
484 */
485void QItemModelScatterDataProxy::setYPosRoleReplace(const QString &replace)
486{
487 if (dptr()->m_yPosRoleReplace != replace) {
488 dptr()->m_yPosRoleReplace = replace;
489 emit yPosRoleReplaceChanged(replace);
490 }
491}
492
493QString QItemModelScatterDataProxy::yPosRoleReplace() const
494{
495 return dptrc()->m_yPosRoleReplace;
496}
497
498/*!
499 * \property QItemModelScatterDataProxy::zPosRoleReplace
500 *
501 * \brief The replace content to be used in conjunction with the z position role
502 * pattern.
503 *
504 * Defaults to an empty string. For more information on how the search and replace using regular
505 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
506 * function documentation.
507 *
508 * \sa zPosRole, zPosRolePattern
509 */
510void QItemModelScatterDataProxy::setZPosRoleReplace(const QString &replace)
511{
512 if (dptr()->m_zPosRoleReplace != replace) {
513 dptr()->m_zPosRoleReplace = replace;
514 emit zPosRoleReplaceChanged(replace);
515 }
516}
517
518QString QItemModelScatterDataProxy::zPosRoleReplace() const
519{
520 return dptrc()->m_zPosRoleReplace;
521}
522
523/*!
524 * \property QItemModelScatterDataProxy::rotationRoleReplace
525 *
526 * \brief The replace content to be used in conjunction with the rotation role
527 * pattern.
528 *
529 * Defaults to an empty string. For more information on how the search and replace using regular
530 * expressions works, see QString::replace(const QRegularExpression &rx, const QString &after)
531 * function documentation.
532 *
533 * \sa rotationRole, rotationRolePattern
534 */
535void QItemModelScatterDataProxy::setRotationRoleReplace(const QString &replace)
536{
537 if (dptr()->m_rotationRoleReplace != replace) {
538 dptr()->m_rotationRoleReplace = replace;
539 emit rotationRoleReplaceChanged(replace);
540 }
541}
542
543QString QItemModelScatterDataProxy::rotationRoleReplace() const
544{
545 return dptrc()->m_rotationRoleReplace;
546}
547
548/*!
549 * Changes \a xPosRole, \a yPosRole, \a zPosRole, and \a rotationRole mapping.
550 */
551void QItemModelScatterDataProxy::remap(const QString &xPosRole, const QString &yPosRole,
552 const QString &zPosRole, const QString &rotationRole)
553{
554 setXPosRole(xPosRole);
555 setYPosRole(yPosRole);
556 setZPosRole(zPosRole);
557 setRotationRole(rotationRole);
558}
559
560/*!
561 * \internal
562 */
563QItemModelScatterDataProxyPrivate *QItemModelScatterDataProxy::dptr()
564{
565 return static_cast<QItemModelScatterDataProxyPrivate *>(d_ptr.data());
566}
567
568/*!
569 * \internal
570 */
571const QItemModelScatterDataProxyPrivate *QItemModelScatterDataProxy::dptrc() const
572{
573 return static_cast<const QItemModelScatterDataProxyPrivate *>(d_ptr.data());
574}
575
576// QItemModelScatterDataProxyPrivate
577
578QItemModelScatterDataProxyPrivate::QItemModelScatterDataProxyPrivate(QItemModelScatterDataProxy *q)
579 : QScatterDataProxyPrivate(q),
580 m_itemModelHandler(new ScatterItemModelHandler(q))
581{
582}
583
584QItemModelScatterDataProxyPrivate::~QItemModelScatterDataProxyPrivate()
585{
586 delete m_itemModelHandler;
587}
588
589QItemModelScatterDataProxy *QItemModelScatterDataProxyPrivate::qptr()
590{
591 return static_cast<QItemModelScatterDataProxy *>(q_ptr);
592}
593
594void QItemModelScatterDataProxyPrivate::connectItemModelHandler()
595{
596 QObject::connect(sender: m_itemModelHandler, signal: &ScatterItemModelHandler::itemModelChanged,
597 context: qptr(), slot: &QItemModelScatterDataProxy::itemModelChanged);
598 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::xPosRoleChanged,
599 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
600 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::yPosRoleChanged,
601 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
602 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::zPosRoleChanged,
603 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
604 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::rotationRoleChanged,
605 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
606 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::xPosRolePatternChanged,
607 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
608 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::yPosRolePatternChanged,
609 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
610 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::zPosRolePatternChanged,
611 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
612 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::rotationRolePatternChanged,
613 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
614 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::xPosRoleReplaceChanged,
615 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
616 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::yPosRoleReplaceChanged,
617 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
618 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::zPosRoleReplaceChanged,
619 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
620 QObject::connect(sender: qptr(), signal: &QItemModelScatterDataProxy::rotationRoleReplaceChanged,
621 context: m_itemModelHandler, slot: &AbstractItemModelHandler::handleMappingChanged);
622}
623
624QT_END_NAMESPACE
625

source code of qtdatavis3d/src/datavisualization/data/qitemmodelscatterdataproxy.cpp