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

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