1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "surfaceitemmodelhandler_p.h"
31
32QT_BEGIN_NAMESPACE_DATAVISUALIZATION
33
34static const int noRoleIndex = -1;
35
36SurfaceItemModelHandler::SurfaceItemModelHandler(QItemModelSurfaceDataProxy *proxy, QObject *parent)
37 : AbstractItemModelHandler(parent),
38 m_proxy(proxy),
39 m_proxyArray(0),
40 m_xPosRole(noRoleIndex),
41 m_yPosRole(noRoleIndex),
42 m_zPosRole(noRoleIndex),
43 m_haveXPosPattern(false),
44 m_haveYPosPattern(false),
45 m_haveZPosPattern(false)
46{
47}
48
49SurfaceItemModelHandler::~SurfaceItemModelHandler()
50{
51}
52
53void SurfaceItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
54 const QModelIndex &bottomRight,
55 const QVector<int> &roles)
56{
57 // Do nothing if full reset already pending
58 if (!m_fullReset) {
59 if (!m_proxy->useModelCategories()) {
60 // If the data model doesn't directly map rows and columns, we cannot optimize
61 AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
62 } else {
63 int startRow = qMin(a: topLeft.row(), b: bottomRight.row());
64 int endRow = qMax(a: topLeft.row(), b: bottomRight.row());
65 int startCol = qMin(a: topLeft.column(), b: bottomRight.column());
66 int endCol = qMax(a: topLeft.column(), b: bottomRight.column());
67
68 for (int i = startRow; i <= endRow; i++) {
69 for (int j = startCol; j <= endCol; j++) {
70 QModelIndex index = m_itemModel->index(row: i, column: j);
71 QSurfaceDataItem item;
72 QVariant xValueVar = index.data(arole: m_xPosRole);
73 QVariant yValueVar = index.data(arole: m_yPosRole);
74 QVariant zValueVar = index.data(arole: m_zPosRole);
75 const QSurfaceDataItem *oldItem = m_proxy->itemAt(rowIndex: i, columnIndex: j);
76 float xPos;
77 float yPos;
78 float zPos;
79 if (m_xPosRole != noRoleIndex) {
80 if (m_haveXPosPattern)
81 xPos = xValueVar.toString().replace(rx: m_xPosPattern, after: m_xPosReplace).toFloat();
82 else
83 xPos = xValueVar.toFloat();
84 } else {
85 xPos = oldItem->x();
86 }
87
88 if (m_haveYPosPattern)
89 yPos = yValueVar.toString().replace(rx: m_yPosPattern, after: m_yPosReplace).toFloat();
90 else
91 yPos = yValueVar.toFloat();
92
93 if (m_zPosRole != noRoleIndex) {
94 if (m_haveZPosPattern)
95 zPos = zValueVar.toString().replace(rx: m_zPosPattern, after: m_zPosReplace).toFloat();
96 else
97 zPos = zValueVar.toFloat();
98 } else {
99 zPos = oldItem->z();
100 }
101 item.setPosition(QVector3D(xPos, yPos, zPos));
102 m_proxy->setItem(rowIndex: i, columnIndex: j, item);
103 }
104 }
105 }
106 }
107}
108
109// Resolve entire item model into QSurfaceDataArray.
110void SurfaceItemModelHandler::resolveModel()
111{
112 if (m_itemModel.isNull()) {
113 m_proxy->resetArray(newArray: 0);
114 m_proxyArray = 0;
115 return;
116 }
117
118 if (!m_proxy->useModelCategories()
119 && (m_proxy->rowRole().isEmpty() || m_proxy->columnRole().isEmpty())) {
120 m_proxy->resetArray(newArray: 0);
121 m_proxyArray = 0;
122 return;
123 }
124
125 // Position patterns can be reused on single item changes, so store them to member variables.
126 QRegExp rowPattern(m_proxy->rowRolePattern());
127 QRegExp colPattern(m_proxy->columnRolePattern());
128 m_xPosPattern = m_proxy->xPosRolePattern();
129 m_yPosPattern = m_proxy->yPosRolePattern();
130 m_zPosPattern = m_proxy->zPosRolePattern();
131 QString rowReplace = m_proxy->rowRoleReplace();
132 QString colReplace = m_proxy->columnRoleReplace();
133 m_xPosReplace = m_proxy->xPosRoleReplace();
134 m_yPosReplace = m_proxy->yPosRoleReplace();
135 m_zPosReplace = m_proxy->zPosRoleReplace();
136 bool haveRowPattern = !rowPattern.isEmpty() && rowPattern.isValid();
137 bool haveColPattern = !colPattern.isEmpty() && colPattern.isValid();
138 m_haveXPosPattern = !m_xPosPattern.isEmpty() && m_xPosPattern.isValid();
139 m_haveYPosPattern = !m_yPosPattern.isEmpty() && m_yPosPattern.isValid();
140 m_haveZPosPattern = !m_zPosPattern.isEmpty() && m_zPosPattern.isValid();
141
142 QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
143
144 // Default to display role if no mapping
145 m_xPosRole = roleHash.key(avalue: m_proxy->xPosRole().toLatin1(), defaultValue: noRoleIndex);
146 m_yPosRole = roleHash.key(avalue: m_proxy->yPosRole().toLatin1(), defaultValue: Qt::DisplayRole);
147 m_zPosRole = roleHash.key(avalue: m_proxy->zPosRole().toLatin1(), defaultValue: noRoleIndex);
148 int rowCount = m_itemModel->rowCount();
149 int columnCount = m_itemModel->columnCount();
150
151 if (m_proxy->useModelCategories()) {
152 // If dimensions have changed, recreate the array
153 if (m_proxyArray != m_proxy->array() || columnCount != m_proxy->columnCount()
154 || rowCount != m_proxyArray->size()) {
155 m_proxyArray = new QSurfaceDataArray;
156 m_proxyArray->reserve(alloc: rowCount);
157 for (int i = 0; i < rowCount; i++)
158 m_proxyArray->append(t: new QSurfaceDataRow(columnCount));
159 }
160 for (int i = 0; i < rowCount; i++) {
161 QSurfaceDataRow &newProxyRow = *m_proxyArray->at(i);
162 for (int j = 0; j < columnCount; j++) {
163 QModelIndex index = m_itemModel->index(row: i, column: j);
164 QVariant xValueVar = index.data(arole: m_xPosRole);
165 QVariant yValueVar = index.data(arole: m_yPosRole);
166 QVariant zValueVar = index.data(arole: m_zPosRole);
167 float xPos;
168 float yPos;
169 float zPos;
170 if (m_xPosRole != noRoleIndex) {
171 if (m_haveXPosPattern)
172 xPos = xValueVar.toString().replace(rx: m_xPosPattern, after: m_xPosReplace).toFloat();
173 else
174 xPos = xValueVar.toFloat();
175 } else {
176 QString header = m_itemModel->headerData(section: j, orientation: Qt::Horizontal).toString();
177 bool ok = false;
178 float headerValue = header.toFloat(ok: &ok);
179 if (ok)
180 xPos = headerValue;
181 else
182 xPos = float(j);
183 }
184
185 if (m_haveYPosPattern)
186 yPos = yValueVar.toString().replace(rx: m_yPosPattern, after: m_yPosReplace).toFloat();
187 else
188 yPos = yValueVar.toFloat();
189
190 if (m_zPosRole != noRoleIndex) {
191 if (m_haveZPosPattern)
192 zPos = zValueVar.toString().replace(rx: m_zPosPattern, after: m_zPosReplace).toFloat();
193 else
194 zPos = zValueVar.toFloat();
195 } else {
196 QString header = m_itemModel->headerData(section: i, orientation: Qt::Vertical).toString();
197 bool ok = false;
198 float headerValue = header.toFloat(ok: &ok);
199 if (ok)
200 zPos = headerValue;
201 else
202 zPos = float(i);
203 }
204
205 newProxyRow[j].setPosition(QVector3D(xPos, yPos, zPos));
206 }
207 }
208 } else {
209 int rowRole = roleHash.key(avalue: m_proxy->rowRole().toLatin1());
210 int columnRole = roleHash.key(avalue: m_proxy->columnRole().toLatin1());
211 if (m_xPosRole == noRoleIndex)
212 m_xPosRole = columnRole;
213 if (m_zPosRole == noRoleIndex)
214 m_zPosRole = rowRole;
215
216 bool generateRows = m_proxy->autoRowCategories();
217 bool generateColumns = m_proxy->autoColumnCategories();
218
219 QStringList rowList;
220 QStringList columnList;
221 // For detecting duplicates in categories generation, using QHashes should be faster than
222 // simple QStringList::contains() check.
223 QHash<QString, bool> rowListHash;
224 QHash<QString, bool> columnListHash;
225
226 bool cumulative = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBAverage
227 || m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBCumulativeY;
228 bool average = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBAverage;
229 bool takeFirst = m_proxy->multiMatchBehavior() == QItemModelSurfaceDataProxy::MMBFirst;
230 QHash<QString, QHash<QString, int> > *matchCountMap = 0;
231 if (cumulative)
232 matchCountMap = new QHash<QString, QHash<QString, int> >;
233
234 // Sort values into rows and columns
235 typedef QHash<QString, QVector3D> ColumnValueMap;
236 QHash <QString, ColumnValueMap> itemValueMap;
237 for (int i = 0; i < rowCount; i++) {
238 for (int j = 0; j < columnCount; j++) {
239 QModelIndex index = m_itemModel->index(row: i, column: j);
240 QString rowRoleStr = index.data(arole: rowRole).toString();
241 if (haveRowPattern)
242 rowRoleStr.replace(rx: rowPattern, after: rowReplace);
243 QString columnRoleStr = index.data(arole: columnRole).toString();
244 if (haveColPattern)
245 columnRoleStr.replace(rx: colPattern, after: colReplace);
246 QVariant xValueVar = index.data(arole: m_xPosRole);
247 QVariant yValueVar = index.data(arole: m_yPosRole);
248 QVariant zValueVar = index.data(arole: m_zPosRole);
249 float xPos;
250 float yPos;
251 float zPos;
252 if (m_haveXPosPattern)
253 xPos = xValueVar.toString().replace(rx: m_xPosPattern, after: m_xPosReplace).toFloat();
254 else
255 xPos = xValueVar.toFloat();
256 if (m_haveYPosPattern)
257 yPos = yValueVar.toString().replace(rx: m_yPosPattern, after: m_yPosReplace).toFloat();
258 else
259 yPos = yValueVar.toFloat();
260 if (m_haveZPosPattern)
261 zPos = zValueVar.toString().replace(rx: m_zPosPattern, after: m_zPosReplace).toFloat();
262 else
263 zPos = zValueVar.toFloat();
264
265 QVector3D itemPos(xPos, yPos, zPos);
266
267 if (cumulative)
268 (*matchCountMap)[rowRoleStr][columnRoleStr]++;
269
270 if (cumulative) {
271 itemValueMap[rowRoleStr][columnRoleStr] += itemPos;
272 } else {
273 if (takeFirst && itemValueMap.contains(akey: rowRoleStr)) {
274 if (itemValueMap.value(akey: rowRoleStr).contains(akey: columnRoleStr))
275 continue; // We already have a value for this row/column combo
276 }
277 itemValueMap[rowRoleStr][columnRoleStr] = itemPos;
278 }
279
280 if (generateRows && !rowListHash.value(akey: rowRoleStr, adefaultValue: false)) {
281 rowListHash.insert(akey: rowRoleStr, avalue: true);
282 rowList << rowRoleStr;
283 }
284 if (generateColumns && !columnListHash.value(akey: columnRoleStr, adefaultValue: false)) {
285 columnListHash.insert(akey: columnRoleStr, avalue: true);
286 columnList << columnRoleStr;
287 }
288 }
289 }
290
291 if (generateRows)
292 m_proxy->dptr()->m_rowCategories = rowList;
293 else
294 rowList = m_proxy->rowCategories();
295
296 if (generateColumns)
297 m_proxy->dptr()->m_columnCategories = columnList;
298 else
299 columnList = m_proxy->columnCategories();
300
301 // If dimensions have changed, recreate the array
302 if (m_proxyArray != m_proxy->array() || columnList.size() != m_proxy->columnCount()
303 || rowList.size() != m_proxyArray->size()) {
304 m_proxyArray = new QSurfaceDataArray;
305 m_proxyArray->reserve(alloc: rowList.size());
306 for (int i = 0; i < rowList.size(); i++)
307 m_proxyArray->append(t: new QSurfaceDataRow(columnList.size()));
308 }
309 // Create data array from itemValueMap
310 for (int i = 0; i < rowList.size(); i++) {
311 QString rowKey = rowList.at(i);
312 QSurfaceDataRow &newProxyRow = *m_proxyArray->at(i);
313 for (int j = 0; j < columnList.size(); j++) {
314 QVector3D &itemPos = itemValueMap[rowKey][columnList.at(i: j)];
315 if (cumulative) {
316 if (average) {
317 itemPos /= float((*matchCountMap)[rowKey][columnList.at(i: j)]);
318 } else { // cumulativeY
319 float divisor = float((*matchCountMap)[rowKey][columnList.at(i: j)]);
320 itemPos.setX(itemPos.x() / divisor);
321 itemPos.setZ(itemPos.z() / divisor);
322 }
323 }
324 newProxyRow[j].setPosition(itemPos);
325 }
326 }
327
328 delete matchCountMap;
329 }
330
331 m_proxy->resetArray(newArray: m_proxyArray);
332}
333
334QT_END_NAMESPACE_DATAVISUALIZATION
335

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