1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qsurfacedataproxy_p.h"
5#include "qsurface3dseries_p.h"
6#include "qabstract3daxis_p.h"
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 * \class QSurfaceDataProxy
12 * \inmodule QtDataVisualization
13 * \brief The QSurfaceDataProxy class is the data proxy for a 3D surface graph.
14 * \since QtDataVisualization 1.0
15 *
16 * A surface data proxy handles surface related data in rows. For this it
17 * provides two auxiliary typedefs: QtDataVisualization::QSurfaceDataArray and
18 * QtDataVisualization::QSurfaceDataRow. \c QSurfaceDataArray is a QList that
19 * controls the rows. \c QSurfaceDataRow is a QList that contains
20 * QSurfaceDataItem objects. For more information about how to feed the data to
21 * the proxy, see the sample code in the Q3DSurface documentation.
22 *
23 * All rows must have the same number of items.
24 *
25 * QSurfaceDataProxy takes ownership of all \c QSurfaceDataRow objects passed to
26 * it, whether directly or in a \c QSurfaceDataArray container.
27 * To use surface data row pointers to directly modify data after adding the
28 * array to the proxy, the appropriate signal must be emitted to update the
29 * graph.
30 *
31 * To make a sensible surface, the x-value of each successive item in all rows must be
32 * either ascending or descending throughout the row.
33 * Similarly, the z-value of each successive item in all columns must be either ascending or
34 * descending throughout the column.
35 *
36 * \note Currently only surfaces with straight rows and columns are fully supported. Any row
37 * with items that do not have the exact same z-value or any column with items
38 * that do not have the exact same x-value may get clipped incorrectly if the
39 * whole surface does not completely fit within the visible x-axis or z-axis
40 * ranges.
41 *
42 * \note Surfaces with less than two rows or columns are not considered valid surfaces and will
43 * not be rendered.
44 *
45 * \note On some environments, surfaces with a lot of visible vertices may not render, because
46 * they exceed the per-draw vertex count supported by the graphics driver.
47 * This is mostly an issue on 32-bit and OpenGL ES2 platforms.
48 *
49 * \sa {Qt Data Visualization Data Handling}
50 */
51
52/*!
53 * \typedef QSurfaceDataRow
54 * \relates QSurfaceDataProxy
55 *
56 * A list of \l {QSurfaceDataItem} objects.
57 */
58
59/*!
60 * \typedef QSurfaceDataArray
61 * \relates QSurfaceDataProxy
62 *
63 * A list of pointers to \l {QSurfaceDataRow} objects.
64 */
65
66/*!
67 * \qmltype SurfaceDataProxy
68 * \inqmlmodule QtDataVisualization
69 * \since QtDataVisualization 1.0
70 * \ingroup datavisualization_qml
71 * \instantiates QSurfaceDataProxy
72 * \inherits AbstractDataProxy
73 * \brief The data proxy for a 3D surface graph.
74 *
75 * This type handles surface data items. The data is arranged into rows and columns, and all rows must have
76 * the same number of columns.
77 *
78 * This type is uncreatable, but contains properties that are exposed via subtypes.
79 *
80 * For a more complete description, see QSurfaceDataProxy.
81 *
82 * \sa ItemModelSurfaceDataProxy, {Qt Data Visualization Data Handling}
83 */
84
85/*!
86 * \qmlproperty int SurfaceDataProxy::rowCount
87 * The number of rows in the data array.
88 */
89
90/*!
91 * \qmlproperty int SurfaceDataProxy::columnCount
92 * The number of columns in the data array.
93 */
94
95/*!
96 * \qmlproperty Surface3DSeries SurfaceDataProxy::series
97 *
98 * The series this proxy is attached to.
99 */
100
101/*!
102 * Constructs QSurfaceDataProxy with the given \a parent.
103 */
104QSurfaceDataProxy::QSurfaceDataProxy(QObject *parent) :
105 QAbstractDataProxy(new QSurfaceDataProxyPrivate(this), parent)
106{
107}
108
109/*!
110 * \internal
111 */
112QSurfaceDataProxy::QSurfaceDataProxy(QSurfaceDataProxyPrivate *d, QObject *parent) :
113 QAbstractDataProxy(d, parent)
114{
115}
116
117/*!
118 * Deletes the surface data proxy.
119 */
120QSurfaceDataProxy::~QSurfaceDataProxy()
121{
122}
123
124/*!
125 * \property QSurfaceDataProxy::series
126 *
127 * \brief The series this proxy is attached to.
128 */
129QSurface3DSeries *QSurfaceDataProxy::series() const
130{
131 return static_cast<QSurface3DSeries *>(d_ptr->series());
132}
133
134/*!
135 * Takes ownership of the array \a newArray. Clears the existing array if the
136 * new array differs from it. If the arrays are the same, this function
137 * just triggers the arrayReset() signal.
138 *
139 * Passing a null array deletes the old array and creates a new empty array.
140 * All rows in \a newArray must be of same length.
141 */
142void QSurfaceDataProxy::resetArray(QSurfaceDataArray *newArray)
143{
144 if (dptr()->m_dataArray != newArray) {
145 dptr()->resetArray(newArray);
146 }
147 emit arrayReset();
148 emit rowCountChanged(count: rowCount());
149 emit columnCountChanged(count: columnCount());
150}
151
152/*!
153 * Changes an existing row by replacing the row at the position \a rowIndex
154 * with the new row specified by \a row. The new row can be the same as the
155 * existing row already stored at the \a rowIndex. The new row must have
156 * the same number of columns as the row it is replacing.
157 */
158void QSurfaceDataProxy::setRow(int rowIndex, QSurfaceDataRow *row)
159{
160 dptr()->setRow(rowIndex, row);
161 emit rowsChanged(startIndex: rowIndex, count: 1);
162}
163
164/*!
165 * Changes existing rows by replacing the rows starting at the position
166 * \a rowIndex with the new rows specifies by \a rows.
167 * The rows in the \a rows array can be the same as the existing rows already
168 * stored at the \a rowIndex. The new rows must have the same number of columns
169 * as the rows they are replacing.
170 */
171void QSurfaceDataProxy::setRows(int rowIndex, const QSurfaceDataArray &rows)
172{
173 dptr()->setRows(rowIndex, rows);
174 emit rowsChanged(startIndex: rowIndex, count: rows.size());
175}
176
177/*!
178 * Changes a single item at the position specified by \a rowIndex and
179 * \a columnIndex to the item \a item.
180 */
181void QSurfaceDataProxy::setItem(int rowIndex, int columnIndex, const QSurfaceDataItem &item)
182{
183 dptr()->setItem(rowIndex, columnIndex, item);
184 emit itemChanged(rowIndex, columnIndex);
185}
186
187/*!
188 * Changes a single item at the position \a position to the item \a item.
189 * The x-value of \a position indicates the row and the y-value indicates the
190 * column.
191 */
192void QSurfaceDataProxy::setItem(const QPoint &position, const QSurfaceDataItem &item)
193{
194 setItem(rowIndex: position.x(), columnIndex: position.y(), item);
195}
196
197/*!
198 * Adds the new row \a row to the end of an array. The new row must have
199 * the same number of columns as the rows in the initial array.
200 *
201 * Returns the index of the added row.
202 */
203int QSurfaceDataProxy::addRow(QSurfaceDataRow *row)
204{
205 int addIndex = dptr()->addRow(row);
206 emit rowsAdded(startIndex: addIndex, count: 1);
207 emit rowCountChanged(count: rowCount());
208 return addIndex;
209}
210
211/*!
212 * Adds new \a rows to the end of an array. The new rows must have the same
213 * number of columns as the rows in the initial array.
214 *
215 * Returns the index of the first added row.
216 */
217int QSurfaceDataProxy::addRows(const QSurfaceDataArray &rows)
218{
219 int addIndex = dptr()->addRows(rows);
220 emit rowsAdded(startIndex: addIndex, count: rows.size());
221 emit rowCountChanged(count: rowCount());
222 return addIndex;
223}
224
225/*!
226 * Inserts the new row \a row into \a rowIndex.
227 * If \a rowIndex is equal to the array size, the rows are added to the end of
228 * the array. The new row must have the same number of columns as the rows in
229 * the initial array.
230 */
231void QSurfaceDataProxy::insertRow(int rowIndex, QSurfaceDataRow *row)
232{
233 dptr()->insertRow(rowIndex, row);
234 emit rowsInserted(startIndex: rowIndex, count: 1);
235 emit rowCountChanged(count: rowCount());
236}
237
238/*!
239 * Inserts new \a rows into \a rowIndex.
240 * If \a rowIndex is equal to the array size, the rows are added to the end of
241 * the array. The new \a rows must have the same number of columns as the rows
242 * in the initial array.
243 */
244void QSurfaceDataProxy::insertRows(int rowIndex, const QSurfaceDataArray &rows)
245{
246 dptr()->insertRows(rowIndex, rows);
247 emit rowsInserted(startIndex: rowIndex, count: rows.size());
248 emit rowCountChanged(count: rowCount());
249}
250
251/*!
252 * Removes the number of rows specified by \a removeCount starting at the
253 * position \a rowIndex. Attempting to remove rows past the end of the
254 * array does nothing.
255 */
256void QSurfaceDataProxy::removeRows(int rowIndex, int removeCount)
257{
258 if (rowIndex < rowCount() && removeCount >= 1) {
259 dptr()->removeRows(rowIndex, removeCount);
260 emit rowsRemoved(startIndex: rowIndex, count: removeCount);
261 emit rowCountChanged(count: rowCount());
262 }
263}
264
265/*!
266 * Returns the pointer to the data array.
267 */
268const QSurfaceDataArray *QSurfaceDataProxy::array() const
269{
270 return dptrc()->m_dataArray;
271}
272
273/*!
274 * Returns the pointer to the item at the position specified by \a rowIndex and
275 * \a columnIndex. It is guaranteed to be valid only
276 * until the next call that modifies data.
277 */
278const QSurfaceDataItem *QSurfaceDataProxy::itemAt(int rowIndex, int columnIndex) const
279{
280 const QSurfaceDataArray &dataArray = *dptrc()->m_dataArray;
281 Q_ASSERT(rowIndex >= 0 && rowIndex < dataArray.size());
282 const QSurfaceDataRow &dataRow = *dataArray[rowIndex];
283 Q_ASSERT(columnIndex >= 0 && columnIndex < dataRow.size());
284 return &dataRow.at(i: columnIndex);
285}
286
287/*!
288 * Returns the pointer to the item at the position \a position. The x-value of
289 * \a position indicates the row and the y-value indicates the column. The item
290 * is guaranteed to be valid only until the next call that modifies data.
291 */
292const QSurfaceDataItem *QSurfaceDataProxy::itemAt(const QPoint &position) const
293{
294 return itemAt(rowIndex: position.x(), columnIndex: position.y());
295}
296
297/*!
298 * \property QSurfaceDataProxy::rowCount
299 *
300 * \brief The number of rows in the data array.
301 */
302int QSurfaceDataProxy::rowCount() const
303{
304 return dptrc()->m_dataArray->size();
305}
306
307/*!
308 * \property QSurfaceDataProxy::columnCount
309 *
310 * \brief The number of columns in the data array.
311 */
312int QSurfaceDataProxy::columnCount() const
313{
314 if (dptrc()->m_dataArray->size() > 0)
315 return dptrc()->m_dataArray->at(i: 0)->size();
316 else
317 return 0;
318}
319
320/*!
321 * \internal
322 */
323QSurfaceDataProxyPrivate *QSurfaceDataProxy::dptr()
324{
325 return static_cast<QSurfaceDataProxyPrivate *>(d_ptr.data());
326}
327
328/*!
329 * \internal
330 */
331const QSurfaceDataProxyPrivate *QSurfaceDataProxy::dptrc() const
332{
333 return static_cast<const QSurfaceDataProxyPrivate *>(d_ptr.data());
334}
335
336/*!
337 * \fn void QSurfaceDataProxy::arrayReset()
338 *
339 * This signal is emitted when the data array is reset.
340 * If the contents of the whole array are changed without calling resetArray(),
341 * this signal needs to be emitted to update the graph.
342 */
343
344/*!
345 * \fn void QSurfaceDataProxy::rowsAdded(int startIndex, int count)
346 *
347 * This signal is emitted when the number of rows specified by \a count is
348 * added starting at the position \a startIndex.
349 * If rows are added to the array without calling addRow() or addRows(),
350 * this signal needs to be emitted to update the graph.
351 */
352
353/*!
354 * \fn void QSurfaceDataProxy::rowsChanged(int startIndex, int count)
355 *
356 * This signal is emitted when the number of rows specified by \a count is
357 * changed starting at the position \a startIndex.
358 * If rows are changed in the array without calling setRow() or setRows(),
359 * this signal needs to be emitted to update the graph.
360 */
361
362/*!
363 * \fn void QSurfaceDataProxy::rowsRemoved(int startIndex, int count)
364 *
365 * This signal is emitted when the number of rows specified by \a count is
366 * removed starting at the position \a startIndex.
367 *
368 * The index is the current array size if the rows were removed from the end of
369 * the array. If rows are removed from the array without calling removeRows(),
370 * this signal needs to be emitted to update the graph.
371 */
372
373/*!
374 * \fn void QSurfaceDataProxy::rowsInserted(int startIndex, int count)
375 *
376 * This signal is emitted when the number of rows specified by \a count is
377 * inserted at the position \a startIndex.
378 *
379 * If rows are inserted into the array without calling insertRow() or
380 * insertRows(), this signal needs to be emitted to update the graph.
381 */
382
383/*!
384 * \fn void QSurfaceDataProxy::itemChanged(int rowIndex, int columnIndex)
385 *
386 * This signal is emitted when the item at the position specified by \a rowIndex
387 * and \a columnIndex changes.
388 * If the item is changed in the array without calling setItem(),
389 * this signal needs to be emitted to update the graph.
390 */
391
392// QSurfaceDataProxyPrivate
393
394QSurfaceDataProxyPrivate::QSurfaceDataProxyPrivate(QSurfaceDataProxy *q)
395 : QAbstractDataProxyPrivate(q, QAbstractDataProxy::DataTypeSurface),
396 m_dataArray(new QSurfaceDataArray)
397{
398}
399
400QSurfaceDataProxyPrivate::~QSurfaceDataProxyPrivate()
401{
402 clearArray();
403}
404
405void QSurfaceDataProxyPrivate::resetArray(QSurfaceDataArray *newArray)
406{
407 if (!newArray)
408 newArray = new QSurfaceDataArray;
409
410 if (newArray != m_dataArray) {
411 clearArray();
412 m_dataArray = newArray;
413 }
414}
415
416void QSurfaceDataProxyPrivate::setRow(int rowIndex, QSurfaceDataRow *row)
417{
418 Q_ASSERT(rowIndex >= 0 && rowIndex < m_dataArray->size());
419 Q_ASSERT(m_dataArray->at(rowIndex)->size() == row->size());
420
421 if (row != m_dataArray->at(i: rowIndex)) {
422 clearRow(rowIndex);
423 (*m_dataArray)[rowIndex] = row;
424 }
425}
426
427void QSurfaceDataProxyPrivate::setRows(int rowIndex, const QSurfaceDataArray &rows)
428{
429 QSurfaceDataArray &dataArray = *m_dataArray;
430 Q_ASSERT(rowIndex >= 0 && (rowIndex + rows.size()) <= dataArray.size());
431
432 for (int i = 0; i < rows.size(); i++) {
433 Q_ASSERT(m_dataArray->at(rowIndex)->size() == rows.at(i)->size());
434 if (rows.at(i) != dataArray.at(i: rowIndex)) {
435 clearRow(rowIndex);
436 dataArray[rowIndex] = rows.at(i);
437 }
438 rowIndex++;
439 }
440}
441
442void QSurfaceDataProxyPrivate::setItem(int rowIndex, int columnIndex, const QSurfaceDataItem &item)
443{
444 Q_ASSERT(rowIndex >= 0 && rowIndex < m_dataArray->size());
445 QSurfaceDataRow &row = *(*m_dataArray)[rowIndex];
446 Q_ASSERT(columnIndex < row.size());
447 row[columnIndex] = item;
448}
449
450int QSurfaceDataProxyPrivate::addRow(QSurfaceDataRow *row)
451{
452 Q_ASSERT(m_dataArray->isEmpty()
453 || m_dataArray->at(0)->size() == row->size());
454 int currentSize = m_dataArray->size();
455 m_dataArray->append(t: row);
456 return currentSize;
457}
458
459int QSurfaceDataProxyPrivate::addRows(const QSurfaceDataArray &rows)
460{
461 int currentSize = m_dataArray->size();
462 for (int i = 0; i < rows.size(); i++) {
463 Q_ASSERT(m_dataArray->isEmpty()
464 || m_dataArray->at(0)->size() == rows.at(i)->size());
465 m_dataArray->append(t: rows.at(i));
466 }
467 return currentSize;
468}
469
470void QSurfaceDataProxyPrivate::insertRow(int rowIndex, QSurfaceDataRow *row)
471{
472 Q_ASSERT(rowIndex >= 0 && rowIndex <= m_dataArray->size());
473 Q_ASSERT(m_dataArray->isEmpty()
474 || m_dataArray->at(0)->size() == row->size());
475 m_dataArray->insert(i: rowIndex, t: row);
476}
477
478void QSurfaceDataProxyPrivate::insertRows(int rowIndex, const QSurfaceDataArray &rows)
479{
480 Q_ASSERT(rowIndex >= 0 && rowIndex <= m_dataArray->size());
481
482 for (int i = 0; i < rows.size(); i++) {
483 Q_ASSERT(m_dataArray->isEmpty()
484 || m_dataArray->at(0)->size() == rows.at(i)->size());
485 m_dataArray->insert(i: rowIndex++, t: rows.at(i));
486 }
487}
488
489void QSurfaceDataProxyPrivate::removeRows(int rowIndex, int removeCount)
490{
491 Q_ASSERT(rowIndex >= 0);
492 int maxRemoveCount = m_dataArray->size() - rowIndex;
493 removeCount = qMin(a: removeCount, b: maxRemoveCount);
494 for (int i = 0; i < removeCount; i++) {
495 clearRow(rowIndex);
496 m_dataArray->removeAt(i: rowIndex);
497 }
498}
499
500QSurfaceDataProxy *QSurfaceDataProxyPrivate::qptr()
501{
502 return static_cast<QSurfaceDataProxy *>(q_ptr);
503}
504
505void QSurfaceDataProxyPrivate::limitValues(QVector3D &minValues, QVector3D &maxValues,
506 QAbstract3DAxis *axisX, QAbstract3DAxis *axisY,
507 QAbstract3DAxis *axisZ) const
508{
509 float min = 0.0f;
510 float max = 0.0f;
511
512 int rows = m_dataArray->size();
513 int columns = 0;
514 if (rows)
515 columns = m_dataArray->at(i: 0)->size();
516
517 if (rows && columns) {
518 min = m_dataArray->at(i: 0)->at(i: 0).y();
519 max = m_dataArray->at(i: 0)->at(i: 0).y();
520 }
521
522 for (int i = 0; i < rows; i++) {
523 QSurfaceDataRow *row = m_dataArray->at(i);
524 if (row) {
525 for (int j = 0; j < columns; j++) {
526 float itemValue = m_dataArray->at(i)->at(i: j).y();
527 if (qIsNaN(f: itemValue) || qIsInf(f: itemValue))
528 continue;
529 if ((min > itemValue || (qIsNaN(f: min) || qIsInf(f: min)))
530 && isValidValue(value: itemValue, axis: axisY)) {
531 min = itemValue;
532 }
533 if (max < itemValue || (qIsNaN(f: max) || qIsInf(f: max)))
534 max = itemValue;
535 }
536 }
537 }
538
539 minValues.setY(min);
540 maxValues.setY(max);
541
542 if (columns) {
543 // Have some defaults
544 float xLow = m_dataArray->at(i: 0)->at(i: 0).x();
545 float xHigh = m_dataArray->at(i: 0)->last().x();
546 float zLow = m_dataArray->at(i: 0)->at(i: 0).z();
547 float zHigh = m_dataArray->last()->at(i: 0).z();
548 for (int i = 0; i < rows; i++) {
549 for (int j = 0; j < columns; j++) {
550 float zItemValue = m_dataArray->at(i)->at(i: j).z();
551 if (qIsNaN(f: zItemValue) || qIsInf(f: zItemValue))
552 continue;
553 else if (isValidValue(value: zItemValue, axis: axisZ))
554 zLow = qMin(a: zLow,b: zItemValue);
555 }
556 if (!qIsNaN(f: zLow) && !qIsInf(f: zLow))
557 break;
558 }
559 for (int i = rows - 1; i >= 0; i--) {
560 for (int j = 0; j < columns; j++) {
561 float zItemValue = m_dataArray->at(i)->at(i: j).z();
562 if (qIsNaN(f: zItemValue) || qIsInf(f: zItemValue))
563 continue;
564 else if (isValidValue(value: zItemValue, axis: axisZ))
565 {
566 if (!qIsNaN(f: zHigh) && !qIsInf(f: zHigh))
567 zHigh = qMax(a: zHigh, b: zItemValue);
568 else
569 zHigh = zItemValue;
570 }
571 }
572 if (!qIsNaN(f: zHigh) && !qIsInf(f: zHigh))
573 break;
574 }
575 for (int j = 0; j<columns; j++){
576 for (int i = 0; i < rows; i++) {
577 float xItemValue = m_dataArray->at(i)->at(i: j).x();
578 if (qIsNaN(f: xItemValue) || qIsInf(f: xItemValue))
579 continue;
580 else if (isValidValue(value: xItemValue, axis: axisX))
581 xLow = qMin(a: xLow, b: xItemValue);
582 }
583 if (!qIsNaN(f: xLow) && !qIsInf(f: xLow))
584 break;
585 }
586 for (int j = columns-1; j >= 0; j--){
587 for (int i = 0; i < rows; i++) {
588 float xItemValue = m_dataArray->at(i)->at(i: j).x();
589 if (qIsNaN(f: xItemValue) || qIsInf(f: xItemValue))
590 continue;
591 else if (isValidValue(value: xItemValue, axis: axisX))
592 {
593 if (!qIsNaN(f: xHigh) && !qIsInf(f: xHigh))
594 xHigh = qMax(a: xHigh, b: xItemValue);
595 else
596 xHigh = xItemValue;
597 }
598 }
599 if (!qIsNaN(f: xHigh) && !qIsInf(f: xHigh))
600 break;
601 }
602 minValues.setX(xLow);
603 minValues.setZ(zLow);
604 maxValues.setX(xHigh);
605 maxValues.setZ(zHigh);
606 } else {
607 minValues.setX(axisX->d_ptr->allowZero() ? 0.0f : 1.0f);
608 minValues.setZ(axisZ->d_ptr->allowZero() ? 0.0f : 1.0f);
609 maxValues.setX(axisX->d_ptr->allowZero() ? 0.0f : 1.0f);
610 maxValues.setZ(axisZ->d_ptr->allowZero() ? 0.0f : 1.0f);
611 }
612}
613
614bool QSurfaceDataProxyPrivate::isValidValue(float value, QAbstract3DAxis *axis) const
615{
616 return (value > 0.0f || (value == 0.0f && axis->d_ptr->allowZero())
617 || (value < 0.0f && axis->d_ptr->allowNegatives()));
618}
619
620void QSurfaceDataProxyPrivate::clearRow(int rowIndex)
621{
622 if (m_dataArray->at(i: rowIndex)) {
623 delete m_dataArray->at(i: rowIndex);
624 (*m_dataArray)[rowIndex] = 0;
625 }
626}
627
628void QSurfaceDataProxyPrivate::clearArray()
629{
630 for (int i = 0; i < m_dataArray->size(); i++)
631 clearRow(rowIndex: i);
632 m_dataArray->clear();
633 delete m_dataArray;
634}
635
636void QSurfaceDataProxyPrivate::setSeries(QAbstract3DSeries *series)
637{
638 QAbstractDataProxyPrivate::setSeries(series);
639 QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
640 emit qptr()->seriesChanged(series: surfaceSeries);
641}
642
643QT_END_NAMESPACE
644

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