1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "scatteritemmodelhandler_p.h"
5
6QT_BEGIN_NAMESPACE
7
8ScatterItemModelHandler::ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent)
9 : AbstractItemModelHandler(parent),
10 m_proxy(proxy),
11 m_proxyArray(0),
12 m_xPosRole(noRoleIndex),
13 m_yPosRole(noRoleIndex),
14 m_zPosRole(noRoleIndex),
15 m_rotationRole(noRoleIndex),
16 m_haveXPosPattern(false),
17 m_haveYPosPattern(false),
18 m_haveZPosPattern(false),
19 m_haveRotationPattern(false)
20{
21}
22
23ScatterItemModelHandler::~ScatterItemModelHandler()
24{
25}
26
27void ScatterItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
28 const QModelIndex &bottomRight,
29 const QList<int> &roles)
30{
31 // Do nothing if full reset already pending
32 if (!m_fullReset) {
33 if (m_itemModel->columnCount() > 1) {
34 // If the data model is multi-column, do full asynchronous reset to simplify things
35 AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
36 } else {
37 int start = qMin(a: topLeft.row(), b: bottomRight.row());
38 int end = qMax(a: topLeft.row(), b: bottomRight.row());
39
40 QScatterDataArray array(end - start + 1);
41 int count = 0;
42 for (int i = start; i <= end; i++)
43 modelPosToScatterItem(modelRow: i, modelColumn: 0, item&: array[count++]);
44
45 m_proxy->setItems(index: start, items: array);
46 }
47 }
48}
49
50void ScatterItemModelHandler::handleRowsInserted(const QModelIndex &parent, int start, int end)
51{
52 // Do nothing if full reset already pending
53 if (!m_fullReset) {
54 if (!m_proxy->itemCount() || m_itemModel->columnCount() > 1) {
55 // If inserting into an empty array, do full asynchronous reset to avoid multiple
56 // separate inserts when initializing the model.
57 // If the data model is multi-column, do full asynchronous reset to simplify things
58 AbstractItemModelHandler::handleRowsInserted(parent, start, end);
59 } else {
60 QScatterDataArray array(end - start + 1);
61 int count = 0;
62 for (int i = start; i <= end; i++)
63 modelPosToScatterItem(modelRow: i, modelColumn: 0, item&: array[count++]);
64
65 m_proxy->insertItems(index: start, items: array);
66 }
67 }
68}
69
70void ScatterItemModelHandler::handleRowsRemoved(const QModelIndex &parent, int start, int end)
71{
72 Q_UNUSED(parent);
73
74 // Do nothing if full reset already pending
75 if (!m_fullReset) {
76 if (m_itemModel->columnCount() > 1) {
77 // If the data model is multi-column, do full asynchronous reset to 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 int index = s.indexOf(c: QLatin1Char(','));
99 int index2 = s.indexOf(c: QLatin1Char(','), from: index + 1);
100 int index3 = s.indexOf(c: 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, int modelColumn,
121 QScatterDataItem &item)
122{
123 QModelIndex index = m_itemModel->index(row: modelRow, column: modelColumn);
124 float xPos;
125 float yPos;
126 float zPos;
127 if (m_xPosRole != noRoleIndex) {
128 QVariant xValueVar = index.data(arole: m_xPosRole);
129 if (m_haveXPosPattern)
130 xPos = xValueVar.toString().replace(re: m_xPosPattern, after: m_xPosReplace).toFloat();
131 else
132 xPos = xValueVar.toFloat();
133 } else {
134 xPos = 0.0f;
135 }
136 if (m_yPosRole != noRoleIndex) {
137 QVariant yValueVar = index.data(arole: m_yPosRole);
138 if (m_haveYPosPattern)
139 yPos = yValueVar.toString().replace(re: m_yPosPattern, after: m_yPosReplace).toFloat();
140 else
141 yPos = yValueVar.toFloat();
142 } else {
143 yPos = 0.0f;
144 }
145 if (m_zPosRole != noRoleIndex) {
146 QVariant zValueVar = index.data(arole: m_zPosRole);
147 if (m_haveZPosPattern)
148 zPos = zValueVar.toString().replace(re: m_zPosPattern, after: m_zPosReplace).toFloat();
149 else
150 zPos = zValueVar.toFloat();
151 } else {
152 zPos = 0.0f;
153 }
154 if (m_rotationRole != noRoleIndex) {
155 QVariant rotationVar = index.data(arole: m_rotationRole);
156 if (m_haveRotationPattern) {
157 item.setRotation(
158 toQuaternion(
159 variant: QVariant(rotationVar.toString().replace(re: m_rotationPattern,
160 after: m_rotationReplace))));
161 } else {
162 item.setRotation(toQuaternion(variant: rotationVar));
163 }
164 }
165
166 item.setPosition(QVector3D(xPos, yPos, zPos));
167}
168
169// Resolve entire item model into QScatterDataArray.
170void ScatterItemModelHandler::resolveModel()
171{
172 if (m_itemModel.isNull()) {
173 m_proxy->resetArray(newArray: 0);
174 m_proxyArray = 0;
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() && m_rotationPattern.isValid();
190
191 QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
192 m_xPosRole = roleHash.key(value: m_proxy->xPosRole().toLatin1(), defaultKey: noRoleIndex);
193 m_yPosRole = roleHash.key(value: m_proxy->yPosRole().toLatin1(), defaultKey: noRoleIndex);
194 m_zPosRole = roleHash.key(value: m_proxy->zPosRole().toLatin1(), defaultKey: noRoleIndex);
195 m_rotationRole = roleHash.key(value: m_proxy->rotationRole().toLatin1(), defaultKey: noRoleIndex);
196 const int columnCount = m_itemModel->columnCount();
197 const int rowCount = m_itemModel->rowCount();
198 const int totalCount = rowCount * columnCount;
199 int runningCount = 0;
200
201 // If dimensions have changed, recreate the array
202 if (m_proxyArray != m_proxy->array() || totalCount != m_proxyArray->size())
203 m_proxyArray = new QScatterDataArray(totalCount);
204
205 // Parse data into newProxyArray
206 for (int i = 0; i < rowCount; i++) {
207 for (int j = 0; j < columnCount; j++) {
208 modelPosToScatterItem(modelRow: i, modelColumn: j, item&: (*m_proxyArray)[runningCount]);
209 runningCount++;
210 }
211 }
212
213 m_proxy->resetArray(newArray: m_proxyArray);
214}
215
216QT_END_NAMESPACE
217

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