Soroush Shariaty
Soroush Shariaty

Reputation: 15

Q3DSurface only paints half of the given range of data

I have a Q3DSurface which draws a surface every 100 milliseconds with a time dependent function. The problem is only a quarter of my data is drawn every time. Here is the function that I use to generate my data.

void Waterfall3dChart::generateWaterfallData(float t) {
    const int SIZE = 100;   // Define the size of the grid
    const double A = 0.7;   // Amplitude
    const double k_x = 0.1; // Wave number in x direction
    const double k_y = 0.1; // Wave number in y direction
    const double omega = 1; // Angular frequency (Increase it to increase animation speed)

    float z;
    auto* dataArray = new QSurfaceDataArray;
    dataArray->reserve(SIZE);
    for (int x = 0; x < SIZE; ++x) {
        auto* newRow = new QSurfaceDataRow;
        newRow->reserve(SIZE);
        for (int y = 0; y < SIZE; ++y) {
            // Combine sine and Gaussian functions
            z = A * std::sin(k_x * x + omega * t) + std::cos(k_y * y);
            newRow->append(QSurfaceDataItem({(float)x, z, (float)y}));
        }
        dataArray->append(newRow);
    }

    m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawFlag::DrawSurfaceAndWireframe);
    m_sqrtSinSeries->setFlatShadingEnabled(true);

    m_surfaceGraph->axisX()->setLabelFormat("%.2f");
    m_surfaceGraph->axisZ()->setLabelFormat("%.2f");

    m_surfaceGraph->axisX()->setRange(0, SIZE);
    m_surfaceGraph->axisY()->setRange(sampleMin, sampleMax);
    m_surfaceGraph->axisZ()->setRange(0, SIZE);

    m_surfaceGraph->axisX()->setLabelAutoRotation(30.f);
    m_surfaceGraph->axisY()->setLabelAutoRotation(90.f);
    m_surfaceGraph->axisZ()->setLabelAutoRotation(30.f);

    m_surfaceGraph->addSeries(m_sqrtSinSeries);

    m_surfaceGraph->axisX()->setTitleVisible(true);
    m_surfaceGraph->axisY()->setTitleVisible(true);
    m_surfaceGraph->axisZ()->setTitleVisible(true);

    m_surfaceGraph->axisX()->setTitle("X");
    m_surfaceGraph->axisY()->setTitle("Y");
    m_surfaceGraph->axisZ()->setTitle("Z");

    m_sqrtSinProxy->resetArray(dataArray);
}

Which the argument t is current time in milliseconds. As can be seen I have set my Size variable to 100. So my x and z coordinates should have values from 0 to 100. Here is the result of my code.

Code run result

Notice that only half of my range of x (0 to 50) is drawn. the same is also happening for z coordinate. I also tested with other values of SIZE but the problem is the same. only half of the ranges are drawn every time. What could be the problem here?

Here is the full version of my code for testing/reproducing.

Waterfall3DChart.h

#ifndef WATERFALL3DCHART_H
#define WATERFALL3DCHART_H

#include <QHBoxLayout>
#include <QObject>
#include <QWidget>
#include <QtDataVisualization/q3dsurface.h>

class Waterfall3dChart : public QObject
{
        Q_OBJECT
    public:
        explicit Waterfall3dChart(QObject* parent = nullptr);
        bool initialize();
        QWidget* surfaceWidget() { return m_surfaceWidget; }

    private:
        Q3DSurface* m_surfaceGraph = nullptr;
        QWidget* m_container = nullptr;
        QWidget* m_surfaceWidget = nullptr;
        QSurfaceDataProxy* m_sqrtSinProxy = nullptr;
        QSurface3DSeries* m_sqrtSinSeries = nullptr;
        void generateWaterfallData(float t);
        void setGradient();

        // QObject interface
    protected:
        void timerEvent(QTimerEvent* event) override;
};

#endif // WATERFALL3DCHART_H

Waterfall3DChart.cpp

#include "waterfall_3d_chart.h"
#include <QElapsedTimer>
#include <QTime>
#include <mutex>

const float sampleMin = -8.f;
const float sampleMax = 8.f;

Waterfall3dChart::Waterfall3dChart(QObject* parent) : QObject{parent} {
    m_surfaceGraph = new Q3DSurface();
    initialize();
    startTimer(100);
}

bool Waterfall3dChart::initialize() {
    if (!m_surfaceGraph->hasContext())
        return false;

    m_surfaceWidget = new QWidget;
    auto* hLayout = new QHBoxLayout(m_surfaceWidget);
    m_container = QWidget::createWindowContainer(m_surfaceGraph, m_surfaceWidget);
    m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_container->setFocusPolicy(Qt::StrongFocus);
    hLayout->addWidget(m_container, 1);

    auto* vLayout = new QVBoxLayout();
    hLayout->addLayout(vLayout);
    vLayout->setAlignment(Qt::AlignTop);

    m_surfaceGraph->scene()->activeCamera()->setZoomLevel(85.f);
    m_surfaceGraph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight);
    m_surfaceGraph->activeTheme()->setType(Q3DTheme::ThemeRetro);

    m_surfaceGraph->setAxisX(new QValue3DAxis);
    m_surfaceGraph->setAxisY(new QValue3DAxis);
    m_surfaceGraph->setAxisZ(new QValue3DAxis);

    m_sqrtSinProxy = new QSurfaceDataProxy();
    m_sqrtSinSeries = new QSurface3DSeries(m_sqrtSinProxy);
    setGradient();

    return true;
}

void Waterfall3dChart::generateWaterfallData(float t) {
    const int SIZE = 100;   // Define the size of the grid
    const double A = 0.7;   // Amplitude
    const double k_x = 0.1; // Wave number in x direction
    const double k_y = 0.1; // Wave number in y direction
    const double omega = 1; // Angular frequency (Increase it to increase animation speed)

    float z;
    auto* dataArray = new QSurfaceDataArray;
    dataArray->reserve(SIZE);
    for (int x = 0; x < SIZE; ++x) {
        auto* newRow = new QSurfaceDataRow;
        newRow->reserve(SIZE);
        for (int y = 0; y < SIZE; ++y) {
            // Combine sine and Gaussian functions
            z = A * std::sin(k_x * x + omega * t) + std::cos(k_y * y);
            newRow->append(QSurfaceDataItem({(float)x, z, (float)y}));
        }
        dataArray->append(newRow);
    }

    m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawFlag::DrawSurfaceAndWireframe);
    m_sqrtSinSeries->setFlatShadingEnabled(true);

    m_surfaceGraph->axisX()->setLabelFormat("%.2f");
    m_surfaceGraph->axisZ()->setLabelFormat("%.2f");

    m_surfaceGraph->axisX()->setRange(0, SIZE);
    m_surfaceGraph->axisY()->setRange(sampleMin, sampleMax);
    m_surfaceGraph->axisZ()->setRange(0, SIZE);

    m_surfaceGraph->axisX()->setLabelAutoRotation(30.f);
    m_surfaceGraph->axisY()->setLabelAutoRotation(90.f);
    m_surfaceGraph->axisZ()->setLabelAutoRotation(30.f);

    m_surfaceGraph->addSeries(m_sqrtSinSeries);

    m_surfaceGraph->axisX()->setTitleVisible(true);
    m_surfaceGraph->axisY()->setTitleVisible(true);
    m_surfaceGraph->axisZ()->setTitleVisible(true);

    m_surfaceGraph->axisX()->setTitle("X");
    m_surfaceGraph->axisY()->setTitle("Y");
    m_surfaceGraph->axisZ()->setTitle("Z");

    m_sqrtSinProxy->resetArray(dataArray);
}

void Waterfall3dChart::setGradient() {
    QLinearGradient gr;
    gr.setColorAt(0.f, Qt::darkGreen);
    gr.setColorAt(0.5f, Qt::yellow);
    gr.setColorAt(0.8f, Qt::red);
    gr.setColorAt(1.f, Qt::darkRed);

    m_sqrtSinSeries->setBaseGradient(gr);
    m_sqrtSinSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
}

void Waterfall3dChart::timerEvent(QTimerEvent* event) {
    generateWaterfallData(QTime::currentTime().msecsSinceStartOfDay());
}

Upvotes: 0

Views: 135

Answers (1)

vgziqy
vgziqy

Reputation: 11

For each axis, set the range like this maybe will be useful.But I don't know the reason. m_surfaceGraph->axisX()->setRange(-1, SIZE + 1); Another way is to change the code to

newRow->append(QSurfaceDataItem({(float)y, z, (float)x}));

Upvotes: 1

Related Questions