1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qscatter3dseries_p.h"
5#include "scatteritemmodelhandler_p.h"
6
7QT_BEGIN_NAMESPACE
8
9ScatterItemModelHandler::ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent)
10 : AbstractItemModelHandler(parent)
11 , m_proxy(proxy)
12 , m_proxyArray(0)
13 , m_xPosRole(noRoleIndex)
14 , m_yPosRole(noRoleIndex)
15 , m_zPosRole(noRoleIndex)
16 , m_rotationRole(noRoleIndex)
17 , m_haveXPosPattern(false)
18 , m_haveYPosPattern(false)
19 , m_haveZPosPattern(false)
20 , m_haveRotationPattern(false)
21{}
22
23ScatterItemModelHandler::~ScatterItemModelHandler() {}
24
25void ScatterItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
26 const QModelIndex &bottomRight,
27 const QList<int> &roles)
28{
29 // Do nothing if full reset already pending
30 if (!m_fullReset) {
31 if (m_itemModel->columnCount() > 1) {
32 // If the data model is multi-column, do full asynchronous reset to
33 // simplify things
34 AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
35 } else {
36 int start = qMin(a: topLeft.row(), b: bottomRight.row());
37 int end = qMax(a: topLeft.row(), b: bottomRight.row());
38
39 QScatterDataArray array(end - start + 1);
40 int count = 0;
41 for (int i = start; i <= end; i++)
42 modelPosToScatterItem(modelRow: i, modelColumn: 0, item&: array[count++]);
43
44 m_proxy->setItems(index: start, items: array);
45 }
46 }
47}
48
49void ScatterItemModelHandler::handleRowsInserted(const QModelIndex &parent, int start, int end)
50{
51 // Do nothing if full reset already pending
52 if (!m_fullReset) {
53 if (!m_proxy->itemCount() || m_itemModel->columnCount() > 1) {
54 // If inserting into an empty array, do full asynchronous reset to avoid
55 // multiple separate inserts when initializing the model. If the data
56 // model is multi-column, do full asynchronous reset to simplify things
57 AbstractItemModelHandler::handleRowsInserted(parent, start, end);
58 } else {
59 QScatterDataArray array(end - start + 1);
60 int count = 0;
61 for (int i = start; i <= end; i++)
62 modelPosToScatterItem(modelRow: i, modelColumn: 0, item&: array[count++]);
63
64 m_proxy->insertItems(index: start, items: array);
65 }
66 }
67}
68
69void ScatterItemModelHandler::handleRowsRemoved(const QModelIndex &parent, int start, int end)
70{
71 Q_UNUSED(parent);
72
73 // Do nothing if full reset already pending
74 if (!m_fullReset) {
75 if (m_itemModel->columnCount() > 1) {
76 // If the data model is multi-column, do full asynchronous reset to
77 // simplify things
78 AbstractItemModelHandler::handleRowsRemoved(parent, start, end);
79 } else {
80 m_proxy->removeItems(index: start, removeCount: end - start + 1);
81 }
82 }
83}
84
85static inline QQuaternion toQuaternion(const QVariant &variant)
86{
87 if (variant.canConvert<QQuaternion>()) {
88 return variant.value<QQuaternion>();
89 } else if (variant.canConvert<QString>()) {
90 QString s = variant.toString();
91 if (!s.isEmpty()) {
92 bool angleAndAxis = false;
93 if (s.startsWith(c: QLatin1Char('@'))) {
94 angleAndAxis = true;
95 s = s.mid(position: 1);
96 }
97 if (s.count(c: QLatin1Char(',')) == 3) {
98 qsizetype index = s.indexOf(ch: QLatin1Char(','));
99 qsizetype index2 = s.indexOf(ch: QLatin1Char(','), from: index + 1);
100 qsizetype index3 = s.indexOf(ch: QLatin1Char(','), from: index2 + 1);
101
102 bool sGood, xGood, yGood, zGood;
103 float sCoord = s.left(n: index).toFloat(ok: &sGood);
104 float xCoord = s.mid(position: index + 1, n: index2 - index - 1).toFloat(ok: &xGood);
105 float yCoord = s.mid(position: index2 + 1, n: index3 - index2 - 1).toFloat(ok: &yGood);
106 float zCoord = s.mid(position: index3 + 1).toFloat(ok: &zGood);
107
108 if (sGood && xGood && yGood && zGood) {
109 if (angleAndAxis)
110 return QQuaternion::fromAxisAndAngle(x: xCoord, y: yCoord, z: zCoord, angle: sCoord);
111 else
112 return QQuaternion(sCoord, xCoord, yCoord, zCoord);
113 }
114 }
115 }
116 }
117 return QQuaternion();
118}
119
120void ScatterItemModelHandler::modelPosToScatterItem(int modelRow,
121 int modelColumn,
122 QScatterDataItem &item)
123{
124 QModelIndex index = m_itemModel->index(row: modelRow, column: modelColumn);
125 float xPos;
126 float yPos;
127 float zPos;
128 if (m_xPosRole != noRoleIndex) {
129 QVariant xValueVar = index.data(arole: m_xPosRole);
130 if (m_haveXPosPattern)
131 xPos = xValueVar.toString().replace(re: m_xPosPattern, after: m_xPosReplace).toFloat();
132 else
133 xPos = xValueVar.toFloat();
134 } else {
135 xPos = 0.0f;
136 }
137 if (m_yPosRole != noRoleIndex) {
138 QVariant yValueVar = index.data(arole: m_yPosRole);
139 if (m_haveYPosPattern)
140 yPos = yValueVar.toString().replace(re: m_yPosPattern, after: m_yPosReplace).toFloat();
141 else
142 yPos = yValueVar.toFloat();
143 } else {
144 yPos = 0.0f;
145 }
146 if (m_zPosRole != noRoleIndex) {
147 QVariant zValueVar = index.data(arole: m_zPosRole);
148 if (m_haveZPosPattern)
149 zPos = zValueVar.toString().replace(re: m_zPosPattern, after: m_zPosReplace).toFloat();
150 else
151 zPos = zValueVar.toFloat();
152 } else {
153 zPos = 0.0f;
154 }
155 if (m_rotationRole != noRoleIndex) {
156 QVariant rotationVar = index.data(arole: m_rotationRole);
157 if (m_haveRotationPattern) {
158 item.setRotation(toQuaternion(
159 variant: QVariant(rotationVar.toString().replace(re: m_rotationPattern, after: m_rotationReplace))));
160 } else {
161 item.setRotation(toQuaternion(variant: rotationVar));
162 }
163 }
164
165 item.setPosition(QVector3D(xPos, yPos, zPos));
166}
167
168// Resolve entire item model into QScatterDataArray.
169void ScatterItemModelHandler::resolveModel()
170{
171 if (m_itemModel.isNull()) {
172 QScatterDataArray empty;
173 m_proxy->resetArray(newArray: empty);
174 m_proxyArray.clear();
175 return;
176 }
177
178 m_xPosPattern = m_proxy->xPosRolePattern();
179 m_yPosPattern = m_proxy->yPosRolePattern();
180 m_zPosPattern = m_proxy->zPosRolePattern();
181 m_rotationPattern = m_proxy->rotationRolePattern();
182 m_xPosReplace = m_proxy->xPosRoleReplace();
183 m_yPosReplace = m_proxy->yPosRoleReplace();
184 m_zPosReplace = m_proxy->zPosRoleReplace();
185 m_rotationReplace = m_proxy->rotationRoleReplace();
186 m_haveXPosPattern = !m_xPosPattern.namedCaptureGroups().isEmpty() && m_xPosPattern.isValid();
187 m_haveYPosPattern = !m_yPosPattern.namedCaptureGroups().isEmpty() && m_yPosPattern.isValid();
188 m_haveZPosPattern = !m_zPosPattern.namedCaptureGroups().isEmpty() && m_zPosPattern.isValid();
189 m_haveRotationPattern = !m_rotationPattern.namedCaptureGroups().isEmpty()
190 && m_rotationPattern.isValid();
191
192 QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
193 m_xPosRole = roleHash.key(value: m_proxy->xPosRole().toLatin1(), defaultKey: noRoleIndex);
194 m_yPosRole = roleHash.key(value: m_proxy->yPosRole().toLatin1(), defaultKey: noRoleIndex);
195 m_zPosRole = roleHash.key(value: m_proxy->zPosRole().toLatin1(), defaultKey: noRoleIndex);
196 m_rotationRole = roleHash.key(value: m_proxy->rotationRole().toLatin1(), defaultKey: noRoleIndex);
197 const int columnCount = m_itemModel->columnCount();
198 const int rowCount = m_itemModel->rowCount();
199 const int totalCount = rowCount * columnCount;
200 int runningCount = 0;
201
202 // If dimensions have changed, recreate the array
203 if (m_proxyArray.data() != m_proxy->series()->dataArray().data()
204 || totalCount != m_proxyArray.size()) {
205 m_proxyArray.resize(size: totalCount);
206 }
207
208 // Parse data into newProxyArray
209 for (int i = 0; i < rowCount; i++) {
210 for (int j = 0; j < columnCount; j++) {
211 modelPosToScatterItem(modelRow: i, modelColumn: j, item&: m_proxyArray[runningCount]);
212 runningCount++;
213 }
214 }
215
216 m_proxy->resetArray(newArray: m_proxyArray);
217}
218
219QT_END_NAMESPACE
220

Provided by KDAB

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

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