- Qt 6.7
- Qt Graphs
- Surface Graph Gallery
- Qt Graphs | Commercial or GPLv3

# Surface Graph Gallery

     // Copyright (C) 2023 The Qt Company Ltd.
     // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

     #include "datasource.h"
     #include <QtCore/qmath.h>
     #include <QtCore/qrandom.h>

     DataSource::DataSource(QObject *parent)
         : QObject(parent)
         , m_index(-1)
     {
         qRegisterMetaType<QSurface3DSeries *>();
     }

     DataSource::~DataSource()
     {
         clearData();
     }

     void DataSource::generateData(int cacheCount,
                                   int rowCount,
                                   int columnCount,
                                   float xMin,
                                   float xMax,
                                   float yMin,
                                   float yMax,
                                   float zMin,
                                   float zMax)
     {
         if (!cacheCount || !rowCount || !columnCount)
             return;

         clearData();

         // Re-create the cache array
         m_data.resize(cacheCount);
         for (int i = 0; i < cacheCount; i++) {
             QSurfaceDataArray &array = m_data[i];
             array.reserve(rowCount);
             for (int j = 0; j < rowCount; j++)
                 array.append(QSurfaceDataRow(columnCount));
         }

         float xRange = xMax - xMin;
         float yRange = yMax - yMin;
         float zRange = zMax - zMin;
         int cacheIndexStep = columnCount / cacheCount;
         float cacheStep = float(cacheIndexStep) * xRange / float(columnCount);

         // Populate caches
         for (int i = 0; i < cacheCount; i++) {
             QSurfaceDataArray &cache = m_data[i];
             float cacheXAdjustment = cacheStep * i;
             float cacheIndexAdjustment = cacheIndexStep * i;
             for (int j = 0; j < rowCount; j++) {
                 QSurfaceDataRow &row = cache[j];
                 float rowMod = (float(j)) / float(rowCount);
                 float yRangeMod = yRange * rowMod;
                 float zRangeMod = zRange * rowMod;
                 float z = zRangeMod + zMin;
                 qreal waveAngleMul = M_PI * M_PI * rowMod;
                 float waveMul = yRangeMod * 0.2f;
                 for (int k = 0; k < columnCount; k++) {
                     float colMod = (float(k)) / float(columnCount);
                     float xRangeMod = xRange * colMod;
                     float x = xRangeMod + xMin + cacheXAdjustment;
                     float colWave = float(qSin((2.0 * M_PI * colMod) - (1.0 / 2.0 * M_PI)) + 1.0);
                     float y = (colWave * ((float(qSin(waveAngleMul * colMod) + 1.0)))) * waveMul
                               + QRandomGenerator::global()->bounded(0.15f) * yRangeMod;

                     int index = k + cacheIndexAdjustment;
                     if (index >= columnCount) {
                         // Wrap over
                         index -= columnCount;
                         x -= xRange;
                     }
                     row[index] = QSurfaceDataItem(x, y, z);
                 }
             }
         }
     }

     void DataSource::update(QSurface3DSeries *series)
     {
         if (series && m_data.size()) {
             // Each iteration uses data from a different cached array
             m_index++;
             if (m_index > m_data.count() - 1)
                 m_index = 0;

             QSurfaceDataArray array = m_data.at(m_index);
             int newRowCount = array.size();
             int newColumnCount = array.at(0).size();

             // If the first time or the dimensions of the cache array have changed,
             // reconstruct the reset array
             if (m_resetArray.isEmpty() || series->dataProxy()->rowCount() != newRowCount
                 || series->dataProxy()->columnCount() != newColumnCount) {
                 m_resetArray.clear();
                 m_resetArray.reserve(newRowCount);
                 for (int i = 0; i < newRowCount; i++)
                     m_resetArray.append(QSurfaceDataRow(newColumnCount));
             }

             // Copy items from our cache to the reset array
             for (int i = 0; i < newRowCount; i++) {
                 const QSurfaceDataRow &sourceRow = array.at(i);
                 QSurfaceDataRow &row = m_resetArray[i];
                 for (int j = 0; j < newColumnCount; j++)
                     row[j].setPosition(sourceRow.at(j).position());
             }

             // Notify the proxy that data has changed
             series->dataProxy()->resetArray(m_resetArray);
         }
     }

     void DataSource::clearData()
     {
         for (int i = 0; i < m_data.size(); i++) {
             QSurfaceDataArray &array = m_data[i];
             array.clear();
         }
     }