diff options
-rw-r--r-- | examples/qmlsurface/qml/qmlsurface/data.qml | 7 | ||||
-rw-r--r-- | examples/qmlsurface/qml/qmlsurface/main.qml | 10 | ||||
-rw-r--r-- | examples/surfacechart/chartmodifier.cpp | 82 | ||||
-rw-r--r-- | examples/surfacechart/chartmodifier.h | 20 | ||||
-rw-r--r-- | examples/surfacechart/main.cpp | 72 | ||||
-rw-r--r-- | src/datavisualization/data/qheightmapsurfacedataproxy.cpp | 101 | ||||
-rw-r--r-- | src/datavisualization/data/qheightmapsurfacedataproxy_p.h | 4 | ||||
-rw-r--r-- | src/datavisualization/data/qsurfacedataproxy.cpp | 3 | ||||
-rw-r--r-- | src/datavisualization/engine/surface3drenderer.cpp | 178 | ||||
-rw-r--r-- | src/datavisualization/engine/surface3drenderer_p.h | 10 | ||||
-rw-r--r-- | src/datavisualization/utils/surfaceobject.cpp | 6 | ||||
-rw-r--r-- | src/datavisualization/utils/surfaceobject_p.h | 4 |
12 files changed, 338 insertions, 159 deletions
diff --git a/examples/qmlsurface/qml/qmlsurface/data.qml b/examples/qmlsurface/qml/qmlsurface/data.qml index ccafc5d2..2c6849eb 100644 --- a/examples/qmlsurface/qml/qmlsurface/data.qml +++ b/examples/qmlsurface/qml/qmlsurface/data.qml @@ -28,6 +28,13 @@ Item { HeightMapSurfaceDataProxy { id: heightMapProxy heightMapFile: ":/heightmaps/image" + onArrayReset: { + // We don't want the default data values set by heightmap proxy. + minValueRows = 30 + maxValueRows = 60 + minValueColumns = 67 + maxValueColumns = 97 + } } SurfaceDataMapping { diff --git a/examples/qmlsurface/qml/qmlsurface/main.qml b/examples/qmlsurface/qml/qmlsurface/main.qml index b8c09b35..7d83c1a0 100644 --- a/examples/qmlsurface/qml/qmlsurface/main.qml +++ b/examples/qmlsurface/qml/qmlsurface/main.qml @@ -50,9 +50,7 @@ Item { cameraPreset: Surface3D.PresetIsometricLeft dataProxy: surfaceData.heightProxy axisY.min: 0.0 - axisY.max: 255.0 - axisX.max: 40.0 - axisZ.max: 40.0 + axisY.max: 250.0 axisX.segmentCount: 10 axisX.subSegmentCount: 2 axisX.labelFormat: "%i" @@ -141,14 +139,10 @@ Item { onClicked: { if (surfaceplot.dataProxy === surfaceData.heightProxy) { surfaceplot.axisY.max = 500.0 - surfaceplot.axisX.max = 99.0 - surfaceplot.axisZ.max = 99.0 surfaceplot.dataProxy = surfaceData.proxy text = "Switch to Height Map Proxy" } else { - surfaceplot.axisY.max = 255.0 - surfaceplot.axisX.max = 40.0 - surfaceplot.axisZ.max = 40.0 + surfaceplot.axisY.max = 250.0 surfaceplot.dataProxy = surfaceData.heightProxy text = "Switch to Item Model Proxy" } diff --git a/examples/surfacechart/chartmodifier.cpp b/examples/surfacechart/chartmodifier.cpp index 608f2701..41be0ecc 100644 --- a/examples/surfacechart/chartmodifier.cpp +++ b/examples/surfacechart/chartmodifier.cpp @@ -17,25 +17,36 @@ ****************************************************************************/ #include "chartmodifier.h" -#include <Q3DValueAxis> -#include <QSurfaceDataProxy> +#include <QtDataVisualization/Q3DValueAxis> +#include <QtDataVisualization/QSurfaceDataProxy> #include <qmath.h> - #include <QDebug> QT_DATAVISUALIZATION_USE_NAMESPACE ChartModifier::ChartModifier(Q3DSurface *chart) : m_chart(chart), - m_xCount(30), - m_zCount(30), + m_gridSliderX(0), + m_gridSliderZ(0), + m_axisRangeSliderX(0), + m_axisRangeSliderZ(0), + m_axisMinSliderX(0), + m_axisMinSliderZ(0), + m_xCount(50), + m_zCount(50), m_activeSample(0), - m_fontSize(40.0f) + m_fontSize(40), + m_rangeX(16.0), + m_rangeZ(16.0), + m_minX(-8.0), + m_minZ(-8.0) { m_chart->setAxisX(new Q3DValueAxis); m_chart->setAxisY(new Q3DValueAxis); m_chart->setAxisZ(new Q3DValueAxis); + m_chart->axisX()->setRange(m_minX, m_minX + m_rangeX); + m_chart->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ); } ChartModifier::~ChartModifier() @@ -80,7 +91,7 @@ void ChartModifier::toggleSqrtSin(bool enable) m_chart->axisX()->setLabelFormat("%.2f"); m_chart->axisZ()->setLabelFormat("%.2f"); - m_chart->activeDataProxy()->resetArray(dataArray, -8.0, 8.0, -8.0, 8.0); + resetArrayAndSliders(dataArray, -8.0, 8.0, -8.0, 8.0); m_activeSample = ChartModifier::SqrtSin; } else { @@ -94,21 +105,20 @@ void ChartModifier::togglePlane(bool enable) if (enable) { QSurfaceDataArray *dataArray = new QSurfaceDataArray; - qreal y = 2.0 / qreal(m_zCount - 1); + qreal y = 1.0 / (qreal(m_zCount - 1) + qreal(m_xCount - 1)); dataArray->reserve(m_zCount); for (int i = 0; i < m_zCount; i++) { QSurfaceDataRow *newRow = new QSurfaceDataRow(m_xCount); for (int j = 0; j < m_xCount; j++) - (*newRow)[j] = i * y; + (*newRow)[j] = (i + j) * y; *dataArray << newRow; } - m_chart->axisY()->setAutoAdjustRange(true); - m_chart->axisX()->setSegmentCount(4); + m_chart->axisY()->setRange(0.0, 1.0); m_chart->axisX()->setLabelFormat("%.2f"); m_chart->axisZ()->setLabelFormat("%.2f"); - m_chart->activeDataProxy()->resetArray(dataArray, -2.0, 10.0, 16.0, 22.0); + resetArrayAndSliders(dataArray, -10.0, 10.0, -10.0, 20.0); m_activeSample = ChartModifier::Plane; } @@ -135,7 +145,7 @@ void ChartModifier::setHeightMapData(bool enable) m_chart->axisX()->setLabelFormat("%.1f N"); m_chart->axisZ()->setLabelFormat("%.1f E"); - m_chart->activeDataProxy()->resetArray(dataArray, 34.0, 40.0, 18.0, 24.0); + resetArrayAndSliders(dataArray, 18.0, 24.0, 34.0, 40.0); m_activeSample = ChartModifier::Map; } @@ -160,7 +170,7 @@ void ChartModifier::adjustXCount(int count) updateSamples(); - qDebug() << "X count = " << count; + qDebug() << "X count =" << count; } void ChartModifier::adjustZCount(int count) @@ -169,7 +179,39 @@ void ChartModifier::adjustZCount(int count) updateSamples(); - qDebug() << "Z count = " << count; + qDebug() << "Z count =" << count; +} + +void ChartModifier::adjustXRange(int range) +{ + m_rangeX = range; + m_chart->axisX()->setRange(m_minX, m_minX + m_rangeX); + + qDebug() << "X Range =" << range; +} + +void ChartModifier::adjustZRange(int range) +{ + m_rangeZ = range; + m_chart->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ); + + qDebug() << "Z Range =" << range; +} + +void ChartModifier::adjustXMin(int min) +{ + m_minX = min; + m_chart->axisX()->setRange(m_minX, m_minX + m_rangeX); + + qDebug() << "X Minimum =" << min; +} + +void ChartModifier::adjustZMin(int min) +{ + m_minZ = min; + m_chart->axisZ()->setRange(m_minZ, m_minZ + m_rangeZ); + + qDebug() << "Z Minimum =" << min; } void ChartModifier::colorPressed() @@ -199,6 +241,16 @@ void ChartModifier::changeTheme(int theme) m_chart->setTheme((QDataVis::ColorTheme)theme); } +void ChartModifier::resetArrayAndSliders(QSurfaceDataArray *array, qreal minZ, qreal maxZ, qreal minX, qreal maxX) +{ + m_axisMinSliderX->setValue(minX); + m_axisMinSliderZ->setValue(minZ); + m_axisRangeSliderX->setValue(maxX - minX); + m_axisRangeSliderZ->setValue(maxZ - minZ); + + m_chart->activeDataProxy()->resetArray(array, minZ, maxZ, minX, maxX); +} + void ChartModifier::changeShadowQuality(int quality) { QDataVis::ShadowQuality sq = QDataVis::ShadowQuality(quality); diff --git a/examples/surfacechart/chartmodifier.h b/examples/surfacechart/chartmodifier.h index ad0ea162..8d121976 100644 --- a/examples/surfacechart/chartmodifier.h +++ b/examples/surfacechart/chartmodifier.h @@ -20,6 +20,7 @@ #define CHARTMODIFIER_H #include <QtDataVisualization/Q3DSurface> +#include <QtDataVisualization/QSurfaceDataProxy> #include <QSlider> using namespace QtDataVisualization; @@ -45,8 +46,16 @@ public: void toggleGridSliderLock(bool enable); void setGridSliderX(QSlider *slider) { m_gridSliderX = slider; } void setGridSliderZ(QSlider *slider) { m_gridSliderZ = slider; } + void setAxisRangeSliderX(QSlider *slider) { m_axisRangeSliderX = slider; } + void setAxisRangeSliderZ(QSlider *slider) { m_axisRangeSliderZ = slider; } + void setAxisMinSliderX(QSlider *slider) { m_axisMinSliderX = slider; } + void setAxisMinSliderZ(QSlider *slider) { m_axisMinSliderZ = slider; } void adjustXCount(int count); void adjustZCount(int count); + void adjustXRange(int range); + void adjustZRange(int range); + void adjustXMin(int min); + void adjustZMin(int min); void updateSamples(); void colorPressed(); void changeFont(const QFont &font); @@ -57,14 +66,25 @@ public slots: void changeTheme(int theme); private: + void resetArrayAndSliders(QSurfaceDataArray *array, qreal minZ, qreal maxZ, qreal minX, + qreal maxX); + Q3DSurface *m_chart; QSlider *m_gridSliderX; QSlider *m_gridSliderZ; + QSlider *m_axisRangeSliderX; + QSlider *m_axisRangeSliderZ; + QSlider *m_axisMinSliderX; + QSlider *m_axisMinSliderZ; bool m_gridSlidersLocked; int m_xCount; int m_zCount; int m_activeSample; int m_fontSize; + qreal m_rangeX; + qreal m_rangeZ; + qreal m_minX; + qreal m_minZ; }; #endif // CHARTMODIFIER_H diff --git a/examples/surfacechart/main.cpp b/examples/surfacechart/main.cpp index 3b6ece67..00f3a1c9 100644 --- a/examples/surfacechart/main.cpp +++ b/examples/surfacechart/main.cpp @@ -19,7 +19,6 @@ #include "chartmodifier.h" #include <QApplication> -#include <QApplication> #include <QWidget> #include <QHBoxLayout> #include <QVBoxLayout> @@ -96,6 +95,32 @@ int main(int argc, char *argv[]) gridSliderZ->setMaximum(200); gridSliderZ->setEnabled(true); + QSlider *axisRangeSliderX = new QSlider(Qt::Horizontal, widget); + axisRangeSliderX->setTickInterval(1); + axisRangeSliderX->setMinimum(2); + axisRangeSliderX->setValue(16); + axisRangeSliderX->setMaximum(100); + axisRangeSliderX->setEnabled(true); + QSlider *axisRangeSliderZ = new QSlider(Qt::Horizontal, widget); + axisRangeSliderZ->setTickInterval(1); + axisRangeSliderZ->setMinimum(2); + axisRangeSliderZ->setValue(16); + axisRangeSliderZ->setMaximum(100); + axisRangeSliderZ->setEnabled(true); + + QSlider *axisMinSliderX = new QSlider(Qt::Horizontal, widget); + axisMinSliderX->setTickInterval(1); + axisMinSliderX->setMinimum(-50); + axisMinSliderX->setValue(-8); + axisMinSliderX->setMaximum(50); + axisMinSliderX->setEnabled(true); + QSlider *axisMinSliderZ = new QSlider(Qt::Horizontal, widget); + axisMinSliderZ->setTickInterval(1); + axisMinSliderZ->setMinimum(-50); + axisMinSliderZ->setValue(-8); + axisMinSliderZ->setMaximum(50); + axisMinSliderZ->setEnabled(true); + QLinearGradient gr(0, 0, 100, 1); gr.setColorAt(0.0, Qt::blue); gr.setColorAt(0.5, Qt::yellow); @@ -126,15 +151,15 @@ int main(int argc, char *argv[]) themeList->addItem(QStringLiteral("Isabelle")); themeList->setCurrentIndex(0); - QComboBox *shadowQuality = new QComboBox(widget); - shadowQuality->addItem(QStringLiteral("None")); - shadowQuality->addItem(QStringLiteral("Low")); - shadowQuality->addItem(QStringLiteral("Medium")); - shadowQuality->addItem(QStringLiteral("High")); - shadowQuality->addItem(QStringLiteral("Low Soft")); - shadowQuality->addItem(QStringLiteral("Medium Soft")); - shadowQuality->addItem(QStringLiteral("High Soft")); - shadowQuality->setCurrentIndex(3); +// QComboBox *shadowQuality = new QComboBox(widget); +// shadowQuality->addItem(QStringLiteral("None")); +// shadowQuality->addItem(QStringLiteral("Low")); +// shadowQuality->addItem(QStringLiteral("Medium")); +// shadowQuality->addItem(QStringLiteral("High")); +// shadowQuality->addItem(QStringLiteral("Low Soft")); +// shadowQuality->addItem(QStringLiteral("Medium Soft")); +// shadowQuality->addItem(QStringLiteral("High Soft")); +// shadowQuality->setCurrentIndex(3); // Add controls to the layout vLayout->addWidget(smoothCB); @@ -147,14 +172,20 @@ int main(int argc, char *argv[]) vLayout->addWidget(gridSlidersLockCB); vLayout->addWidget(gridSliderX); vLayout->addWidget(gridSliderZ); + vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis range"))); + vLayout->addWidget(axisRangeSliderX); + vLayout->addWidget(axisRangeSliderZ); + vLayout->addWidget(new QLabel(QStringLiteral("Adjust axis minimum"))); + vLayout->addWidget(axisMinSliderX); + vLayout->addWidget(axisMinSliderZ); vLayout->addWidget(colorPB); vLayout->addWidget(new QLabel(QStringLiteral("Change font"))); vLayout->addWidget(fontList); vLayout->addWidget(labelButton); vLayout->addWidget(new QLabel(QStringLiteral("Change theme"))); vLayout->addWidget(themeList); - vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality"))); - vLayout->addWidget(shadowQuality); +// vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality"))); +// vLayout->addWidget(shadowQuality); widget->show(); @@ -177,6 +208,14 @@ int main(int argc, char *argv[]) modifier, &ChartModifier::adjustXCount); QObject::connect(gridSliderZ, &QSlider::valueChanged, modifier, &ChartModifier::adjustZCount); + QObject::connect(axisRangeSliderX, &QSlider::valueChanged, + modifier, &ChartModifier::adjustXRange); + QObject::connect(axisRangeSliderZ, &QSlider::valueChanged, + modifier, &ChartModifier::adjustZRange); + QObject::connect(axisMinSliderX, &QSlider::valueChanged, + modifier, &ChartModifier::adjustXMin); + QObject::connect(axisMinSliderZ, &QSlider::valueChanged, + modifier, &ChartModifier::adjustZMin); QObject::connect(colorPB, &QPushButton::pressed, modifier, &ChartModifier::colorPressed); QObject::connect(fontList, &QFontComboBox::currentFontChanged, @@ -185,12 +224,17 @@ int main(int argc, char *argv[]) modifier, &ChartModifier::changeTransparency); QObject::connect(themeList, SIGNAL(currentIndexChanged(int)), modifier, SLOT(changeTheme(int))); - QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), - modifier, SLOT(changeShadowQuality(int))); +// QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), +// modifier, SLOT(changeShadowQuality(int))); modifier->setGridSliderZ(gridSliderZ); modifier->setGridSliderX(gridSliderX); + modifier->setAxisRangeSliderX(axisRangeSliderX); + modifier->setAxisRangeSliderZ(axisRangeSliderZ); + modifier->setAxisMinSliderX(axisMinSliderX); + modifier->setAxisMinSliderZ(axisMinSliderZ); modifier->toggleGridSliderLock(gridSlidersLockCB->checkState()); + sqrtSinCB->setChecked(true); return app.exec(); } diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp index 293eb9eb..f40fa395 100644 --- a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp +++ b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp @@ -82,7 +82,7 @@ QHeightMapSurfaceDataProxy::QHeightMapSurfaceDataProxy(QObject *parent) : * \sa heightMap */ QHeightMapSurfaceDataProxy::QHeightMapSurfaceDataProxy(const QImage &image, QObject *parent) : - QSurfaceDataProxy(new QHeightMapSurfaceDataProxyPrivate(this, image), parent) + QSurfaceDataProxy(new QHeightMapSurfaceDataProxyPrivate(this), parent) { setHeightMap(image); } @@ -118,50 +118,17 @@ QHeightMapSurfaceDataProxy::~QHeightMapSurfaceDataProxy() * grayscale images may improve data conversion speed for large images. * * Not recommended formats: all mono formats (for example QImage::Format_Mono). + * + * The height map is resolved asynchronously. QSurfaceDataProxy::arrayReset() is emitted when the + * data has been resolved. */ void QHeightMapSurfaceDataProxy::setHeightMap(const QImage &image) { dptr()->m_heightMap = image; - QImage heightImage = image; - // Convert to RGB32 to be sure we're reading the right bytes - if (heightImage.format() != QImage::Format_RGB32) - heightImage = image.convertToFormat(QImage::Format_RGB32); - - uchar *bits = heightImage.bits(); - - int imageHeight = heightImage.height(); - int imageWidth = heightImage.width(); - int bitCount = imageWidth * 4 * (imageHeight - 1); - int widthBits = imageWidth * 4; - qreal height = 0; - - QSurfaceDataArray *dataArray = new QSurfaceDataArray; - dataArray->reserve(imageHeight); - if (heightImage.isGrayscale()) { - // Grayscale, it's enough to read Red byte - for (int i = imageHeight; i > 0; i--, bitCount -= widthBits) { - QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); - for (int j = 0; j < imageWidth; j++) - (*newRow)[j] = qreal(bits[bitCount + (j * 4)]) + 0.1; // Add 0.1 to raise it above ground to avoid glimmering at 0 height - *dataArray << newRow; - } - } else { - // Not grayscale, we'll need to calculate height from RGB - for (int i = imageHeight; i > 0; i--, bitCount -= widthBits) { - QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); - for (int j = 0; j < imageWidth; j++) { - int nextpixel = j * 4; - height = (qreal(bits[bitCount + nextpixel]) - + qreal(bits[1 + bitCount + nextpixel]) - + qreal(bits[2 + bitCount + nextpixel])); - (*newRow)[j] = (height / 3.0) + 0.1; // Add 0.1 to raise it above ground to avoid glimmering at 0 height - } - *dataArray << newRow; - } - } - - resetArray(dataArray, 0.0, imageHeight, 0.0, imageWidth); + // We do resolving asynchronously to make qml onArrayReset handlers actually get the initial reset + if (!dptr()->m_resolveTimer.isActive()) + dptr()->m_resolveTimer.start(0); } QImage QHeightMapSurfaceDataProxy::heightMap() const @@ -210,17 +177,61 @@ const QHeightMapSurfaceDataProxyPrivate *QHeightMapSurfaceDataProxy::dptrc() con QHeightMapSurfaceDataProxyPrivate::QHeightMapSurfaceDataProxyPrivate(QHeightMapSurfaceDataProxy *q) : QSurfaceDataProxyPrivate(q) { + m_resolveTimer.setSingleShot(true); + QObject::connect(&m_resolveTimer, &QTimer::timeout, + this, &QHeightMapSurfaceDataProxyPrivate::handlePendingResolve); } -QHeightMapSurfaceDataProxyPrivate::QHeightMapSurfaceDataProxyPrivate(QHeightMapSurfaceDataProxy *q, - const QImage &image) - : QSurfaceDataProxyPrivate(q), - m_heightMap(image) +QHeightMapSurfaceDataProxyPrivate::~QHeightMapSurfaceDataProxyPrivate() { } -QHeightMapSurfaceDataProxyPrivate::~QHeightMapSurfaceDataProxyPrivate() +QHeightMapSurfaceDataProxy *QHeightMapSurfaceDataProxyPrivate::qptr() { + return static_cast<QHeightMapSurfaceDataProxy *>(q_ptr); +} + +void QHeightMapSurfaceDataProxyPrivate::handlePendingResolve() +{ + QImage heightImage = m_heightMap; + // Convert to RGB32 to be sure we're reading the right bytes + if (heightImage.format() != QImage::Format_RGB32) + heightImage = heightImage.convertToFormat(QImage::Format_RGB32); + + uchar *bits = heightImage.bits(); + + int imageHeight = heightImage.height(); + int imageWidth = heightImage.width(); + int bitCount = imageWidth * 4 * (imageHeight - 1); + int widthBits = imageWidth * 4; + qreal height = 0; + + QSurfaceDataArray *dataArray = new QSurfaceDataArray; + dataArray->reserve(imageHeight); + if (heightImage.isGrayscale()) { + // Grayscale, it's enough to read Red byte + for (int i = imageHeight; i > 0; i--, bitCount -= widthBits) { + QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); + for (int j = 0; j < imageWidth; j++) + (*newRow)[j] = qreal(bits[bitCount + (j * 4)]) + 0.1; // Add 0.1 to raise it above ground to avoid glimmering at 0 height + *dataArray << newRow; + } + } else { + // Not grayscale, we'll need to calculate height from RGB + for (int i = imageHeight; i > 0; i--, bitCount -= widthBits) { + QSurfaceDataRow *newRow = new QSurfaceDataRow(imageWidth); + for (int j = 0; j < imageWidth; j++) { + int nextpixel = j * 4; + height = (qreal(bits[bitCount + nextpixel]) + + qreal(bits[1 + bitCount + nextpixel]) + + qreal(bits[2 + bitCount + nextpixel])); + (*newRow)[j] = (height / 3.0) + 0.1; // Add 0.1 to raise it above ground to avoid glimmering at 0 height + } + *dataArray << newRow; + } + } + + qptr()->resetArray(dataArray, 0.0, imageHeight, 0.0, imageWidth); } QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy_p.h b/src/datavisualization/data/qheightmapsurfacedataproxy_p.h index 3cccb4ed..7140c8ef 100644 --- a/src/datavisualization/data/qheightmapsurfacedataproxy_p.h +++ b/src/datavisualization/data/qheightmapsurfacedataproxy_p.h @@ -31,6 +31,7 @@ #include "qheightmapsurfacedataproxy.h" #include "qsurfacedataproxy_p.h" +#include <QTimer> QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -40,14 +41,15 @@ class QHeightMapSurfaceDataProxyPrivate : public QSurfaceDataProxyPrivate public: QHeightMapSurfaceDataProxyPrivate(QHeightMapSurfaceDataProxy *q); - QHeightMapSurfaceDataProxyPrivate(QHeightMapSurfaceDataProxy *q, const QImage &image); virtual ~QHeightMapSurfaceDataProxyPrivate(); private: QHeightMapSurfaceDataProxy *qptr(); + void handlePendingResolve(); QImage m_heightMap; QString m_heightMapFile; + QTimer m_resolveTimer; friend class QHeightMapSurfaceDataProxy; }; diff --git a/src/datavisualization/data/qsurfacedataproxy.cpp b/src/datavisualization/data/qsurfacedataproxy.cpp index 30cc84f1..bd3688dc 100644 --- a/src/datavisualization/data/qsurfacedataproxy.cpp +++ b/src/datavisualization/data/qsurfacedataproxy.cpp @@ -39,6 +39,9 @@ const qreal defaultMaxValue = 10.0; * * All rows must have same number of values. * + * \note Surfaces with less than two rows or columns are not considered valid surfaces and will + * not get rendered. + * * QSurfaceDataProxy supports the following format tags for QAbstractDataProxy::setItemLabelFormat(): * \table * \row diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 9fea6ddb..67f12b0a 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -76,6 +76,16 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), m_scaleZWithBackground(0.0f), + m_surfaceScaleX(0.0f), + m_surfaceScaleZ(0.0f), + m_surfaceOffsetX(0.0f), + m_surfaceOffsetZ(0.0f), + m_minVisibleColumnValue(0.0f), + m_maxVisibleColumnValue(0.0f), + m_minVisibleRowValue(0.0f), + m_maxVisibleRowValue(0.0f), + m_dataStepX(0.0f), + m_dataStepZ(0.0f), m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), @@ -196,45 +206,17 @@ void Surface3DRenderer::initializeOpenGL() void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) { - // Make a copy of data array. for (int i = 0; i < m_dataArray.size(); i++) delete m_dataArray.at(i); m_dataArray.clear(); - const QSurfaceDataArray *array = dataProxy->array(); - - QRect sampleSpace = calculateSampleRect(dataProxy); - - m_dataArray.reserve(sampleSpace.height()); - for (int i = 0; i < sampleSpace.height(); i++) { - QSurfaceDataRow *newRow = new QSurfaceDataRow(); - newRow->resize(sampleSpace.width()); - for (int j = 0; j < sampleSpace.width(); j++) - (*newRow)[j] = array->at(i + sampleSpace.y())->at(j + sampleSpace.x()); - m_dataArray << newRow; - } - - // If data contains only one row, duplicate it to make surface - if (sampleSpace.height() == 1) { - QSurfaceDataRow *newRow = new QSurfaceDataRow(*m_dataArray.at(0)); - m_dataArray << newRow; - sampleSpace.setHeight(2); - } - - // If data contains only one column, duplicate the value to make surface - if (sampleSpace.width() == 1) { - for (int i = 0; i < sampleSpace.height(); i++) - (*m_dataArray.at(i)) << m_dataArray.at(i)->at(0); - sampleSpace.setWidth(2); - } - calculateSceneScalingFactors(); - if (m_dataArray.size() > 0) { - if (!m_surfaceObj) - loadSurfaceObj(); + const QSurfaceDataArray *array = dataProxy->array(); - sampleSpace.moveTo(0, 0); + // Need minimum of 2x2 array to draw a surface + if (array->size() >= 2 && array->at(0)->size() >= 2) { + QRect sampleSpace = calculateSampleRect(dataProxy); bool dimensionChanged = false; if (m_sampleSpace != sampleSpace) { @@ -242,13 +224,31 @@ void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) m_sampleSpace = sampleSpace; } - if (m_cachedSmoothSurface) - m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); - else - m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); + // TODO: Handle partial surface grids on the graph edges + if (sampleSpace.width() >= 2 && sampleSpace.height() >= 2) { + m_dataArray.reserve(sampleSpace.height()); + for (int i = 0; i < sampleSpace.height(); i++) { + QSurfaceDataRow *newRow = new QSurfaceDataRow(); + newRow->resize(sampleSpace.width()); + for (int j = 0; j < sampleSpace.width(); j++) + (*newRow)[j] = array->at(i + sampleSpace.y())->at(j + sampleSpace.x()); + m_dataArray << newRow; + } + + if (m_dataArray.size() > 0) { + if (!m_surfaceObj) + loadSurfaceObj(); - if (dimensionChanged) - updateSelectionTexture(); + // Note: Data setup can change samplespace (as min width/height is 1) + if (m_cachedSmoothSurface) + m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); + else + m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); + + if (dimensionChanged) + updateSelectionTexture(); + } + } } Abstract3DRenderer::updateDataModel(dataProxy); @@ -256,19 +256,57 @@ void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) QRect Surface3DRenderer::calculateSampleRect(QSurfaceDataProxy *dataProxy) { - QRect sampleSpace(0, 0, dataProxy->columnCount(), dataProxy->rowCount()); - - // TODO: Calculate the actual sample rect, for now it is required data and axis ranges are the same -#if 0 - if (m_axisCacheX.min() != dataProxy->minValueColumns() || m_axisCacheX.max() != dataProxy->maxValueColumns() - || m_axisCacheZ.min() != dataProxy->minValueRows() || m_axisCacheZ.max() != dataProxy->maxValueRows()) { - qWarning() << "Warning: Technology preview doesn't support axis ranges that are different from data ranges -" - << m_axisCacheX.min() << dataProxy->minValueColumns() << "-" - << m_axisCacheX.max() << dataProxy->maxValueColumns() << "-" - << m_axisCacheZ.min() << dataProxy->minValueRows() << "-" - << m_axisCacheZ.max() << dataProxy->maxValueRows(); + QRect sampleSpace; + + m_minVisibleColumnValue = dataProxy->minValueColumns(); + m_maxVisibleColumnValue = dataProxy->maxValueColumns(); + m_minVisibleRowValue = dataProxy->minValueRows(); + m_maxVisibleRowValue = dataProxy->maxValueRows(); + m_dataStepX = (m_maxVisibleColumnValue - m_minVisibleColumnValue) / (dataProxy->columnCount() - 1); + m_dataStepZ = (m_maxVisibleRowValue - m_minVisibleRowValue) / (dataProxy->rowCount() - 1); + + if (m_minVisibleColumnValue > m_axisCacheX.max() || m_maxVisibleColumnValue < m_axisCacheX.min() + || m_minVisibleRowValue > m_axisCacheZ.max() || m_maxVisibleRowValue < m_axisCacheZ.min()) { + sampleSpace.setWidth(-1); // to indicate nothing needs to be shown } -#endif + + int index = 0; + while (m_minVisibleColumnValue < m_axisCacheX.min()) { + m_minVisibleColumnValue += m_dataStepX; + index++; + } + sampleSpace.setLeft(index); + + index = dataProxy->columnCount() - 1; + while (m_maxVisibleColumnValue > m_axisCacheX.max()) { + m_maxVisibleColumnValue -= m_dataStepX; + index--; + } + sampleSpace.setRight(index); + + index = 0; + while (m_minVisibleRowValue < m_axisCacheZ.min()) { + m_minVisibleRowValue += m_dataStepZ; + index++; + } + sampleSpace.setTop(index); + + index = dataProxy->rowCount() - 1; + while (m_maxVisibleRowValue > m_axisCacheZ.max()) { + m_maxVisibleRowValue -= m_dataStepZ; + index--; + } + sampleSpace.setBottom(index); + + m_surfaceScaleX = m_scaleX * (m_maxVisibleColumnValue - m_minVisibleColumnValue) / m_areaSize.width(); + m_surfaceScaleZ = m_scaleZ * (m_maxVisibleRowValue - m_minVisibleRowValue) / m_areaSize.height(); + GLfloat axis2XCenterX = (m_axisCacheX.min() + m_axisCacheX.max()); + GLfloat axis2XCenterZ = (m_axisCacheZ.min() + m_axisCacheZ.max()); + GLfloat data2XCenterX = (m_minVisibleColumnValue + m_maxVisibleColumnValue); + GLfloat data2XCenterZ = (m_minVisibleRowValue + m_maxVisibleRowValue); + m_surfaceOffsetX = m_scaleX * (data2XCenterX - axis2XCenterX) / m_areaSize.width(); + m_surfaceOffsetZ = -m_scaleZ * (data2XCenterZ - axis2XCenterZ) / m_areaSize.height() + zComp; + return sampleSpace; } @@ -361,6 +399,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Do the surface drawing // + QVector3D surfaceScaler(m_surfaceScaleX, 1.0f, m_surfaceScaleZ); + QVector3D surfaceOffset(m_surfaceOffsetX, 0.0f, m_surfaceOffsetZ); + // Draw depth buffer #if !defined(QT_OPENGL_ES_2) if (m_cachedShadowQuality > QDataVis::ShadowNone && m_surfaceObj) { @@ -405,8 +446,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; - modelMatrix.translate(0.0f, 0.0f, zComp); - modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + modelMatrix.translate(surfaceOffset); + modelMatrix.scale(surfaceScaler); MVPMatrix = depthProjectionViewMatrix * modelMatrix; @@ -469,7 +510,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_TEXTURE_2D); // Draw selection buffer - if (m_querySelection && m_surfaceObj) { + if (m_querySelection && m_surfaceObj && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { m_selectionShader->bind(); glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used @@ -482,8 +523,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; - modelMatrix.translate(0.0f, 0.0f, zComp); - modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + modelMatrix.translate(surfaceOffset); + modelMatrix.scale(surfaceScaler); MVPMatrix = projectionViewMatrix * modelMatrix; @@ -516,8 +557,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_CULL_FACE); } - // Draw surface - if (m_surfaceObj) { + if (m_surfaceObj && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { m_surfaceShader->bind(); // m_selectionShader->bind(); // IFDEF print selection @@ -529,9 +569,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 depthMVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, 0.0f, zComp); - modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); - itModelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + modelMatrix.translate(surfaceOffset); + modelMatrix.scale(surfaceScaler); + itModelMatrix.scale(surfaceScaler); #ifdef SHOW_DEPTH_TEXTURE_SCENE MVPMatrix = depthProjectionViewMatrix * modelMatrix; @@ -1372,7 +1412,6 @@ void Surface3DRenderer::loadSurfaceObj() if (m_surfaceObj) delete m_surfaceObj; m_surfaceObj = new SurfaceObject(); - //m_surfaceObj->setUpData(); } void Surface3DRenderer::loadGridLineMesh() @@ -1405,8 +1444,11 @@ void Surface3DRenderer::surfacePointSelected(int id) if (!m_selectionPointer) m_selectionPointer = new SelectionPointer(m_controller, m_drawer); - m_selectionPointer->setPosition(normalize(float(column), value, float(row))); - m_selectionPointer->setScaling(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + QVector3D pos(normalize(float(column), value, float(row))); + pos += QVector3D(m_surfaceOffsetX, 0.0f, m_surfaceOffsetZ - zComp); + + m_selectionPointer->setPosition(pos); + m_selectionPointer->setScaling(QVector3D(m_surfaceScaleX, 1.0f, m_surfaceScaleZ)); m_selectionPointer->setLabel(createSelectionLabel(value, column, row)); m_selectionPointer->updateBoundingRect(m_mainViewPort); m_selectionPointer->updateScene(m_cachedScene); @@ -1457,19 +1499,13 @@ QString Surface3DRenderer::createSelectionLabel(qreal value, int column, int row // Transforms the model column coordinate to axis coordinate qreal Surface3DRenderer::columnInRange(int column) { - // At this point we'll work only with fixed grid and demand that the user uses proper steps on - // value axis. Zero prevented when doing duplicate from the data. - qreal stepInRange = (m_axisCacheX.max() - m_axisCacheX.min()) / qreal(m_dataArray.at(0)->size() - 1); - return stepInRange * qreal(column) + m_axisCacheX.min(); + return m_dataStepX * qreal(column) + m_minVisibleColumnValue; } // Transforms the model row coordinate to axis coordinate qreal Surface3DRenderer::rowInRange(int row) { - // At this point we'll work only with fixed grid and demand that the user uses proper steps on - // value axis. Zero prevented when doing duplicate from the data. - qreal stepInRange = (m_axisCacheZ.max() - m_axisCacheZ.min()) / qreal(m_dataArray.size() - 1); - return stepInRange * qreal(row) + m_axisCacheZ.min(); + return m_dataStepZ * qreal(row) + m_minVisibleRowValue; } QVector3D Surface3DRenderer::normalize(float x, float y, float z) diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index b612cd34..c209e454 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -89,6 +89,16 @@ private: GLfloat m_scaleZ; GLfloat m_scaleXWithBackground; GLfloat m_scaleZWithBackground; + GLfloat m_surfaceScaleX; + GLfloat m_surfaceScaleZ; + GLfloat m_surfaceOffsetX; + GLfloat m_surfaceOffsetZ; + qreal m_minVisibleColumnValue; + qreal m_maxVisibleColumnValue; + qreal m_minVisibleRowValue; + qreal m_maxVisibleRowValue; + qreal m_dataStepX; + qreal m_dataStepZ; ObjectHelper *m_backgroundObj; ObjectHelper *m_gridLineObj; ObjectHelper *m_labelObj; diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp index a0ed292a..d28ad447 100644 --- a/src/datavisualization/utils/surfaceobject.cpp +++ b/src/datavisualization/utils/surfaceobject.cpp @@ -29,13 +29,14 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE SurfaceObject::SurfaceObject() { m_indicesType = GL_UNSIGNED_INT; + initializeOpenGLFunctions(); } SurfaceObject::~SurfaceObject() { } -void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, QRect space, GLfloat yRange, bool changeGeometry) +void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, bool changeGeometry) { int columns = space.width(); int rows = space.height(); @@ -129,7 +130,7 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, QRect sp } -void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, QRect space, GLfloat yRange, bool changeGeometry) +void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, bool changeGeometry) { int columns = space.width(); int rows = space.height(); @@ -226,7 +227,6 @@ void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVec const QVector<QVector3D> &normals, const GLint *indices, const GLint *gridIndices, bool changeGeometry) { - initializeOpenGLFunctions(); if (m_meshDataLoaded) { // Delete old data glDeleteBuffers(1, &m_vertexbuffer); diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h index e9057f88..f19fffc0 100644 --- a/src/datavisualization/utils/surfaceobject_p.h +++ b/src/datavisualization/utils/surfaceobject_p.h @@ -44,8 +44,8 @@ public: SurfaceObject(); ~SurfaceObject(); - void setUpData(const QSurfaceDataArray &dataArray, QRect space, GLfloat yRange, bool changeGeometry); - void setUpSmoothData(const QSurfaceDataArray &dataArray, QRect space, GLfloat yRange, bool changeGeometry); + void setUpData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, bool changeGeometry); + void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space, GLfloat yRange, bool changeGeometry); GLuint gridElementBuf(); GLuint gridIndexCount(); |