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

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